diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 96283c9d9e3..170a3daad72 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2953,7 +2953,7 @@ JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep) return JS_TRUE; /* Walk slots in obj and if any value is a non-null object, seal it. */ - nslots = scope->map.freeslot; + nslots = scope->freeslot; for (i = 0; i != nslots; ++i) { v = STOBJ_GET_SLOT(obj, i); if (JSVAL_IS_PRIMITIVE(v)) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 5b12beeddef..67e96f96eae 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1199,35 +1199,12 @@ array_trace(JSTracer *trc, JSObject *obj) } } -static JSObjectMap * -array_newObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp, JSObject *obj) -{ -#ifdef DEBUG - extern JSClass js_ArrayClass; - extern JSObjectOps js_ArrayObjectOps; -#endif - JSObjectMap *map = (JSObjectMap *) JS_malloc(cx, sizeof(*map)); - if (!map) - return NULL; +extern JSObjectOps js_ArrayObjectOps; - map->nrefs = nrefs; - JS_ASSERT(ops == &js_ArrayObjectOps); - map->ops = ops; - JS_ASSERT(clasp == &js_ArrayClass); - map->freeslot = JSSLOT_FREE(clasp); - - return map; -} - -void -array_destroyObjectMap(JSContext *cx, JSObjectMap *map) -{ - JS_free(cx, map); -} +static const JSObjectMap SharedArrayMap = { &js_ArrayObjectOps }; JSObjectOps js_ArrayObjectOps = { - array_newObjectMap, array_destroyObjectMap, + &SharedArrayMap, array_lookupProperty, array_defineProperty, array_getProperty, array_setProperty, array_getAttributes, array_setAttributes, @@ -1271,27 +1248,24 @@ JSClass js_SlowArrayClass = { JSBool js_MakeArraySlow(JSContext *cx, JSObject *obj) { - JSObjectMap *map, *oldmap; - uint32 i, capacity; - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass); /* Create a native scope. */ - map = js_NewObjectMap(cx, obj->map->nrefs, &js_SlowArrayObjectOps, - &js_SlowArrayClass, obj); - if (!map) + JSScope *scope = js_NewScope(cx, &js_SlowArrayObjectOps, + &js_SlowArrayClass, obj); + if (!scope) return JS_FALSE; - capacity = js_DenseArrayCapacity(obj); + uint32 capacity = js_DenseArrayCapacity(obj); if (capacity) { - map->freeslot = STOBJ_NSLOTS(obj) + JS_INITIAL_NSLOTS; + scope->freeslot = STOBJ_NSLOTS(obj) + JS_INITIAL_NSLOTS; obj->dslots[-1] = JS_INITIAL_NSLOTS + capacity; } else { - map->freeslot = STOBJ_NSLOTS(obj); + scope->freeslot = STOBJ_NSLOTS(obj); } /* Create new properties pointing to existing values in dslots */ - for (i = 0; i < capacity; i++) { + for (uint32 i = 0; i < capacity; i++) { jsid id; JSScopeProperty *sprop; @@ -1303,7 +1277,7 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj) continue; } - sprop = js_AddScopeProperty(cx, (JSScope *)map, id, NULL, NULL, + sprop = js_AddScopeProperty(cx, scope, id, NULL, NULL, i + JS_INITIAL_NSLOTS, JSPROP_ENUMERATE, 0, 0); if (!sprop) @@ -1327,15 +1301,11 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj) obj->classword ^= (jsuword) &js_ArrayClass; obj->classword |= (jsuword) &js_SlowArrayClass; - /* Swap in our new map. */ - oldmap = obj->map; - obj->map = map; - array_destroyObjectMap(cx, oldmap); - + obj->map = &scope->map; return JS_TRUE; -out_bad: - js_DestroyObjectMap(cx, map); + out_bad: + js_DestroyScope(cx, scope); return JS_FALSE; } @@ -3387,9 +3357,9 @@ js_NewEmptyArray(JSContext* cx, JSObject* proto) if (!obj) return NULL; - JSClass* clasp = &js_ArrayClass; - obj->classword = jsuword(clasp); - + /* Initialize all fields of JSObject. */ + obj->map = const_cast(&SharedArrayMap); + obj->classword = jsuword(&js_ArrayClass); obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); obj->fslots[JSSLOT_PARENT] = proto->fslots[JSSLOT_PARENT]; @@ -3397,11 +3367,6 @@ js_NewEmptyArray(JSContext* cx, JSObject* proto) obj->fslots[JSSLOT_ARRAY_COUNT] = 0; for (unsigned i = JSSLOT_ARRAY_COUNT + 1; i != JS_INITIAL_NSLOTS; ++i) obj->fslots[i] = JSVAL_VOID; - - JSObjectOps* ops = clasp->getObjectOps(cx, clasp); - obj->map = ops->newObjectMap(cx, 1, ops, clasp, obj); - if (!obj->map) - return NULL; obj->dslots = NULL; return obj; } diff --git a/js/src/jsbuiltins.cpp b/js/src/jsbuiltins.cpp index aa4362ae8e7..9673d89b9b1 100644 --- a/js/src/jsbuiltins.cpp +++ b/js/src/jsbuiltins.cpp @@ -258,10 +258,10 @@ js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop) } slot = sprop->slot; - if (!scope->table && sprop->parent == scope->lastProp && slot == scope->map.freeslot) { + if (!scope->table && sprop->parent == scope->lastProp && slot == scope->freeslot) { if (slot < STOBJ_NSLOTS(obj) && !OBJ_GET_CLASS(cx, obj)->reserveSlots) { - JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, scope->map.freeslot))); - ++scope->map.freeslot; + JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, scope->freeslot))); + ++scope->freeslot; } else { if (!js_AllocSlot(cx, obj, &slot)) goto exit_trace; @@ -398,26 +398,27 @@ js_Arguments(JSContext* cx) JS_DEFINE_CALLINFO_1(extern, OBJECT, js_Arguments, CONTEXT, 0, 0) JSObject* FASTCALL -js_NewNullClosure(JSContext* cx, JSObject* funobj, JSObject* proto, JSObject *parent) +js_NewNullClosure(JSContext* cx, JSObject* funobj, JSObject* proto, JSObject* parent) { JS_ASSERT(HAS_FUNCTION_CLASS(funobj)); + JS_ASSERT(HAS_FUNCTION_CLASS(proto)); + JS_ASSERT(JS_ON_TRACE(cx)); JSFunction *fun = (JSFunction*) funobj; JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun); - JS_ASSERT(JS_ON_TRACE(cx)); JSObject* closure = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); if (!closure) return NULL; + js_HoldScope(OBJ_SCOPE(proto)); + closure->map = proto->map; closure->classword = jsuword(&js_FunctionClass); closure->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); closure->fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent); closure->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun); for (unsigned i = JSSLOT_PRIVATE + 1; i != JS_INITIAL_NSLOTS; ++i) closure->fslots[i] = JSVAL_VOID; - - closure->map = js_HoldObjectMap(cx, proto->map); closure->dslots = NULL; return closure; } diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 86a2d65d6e2..ca6a3401072 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -142,24 +142,26 @@ struct JSTraceMonitor { jsval *reservedDoublePool; jsval *reservedDoublePoolPtr; - struct GlobalState globalStates[MONITOR_N_GLOBAL_STATES]; - struct VMFragment* vmfragments[FRAGMENT_TABLE_SIZE]; + struct GlobalState globalStates[MONITOR_N_GLOBAL_STATES]; + struct VMFragment* vmfragments[FRAGMENT_TABLE_SIZE]; JSDHashTable recordAttempts; - /* - * If nonzero, do not flush the JIT cache after a deep bail. That would - * free JITted code pages that we will later return to. Instead, set - * the needFlush flag so that it can be flushed later. - */ - uintN prohibitFlush; - JSPackedBool needFlush; - /* * Maximum size of the code cache before we start flushing. 1/16 of this * size is used as threshold for the regular expression code cache. */ uint32 maxCodeCacheBytes; + /* + * If nonzero, do not flush the JIT cache after a deep bail. That would + * free JITted code pages that we will later return to. Instead, set the + * needFlush flag so that it can be flushed later. + * + * NB: needFlush and useReservedObjects are packed together. + */ + uintN prohibitFlush; + JSPackedBool needFlush; + /* * reservedObjects is a linked list (via fslots[0]) of preallocated JSObjects. * The JIT uses this to ensure that leaving a trace tree can't fail. @@ -236,11 +238,6 @@ struct JSThreadData { /* Property cache for faster call/get/set invocation. */ JSPropertyCache propertyCache; -/* - * N.B. JS_ON_TRACE(cx) is true if JIT code is on the stack in the current - * thread, regardless of whether cx is the context in which that trace is - * executing. cx must be a context on the current thread. - */ #ifdef JS_TRACER /* Trace-tree JIT recorder/interpreter state. */ JSTraceMonitor traceMonitor; @@ -261,7 +258,7 @@ struct JSThreadData { * that can be accessed without a global lock. */ struct JSThread { - /* Linked list of all contexts active on this thread. */ + /* Linked list of all contexts in use on this thread. */ JSCList contextList; /* Opaque thread-id, from NSPR's PR_GetCurrentThread(). */ @@ -276,6 +273,7 @@ struct JSThread { /* Indicates that the thread is waiting in ClaimTitle from jslock.cpp. */ JSTitle *titleToShare; + /* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */ JSThreadData data; }; @@ -348,6 +346,20 @@ struct JSRuntime { /* Context create/destroy callback. */ JSContextCallback cxCallback; + /* + * Shape regenerated whenever a prototype implicated by an "add property" + * property cache fill and induced trace guard has a readonly property or a + * setter defined on it. This number proxies for the shapes of all objects + * along the prototype chain of all objects in the runtime on which such an + * add-property result has been cached/traced. + * + * See bug 492355 for more details. + * + * This comes early in JSRuntime to minimize the immediate format used by + * trace-JITted code that reads it. + */ + uint32 protoHazardShape; + /* Garbage collector state, used by jsgc.c. */ JSGCChunkInfo *gcChunkList; JSGCArenaList gcArenaList[GC_NUM_FREELISTS]; diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index bc626dc6687..df325e8da92 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -1821,7 +1821,7 @@ EmitEnterBlock(JSContext *cx, JSParseNode *pn, JSCodeGenerator *cg) #endif } - blockObj->map->freeslot = JSSLOT_FREE(&js_BlockClass); + OBJ_SCOPE(blockObj)->freeslot = JSSLOT_FREE(&js_BlockClass); js_ReallocSlots(cx, blockObj, JSSLOT_FREE(&js_BlockClass), JS_TRUE); return true; } diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 4c2a47e988e..ac83de3b739 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -629,8 +629,14 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp) if (!env) return NULL; - /* Root env. */ + /* Root env before js_DefineNativeProperty (-> JSClass.addProperty). */ fp->scopeChain = env; + if (!js_DefineNativeProperty(cx, fp->scopeChain, ATOM_TO_JSID(lambdaName), + OBJECT_TO_JSVAL(fp->callee), NULL, NULL, + JSPROP_PERMANENT | JSPROP_READONLY, + 0, 0, NULL)) { + return NULL; + } } callobj = js_NewObjectWithGivenProto(cx, &js_CallClass, NULL, @@ -641,14 +647,6 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp) JS_SetPrivate(cx, callobj, fp); JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, fp->callee)); STOBJ_SET_SLOT(callobj, JSSLOT_CALLEE, OBJECT_TO_JSVAL(fp->callee)); - if (lambdaName && - !js_DefineNativeProperty(cx, fp->scopeChain, ATOM_TO_JSID(lambdaName), - OBJECT_TO_JSVAL(fp->callee), NULL, NULL, - JSPROP_PERMANENT | JSPROP_READONLY, - 0, 0, NULL)) { - return NULL; - } - fp->callobj = callobj; /* @@ -720,8 +718,8 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp) memcpy(callobj->dslots, fp->argv, fun->nargs * sizeof(jsval)); memcpy(callobj->dslots + fun->nargs, fp->slots, fun->u.i.nvars * sizeof(jsval)); - if (scope->object == callobj && n > scope->map.freeslot) - scope->map.freeslot = n; + if (scope->object == callobj && n > scope->freeslot) + scope->freeslot = n; } JS_UNLOCK_SCOPE(cx, scope); } @@ -912,6 +910,8 @@ call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags, uintN slot, attrs; JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass); + JS_ASSERT(!STOBJ_GET_PROTO(obj)); + if (!JSVAL_IS_STRING(idval)) return JS_TRUE; @@ -923,6 +923,17 @@ call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags, if (!js_ValueToStringId(cx, idval, &id)) return JS_FALSE; + /* + * Check whether the id refers to a formal parameter, local variable or + * the arguments special name. + * + * We define all such names using JSDNP_DONT_PURGE to avoid an expensive + * shape invalidation in js_DefineNativeProperty. If such an id happens to + * shadow a global or upvar of the same name, any inner functions can + * never access the outer binding. Thus it cannot invalidate any property + * cache entries or derived trace guards for the outer binding. See also + * comments in js_PurgeScopeChainHelper from jsobj.cpp. + */ localKind = js_LookupLocal(cx, fun, JSID_TO_ATOM(id), &slot); if (localKind != JSLOCAL_NONE && localKind != JSLOCAL_UPVAR) { JS_ASSERT((uint16) slot == slot); @@ -946,7 +957,7 @@ call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags, } if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID, getter, setter, attrs, SPROP_HAS_SHORTID, (int16) slot, - NULL)) { + NULL, JSDNP_DONT_PURGE)) { return JS_FALSE; } *objp = obj; @@ -961,7 +972,7 @@ call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags, if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID, GetCallArguments, SetCallArguments, JSPROP_PERMANENT | JSPROP_SHARED, - 0, 0, NULL)) { + 0, 0, NULL, JSDNP_DONT_PURGE)) { return JS_FALSE; } *objp = obj; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 99ed7bddd6a..a4178247a03 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -3467,8 +3467,13 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) JS_ASSERT(!rt->gcUntracedArenaStackTop); JS_ASSERT(rt->gcTraceLaterCount == 0); - /* Reset the property cache's type id generator so we can compress ids. */ + /* + * Reset the property cache's type id generator so we can compress ids. + * Same for the protoHazardShape proxy-shape standing in for all object + * prototypes having readonly or setter properties. + */ rt->shapeGen = 0; + rt->protoHazardShape = 0; /* * Mark phase. diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index a52d55fa72f..fc7093aa3ca 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -110,12 +110,12 @@ js_GenerateShape(JSContext *cx, JSBool gcLocked) JS_REQUIRES_STACK JSPropCacheEntry * js_FillPropertyCache(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN protoIndex, JSObject *pobj, - JSScopeProperty *sprop, JSBool addedSprop) + JSScopeProperty *sprop, JSBool adding) { JSPropertyCache *cache; jsbytecode *pc; JSScope *scope; - jsuword kshape, khash; + jsuword kshape, vshape, khash; JSOp op; const JSCodeSpec *cs; jsuword vword; @@ -157,12 +157,15 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, * but vcap vs. scope shape tests ensure nothing malfunctions. */ JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj); - if (protoIndex != 0) { - JSObject *tmp; - JS_ASSERT(pobj != obj); + if (protoIndex != 0) { + JSObject *tmp = obj; + + for (uintN i = 0; i != scopeIndex; i++) + tmp = OBJ_GET_PARENT(cx, tmp); + JS_ASSERT(tmp != pobj); + protoIndex = 1; - tmp = obj; for (;;) { tmp = OBJ_GET_PROTO(cx, tmp); @@ -180,6 +183,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, ++protoIndex; } } + if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) { PCMETER(cache->longchains++); return JS_NO_PROP_CACHE_FILL; @@ -256,7 +260,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, } else { /* Best we can do is to cache sprop (still a nice speedup). */ vword = SPROP_TO_PCVAL(sprop); - if (addedSprop && + if (adding && sprop == scope->lastProp && scope->shape == sprop->shape) { /* @@ -294,12 +298,21 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, if (proto && OBJ_IS_NATIVE(proto)) kshape = OBJ_SHAPE(proto); } + + /* + * When adding we predict no prototype object will later gain a + * readonly property or setter. + */ + vshape = cx->runtime->protoHazardShape; } } } while (0); - if (kshape == 0) + if (kshape == 0) { kshape = OBJ_SHAPE(obj); + vshape = scope->shape; + } + khash = PROPERTY_CACHE_HASH_PC(pc, kshape); if (obj == pobj) { JS_ASSERT(scopeIndex == 0 && protoIndex == 0); @@ -312,8 +325,14 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0; GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom); } - JS_ASSERT_IF(scopeIndex == 0, - protoIndex != 1 || OBJ_GET_PROTO(cx, obj) == pobj); + +#ifdef DEBUG + if (scopeIndex == 0) { + JS_ASSERT(protoIndex != 0); + JS_ASSERT((protoIndex == 1) == (OBJ_GET_PROTO(cx, obj) == pobj)); + } +#endif + if (scopeIndex != 0 || protoIndex != 1) { khash = PROPERTY_CACHE_HASH_ATOM(atom, obj, pobj); PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1) @@ -339,7 +358,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, PCMETER(PCVAL_IS_NULL(entry->vword) || cache->recycles++); entry->kpc = pc; entry->kshape = kshape; - entry->vcap = PCVCAP_MAKE(scope->shape, scopeIndex, protoIndex); + entry->vcap = PCVCAP_MAKE(vshape, scopeIndex, protoIndex); entry->vword = vword; cache->empty = JS_FALSE; @@ -2024,7 +2043,7 @@ js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2) return JS_TRUE; } -jsval +jsval& js_GetUpvar(JSContext *cx, uintN level, uintN cookie) { level -= UPVAR_FRAME_SKIP(cookie); @@ -4145,7 +4164,7 @@ js_Interpret(JSContext *cx) ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); if (obj == obj2 && PCVAL_IS_SLOT(entry->vword)) { slot = PCVAL_TO_SLOT(entry->vword); - JS_ASSERT(slot < obj->map->freeslot); + JS_ASSERT(slot < OBJ_SCOPE(obj)->freeslot); rval = LOCKED_OBJ_GET_SLOT(obj, slot); if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) { rtmp = rval; @@ -4401,7 +4420,7 @@ js_Interpret(JSContext *cx) rval = PCVAL_OBJECT_TO_JSVAL(entry->vword); } else if (PCVAL_IS_SLOT(entry->vword)) { slot = PCVAL_TO_SLOT(entry->vword); - JS_ASSERT(slot < obj2->map->freeslot); + JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot); rval = LOCKED_OBJ_GET_SLOT(obj2, slot); } else { JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); @@ -4492,7 +4511,7 @@ js_Interpret(JSContext *cx) rval = PCVAL_OBJECT_TO_JSVAL(entry->vword); } else if (PCVAL_IS_SLOT(entry->vword)) { slot = PCVAL_TO_SLOT(entry->vword); - JS_ASSERT(slot < obj2->map->freeslot); + JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot); rval = LOCKED_OBJ_GET_SLOT(obj2, slot); } else { JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); @@ -4590,11 +4609,13 @@ js_Interpret(JSContext *cx) PCMETER(cache->pctestentry = entry); PCMETER(cache->tests++); PCMETER(cache->settests++); - if (entry->kpc == regs.pc && entry->kshape == kshape) { - JSScope *scope; + if (entry->kpc == regs.pc && + entry->kshape == kshape && + PCVCAP_SHAPE(entry->vcap) == rt->protoHazardShape) { + JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0); JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); + JSScope *scope = OBJ_SCOPE(obj); if (scope->shape == kshape) { JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); sprop = PCVAL_TO_SPROP(entry->vword); @@ -4635,7 +4656,7 @@ js_Interpret(JSContext *cx) if (checkForAdd && SPROP_HAS_STUB_SETTER(sprop) && - (slot = sprop->slot) == scope->map.freeslot) { + (slot = sprop->slot) == scope->freeslot) { /* * Fast path: adding a plain old property that * was once at the frontier of the property @@ -4660,7 +4681,7 @@ js_Interpret(JSContext *cx) */ if (slot < STOBJ_NSLOTS(obj) && !OBJ_GET_CLASS(cx, obj)->reserveSlots) { - ++scope->map.freeslot; + ++scope->freeslot; } else { if (!js_AllocSlot(cx, obj, &slot)) { JS_UNLOCK_SCOPE(cx, scope); @@ -5241,7 +5262,7 @@ js_Interpret(JSContext *cx) if (PCVAL_IS_SLOT(entry->vword)) { slot = PCVAL_TO_SLOT(entry->vword); - JS_ASSERT(slot < obj2->map->freeslot); + JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot); rval = LOCKED_OBJ_GET_SLOT(obj2, slot); JS_UNLOCK_OBJ(cx, obj2); goto do_push_rval; @@ -5721,7 +5742,7 @@ js_Interpret(JSContext *cx) index = GET_UINT16(regs.pc); JS_ASSERT(JS_INITIAL_NSLOTS + index < jsatomid(obj->dslots[-1])); JS_ASSERT_IF(OBJ_SCOPE(obj)->object == obj, - JS_INITIAL_NSLOTS + index < obj->map->freeslot); + JS_INITIAL_NSLOTS + index < OBJ_SCOPE(obj)->freeslot); PUSH_OPND(obj->dslots[index]); if (op == JSOP_CALLDSLOT) @@ -6303,7 +6324,11 @@ js_Interpret(JSContext *cx) PCMETER(cache->tests++); PCMETER(cache->initests++); - if (entry->kpc == regs.pc && entry->kshape == kshape) { + if (entry->kpc == regs.pc && + entry->kshape == kshape && + PCVCAP_SHAPE(entry->vcap) == rt->protoHazardShape) { + JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0); + PCMETER(cache->pchits++); PCMETER(cache->inipchits++); @@ -6341,15 +6366,14 @@ js_Interpret(JSContext *cx) * obj, not a proto-property, and there cannot have been * any deletions of prior properties. */ - JS_ASSERT(PCVCAP_MAKE(sprop->shape, 0, 0) == entry->vcap); JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope)); JS_ASSERT(!scope->table || !SCOPE_HAS_PROPERTY(scope, sprop)); slot = sprop->slot; - JS_ASSERT(slot == scope->map.freeslot); + JS_ASSERT(slot == scope->freeslot); if (slot < STOBJ_NSLOTS(obj)) { - ++scope->map.freeslot; + ++scope->freeslot; } else { if (!js_AllocSlot(cx, obj, &slot)) { JS_UNLOCK_SCOPE(cx, scope); @@ -6406,7 +6430,7 @@ js_Interpret(JSContext *cx) ? js_SetPropertyHelper(cx, obj, id, true, &rval) : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL, JSPROP_ENUMERATE, 0, 0, NULL, - true))) + JSDNP_CACHE_RESULT))) goto error; } while (0); diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index a5cea0228d7..7cbcdcb8788 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -235,7 +235,7 @@ typedef struct JSInlineFrame { #define PCVCAP_TAGMASK JS_BITMASK(PCVCAP_TAGBITS) #define PCVCAP_TAG(t) ((t) & PCVCAP_TAGMASK) -#define PCVCAP_MAKE(t,s,p) (((t) << PCVCAP_TAGBITS) | \ +#define PCVCAP_MAKE(t,s,p) ((uint32(t) << PCVCAP_TAGBITS) | \ ((s) << PCVCAP_PROTOBITS) | \ (p)) #define PCVCAP_SHAPE(t) ((t) >> PCVCAP_TAGBITS) @@ -351,7 +351,7 @@ typedef struct JSPropertyCache { extern JS_REQUIRES_STACK JSPropCacheEntry * js_FillPropertyCache(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN protoIndex, JSObject *pobj, - JSScopeProperty *sprop, JSBool addedSprop); + JSScopeProperty *sprop, JSBool adding); /* * Property cache lookup macros. PROPERTY_CACHE_TEST is designed to inline the @@ -555,7 +555,7 @@ js_InternNonIntElementId(JSContext *cx, JSObject *obj, jsval idval, jsid *idp); * Given an active context, a static scope level, and an upvar cookie, return * the value of the upvar. */ -extern jsval +extern jsval& js_GetUpvar(JSContext *cx, uintN level, uintN cookie); /* diff --git a/js/src/jslock.cpp b/js/src/jslock.cpp index be2f66191b1..35c4fbdc3c4 100644 --- a/js/src/jslock.cpp +++ b/js/src/jslock.cpp @@ -57,11 +57,6 @@ #include "jsscope.h" #include "jsstr.h" -/* - * Check that we can cast the data after JSObjectMap as JSTitle. - */ -JS_STATIC_ASSERT(offsetof(JSScope, title) == sizeof(JSObjectMap)); - #define ReadWord(W) (W) /* Implement NativeCompareAndSwap. */ @@ -456,7 +451,6 @@ ShareTitle(JSContext *cx, JSTitle *title) static void FinishSharingTitle(JSContext *cx, JSTitle *title) { - JSObjectMap *map; JSScope *scope; JSObject *obj; uint32 nslots, i; @@ -464,14 +458,10 @@ FinishSharingTitle(JSContext *cx, JSTitle *title) js_InitLock(&title->lock); title->u.count = 0; /* NULL may not pun as 0 */ - map = TITLE_TO_MAP(title); - if (!MAP_IS_NATIVE(map)) - return; - scope = (JSScope *)map; - + scope = TITLE_TO_SCOPE(title); obj = scope->object; if (obj) { - nslots = scope->map.freeslot; + nslots = scope->freeslot; for (i = 0; i != nslots; ++i) { v = STOBJ_GET_SLOT(obj, i); if (JSVAL_IS_STRING(v) && @@ -617,9 +607,9 @@ ClaimTitle(JSTitle *title, JSContext *cx) * non-null test, and avoid double-insertion bugs. */ if (!title->u.link) { + js_HoldScope(TITLE_TO_SCOPE(title)); title->u.link = rt->titleSharingTodo; rt->titleSharingTodo = title; - js_HoldObjectMap(cx, TITLE_TO_MAP(title)); } /* @@ -693,13 +683,13 @@ js_ShareWaitingTitles(JSContext *cx) title->u.link = NULL; /* null u.link for sanity ASAP */ /* - * If js_DropObjectMap returns null, we held the last ref to scope. - * The waiting thread(s) must have been killed, after which the GC + * If js_DropScope returns false, we held the last ref to scope. The + * waiting thread(s) must have been killed, after which the GC * collected the object that held this scope. Unlikely, because it * requires that the GC ran (e.g., from an operation callback) * during this request, but possible. */ - if (js_DropObjectMap(cx, TITLE_TO_MAP(title), NULL)) { + if (js_DropScope(cx, TITLE_TO_SCOPE(title), NULL)) { FinishSharingTitle(cx, title); /* set ownercx = NULL */ shared = true; } @@ -740,7 +730,7 @@ js_GetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot) scope = OBJ_SCOPE(obj); title = &scope->title; JS_ASSERT(title->ownercx != cx); - JS_ASSERT(slot < obj->map->freeslot); + JS_ASSERT(slot < scope->freeslot); /* * Avoid locking if called from the GC. Also avoid locking an object @@ -835,7 +825,7 @@ js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v) scope = OBJ_SCOPE(obj); title = &scope->title; JS_ASSERT(title->ownercx != cx); - JS_ASSERT(slot < obj->map->freeslot); + JS_ASSERT(slot < scope->freeslot); /* * Avoid locking if called from the GC. Also avoid locking an object @@ -1478,9 +1468,7 @@ js_IsRuntimeLocked(JSRuntime *rt) JSBool js_IsObjLocked(JSContext *cx, JSObject *obj) { - JSScope *scope = OBJ_SCOPE(obj); - - return MAP_IS_NATIVE(&scope->map) && js_IsTitleLocked(cx, &scope->title); + return js_IsTitleLocked(cx, &OBJ_SCOPE(obj)->title); } JSBool diff --git a/js/src/jslock.h b/js/src/jslock.h index 275e2c1e5a2..bfa0c637d7b 100644 --- a/js/src/jslock.h +++ b/js/src/jslock.h @@ -103,14 +103,10 @@ struct JSTitle { }; /* - * Title structures must be immediately preceded by JSObjectMap structures for - * maps that use titles for threadsafety. This is enforced by assertion in - * jsscope.h; see bug 408416 for future remedies to this somewhat fragile - * architecture. + * Title structure is always allocated as a field of JSScope. */ - -#define TITLE_TO_MAP(title) \ - ((JSObjectMap *)((char *)(title) - sizeof(JSObjectMap))) +#define TITLE_TO_SCOPE(title) \ + ((JSScope *)((uint8 *) (title) - offsetof(JSScope, title))) /* * Atomic increment and decrement for a reference counter, given jsrefcount *p. diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 2a0b86b6527..48cc6b847c9 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -102,7 +102,7 @@ js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop); #endif JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { - js_NewObjectMap, js_DestroyObjectMap, + NULL, js_LookupProperty, js_DefineProperty, js_GetProperty, js_SetProperty, js_GetAttributes, js_SetAttributes, @@ -286,7 +286,7 @@ js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj, JS_ASSERT_IF(!checkForCycles, obj != pobj); if (slot == JSSLOT_PROTO) { - JS_UNLOCK_OBJ(cx, obj); + JS_LOCK_OBJ(cx, obj); bool ok = !!js_GetMutableScope(cx, obj); JS_UNLOCK_OBJ(cx, obj); if (!ok) @@ -2033,9 +2033,12 @@ js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } static inline bool -CreateMapForObject(JSContext* cx, JSObject* obj, JSObject* proto, JSObjectOps* ops) +InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, + JSObjectOps* ops) { - JSObjectMap* map; + JS_ASSERT(OPS_IS_NATIVE(ops)); + JS_ASSERT(proto == OBJ_GET_PROTO(cx, obj)); + JSClass* protoclasp; JSClass* clasp = OBJ_GET_CLASS(cx, obj); @@ -2048,33 +2051,36 @@ CreateMapForObject(JSContext* cx, JSObject* obj, JSObject* proto, JSObjectOps* o * object classes must have the same (null or not) reserveSlots hook. */ if (proto && - ((map = proto->map)->ops == ops && - ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp || - (!((protoclasp->flags ^ clasp->flags) & - (JSCLASS_HAS_PRIVATE | - (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) && - protoclasp->reserveSlots == clasp->reserveSlots)))) + proto->map->ops == ops && + ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp || + (!((protoclasp->flags ^ clasp->flags) & + (JSCLASS_HAS_PRIVATE | + (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) && + protoclasp->reserveSlots == clasp->reserveSlots))) { - /* Share the given prototype's map. */ - obj->map = js_HoldObjectMap(cx, map); + js_HoldScope(OBJ_SCOPE(proto)); + obj->map = proto->map; return true; } - map = ops->newObjectMap(cx, 1, ops, clasp, obj); - if (!map) - return false; - obj->map = map; + JSScope *scope = js_NewScope(cx, ops, clasp, obj); + if (!scope) + goto bad; - /* Let ops->newObjectMap set freeslot so as to reserve slots. */ - uint32 nslots = map->freeslot; - JS_ASSERT(nslots >= JSSLOT_PRIVATE); - if (nslots > JS_INITIAL_NSLOTS && - !js_ReallocSlots(cx, obj, nslots, JS_TRUE)) { - js_DropObjectMap(cx, map, obj); - return false; + /* Let js_NewScope set freeslot so as to reserve slots. */ + JS_ASSERT(scope->freeslot >= JSSLOT_PRIVATE); + if (scope->freeslot > JS_INITIAL_NSLOTS && + !js_ReallocSlots(cx, obj, scope->freeslot, JS_TRUE)) { + js_DestroyScope(cx, scope); + goto bad; } - + obj->map = &scope->map; return true; + + bad: + /* Ensure that the map field is initialized for GC. */ + obj->map = NULL; + return false; } #ifdef JS_TRACER @@ -2093,11 +2099,8 @@ NewNativeObject(JSContext* cx, JSClass* clasp, JSObject* proto, JSObject *parent for (unsigned i = JSSLOT_PRIVATE; i < JS_INITIAL_NSLOTS; ++i) obj->fslots[i] = JSVAL_VOID; - if (!CreateMapForObject(cx, obj, proto, &js_ObjectOps)) - return NULL; obj->dslots = NULL; - - return obj; + return InitScopeForObject(cx, obj, proto, &js_ObjectOps) ? obj : NULL; } JSObject* FASTCALL @@ -2363,7 +2366,7 @@ with_ThisObject(JSContext *cx, JSObject *obj) } JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = { - js_NewObjectMap, js_DestroyObjectMap, + NULL, with_LookupProperty, js_DefineProperty, with_GetProperty, with_SetProperty, with_GetAttributes, with_SetAttributes, @@ -2455,7 +2458,7 @@ js_PutBlockObject(JSContext *cx, JSBool normalUnwind) /* * Block objects should never be exposed to scripts. Thus the clone should * not own the property map and rather always share it with the prototype - * object. This allows to skip updating OBJ_SCOPE(obj)->map.freeslot after + * object. This allows us to skip updating OBJ_SCOPE(obj)->freeslot after * we copy the stack slots into reserved slots. */ JS_ASSERT(OBJ_SCOPE(obj)->object != obj); @@ -2845,50 +2848,6 @@ bad: goto out; } -void -js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp) -{ - map->nrefs = nrefs; - map->ops = ops; - 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) -{ - JS_ASSERT(map->nrefs >= 0); - JS_ATOMIC_INCREMENT(&map->nrefs); - return map; -} - -JSObjectMap * -js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj) -{ - JS_ASSERT(map->nrefs > 0); - JS_ATOMIC_DECREMENT(&map->nrefs); - 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; -} - static void FreeSlots(JSContext *cx, JSObject *obj) { @@ -3072,11 +3031,6 @@ JSObject * js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent, uintN objectSize) { - JSObject *obj; - JSObjectOps *ops; - uint32 i; - JSTempValueRooter tvr; - #ifdef INCLUDE_MOZILLA_DTRACE if (JAVASCRIPT_OBJECT_CREATE_START_ENABLED()) jsdtrace_object_create_start(cx->fp, clasp); @@ -3097,16 +3051,18 @@ js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, JS_ASSERT_IF(clasp->flags & JSCLASS_IS_EXTENDED, ((JSExtendedClass *)clasp)->equality); + /* Always call the class's getObjectOps hook if it has one. */ + JSObjectOps *ops = clasp->getObjectOps + ? clasp->getObjectOps(cx, clasp) + : &js_ObjectOps; + /* * Allocate an object from the GC heap and initialize all its fields before * doing any operation that can potentially trigger GC. */ - obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, objectSize); + JSObject *obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, objectSize); if (!obj) - goto earlybad; - - obj->map = NULL; - obj->dslots = NULL; + goto out; /* * Set the class slot with the initial value of the system and delegate @@ -3117,55 +3073,54 @@ js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, JS_ASSERT(!STOBJ_IS_DELEGATE(obj)); JS_ASSERT(!STOBJ_IS_SYSTEM(obj)); - /* Set the proto and parent properties. */ - STOBJ_SET_PROTO(obj, proto); - STOBJ_SET_PARENT(obj, parent); + obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); + + /* + * Default parent to the parent of the prototype, which was set from + * the parent of the prototype's constructor. + */ + obj->fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL((!parent && proto) + ? OBJ_GET_PARENT(cx, proto) + : parent); /* Initialize the remaining fixed slots. */ - for (i = JSSLOT_PRIVATE; i < JS_INITIAL_NSLOTS; ++i) + for (uint32 i = JSSLOT_PRIVATE; i < JS_INITIAL_NSLOTS; ++i) obj->fslots[i] = JSVAL_VOID; + obj->dslots = NULL; + + if (OPS_IS_NATIVE(ops)) { + if (!InitScopeForObject(cx, obj, proto, ops)) { + obj = NULL; + goto out; + } + } else { + JS_ASSERT(ops->objectMap->ops == ops); + obj->map = const_cast(ops->objectMap); + } + #ifdef DEBUG memset((uint8 *) obj + sizeof(JSObject), JS_FREE_PATTERN, objectSize - sizeof(JSObject)); #endif - /* - * Root obj to prevent it from being collected out from under this call to - * js_NewObject. There's a possibilty of GC under the objectHook call-out - * further below. - */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); - - /* Always call the class's getObjectOps hook if it has one. */ - ops = clasp->getObjectOps - ? clasp->getObjectOps(cx, clasp) - : &js_ObjectOps; - - /* - * Default parent to the parent of the prototype, which was set from - * the parent of the prototype's constructor. - */ - if (proto && !parent) - STOBJ_SET_PARENT(obj, OBJ_GET_PARENT(cx, proto)); - - if (!CreateMapForObject(cx, obj, proto, ops)) - goto bad; + /* Check that the newborn root still holds the object. */ + JS_ASSERT_IF(!cx->localRootStack, cx->weakRoots.newborn[GCX_OBJECT] == obj); /* * Do not call debug hooks on trace, because we might be in a non-_FAIL * builtin. See bug 481444. */ if (cx->debugHooks->objectHook && !JS_ON_TRACE(cx)) { + JSAutoTempValueRooter tvr(cx, obj); JS_KEEP_ATOMS(cx->runtime); cx->debugHooks->objectHook(cx, obj, JS_TRUE, cx->debugHooks->objectHookData); JS_UNKEEP_ATOMS(cx->runtime); + cx->weakRoots.newborn[GCX_OBJECT] = obj; } out: - JS_POP_TEMP_ROOT(cx, &tvr); - cx->weakRoots.newborn[GCX_OBJECT] = obj; #ifdef INCLUDE_MOZILLA_DTRACE if (JAVASCRIPT_OBJECT_CREATE_ENABLED()) jsdtrace_object_create(cx, clasp, obj); @@ -3173,28 +3128,21 @@ out: jsdtrace_object_create_done(cx->fp, clasp); #endif return obj; - -bad: - obj = NULL; - goto out; - -earlybad: -#ifdef INCLUDE_MOZILLA_DTRACE - if (JAVASCRIPT_OBJECT_CREATE_ENABLED()) - jsdtrace_object_create(cx, clasp, NULL); - if (JAVASCRIPT_OBJECT_CREATE_DONE_ENABLED()) - jsdtrace_object_create_done(cx->fp, clasp); -#endif - return NULL; } JSObject* js_NewNativeObject(JSContext *cx, JSClass *clasp, JSObject *proto, uint32 slot) { + JS_ASSERT(!clasp->getObjectOps); + JS_ASSERT(proto->map->ops == &js_ObjectOps); + JS_ASSERT(OBJ_GET_CLASS(cx, proto) == clasp); + JSObject* obj = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); if (!obj) return NULL; + js_HoldScope(OBJ_SCOPE(proto)); + obj->map = proto->map; obj->classword = jsuword(clasp); obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); obj->fslots[JSSLOT_PARENT] = proto->fslots[JSSLOT_PARENT]; @@ -3203,9 +3151,6 @@ js_NewNativeObject(JSContext *cx, JSClass *clasp, JSObject *proto, uint32 slot) while (slot < JS_INITIAL_NSLOTS) obj->fslots[slot++] = JSVAL_VOID; - JS_ASSERT(!clasp->getObjectOps); - JS_ASSERT(proto->map->ops == &js_ObjectOps); - obj->map = js_HoldObjectMap(cx, proto->map); obj->dslots = NULL; return obj; } @@ -3456,11 +3401,8 @@ bad: void js_FinalizeObject(JSContext *cx, JSObject *obj) { - JSObjectMap *map; - /* Cope with stillborn objects that have no map. */ - map = obj->map; - if (!map) + if (!obj->map) return; if (cx->debugHooks->objectHook) { @@ -3476,8 +3418,8 @@ js_FinalizeObject(JSContext *cx, JSObject *obj) jsdtrace_object_finalize(obj); #endif - /* Drop map and free slots. */ - js_DropObjectMap(cx, map, obj); + if (OBJ_IS_NATIVE(obj)) + js_DropScope(cx, OBJ_SCOPE(obj), obj); FreeSlots(cx, obj); } @@ -3486,38 +3428,35 @@ js_FinalizeObject(JSContext *cx, JSObject *obj) JSBool js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) { - JSObjectMap *map; - JSClass *clasp; + JS_ASSERT(OBJ_IS_NATIVE(obj)); - map = obj->map; - JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); - clasp = LOCKED_OBJ_GET_CLASS(obj); - if (map->freeslot == JSSLOT_FREE(clasp) && clasp->reserveSlots) { - /* Adjust map->freeslot to include computed reserved slots, if any. */ - map->freeslot += clasp->reserveSlots(cx, obj); + JSScope *scope = OBJ_SCOPE(obj); + JSClass *clasp = LOCKED_OBJ_GET_CLASS(obj); + if (scope->freeslot == JSSLOT_FREE(clasp) && clasp->reserveSlots) { + /* Adjust scope->freeslot to include computed reserved slots, if any. */ + scope->freeslot += clasp->reserveSlots(cx, obj); } - if (map->freeslot >= STOBJ_NSLOTS(obj) && - !js_ReallocSlots(cx, obj, map->freeslot + 1, JS_FALSE)) { + if (scope->freeslot >= STOBJ_NSLOTS(obj) && + !js_ReallocSlots(cx, obj, scope->freeslot + 1, JS_FALSE)) { return JS_FALSE; } /* js_ReallocSlots or js_FreeSlot should set the free slots to void. */ - JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, map->freeslot))); - *slotp = map->freeslot++; + JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, scope->freeslot))); + *slotp = scope->freeslot++; return JS_TRUE; } void js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) { - JSObjectMap *map; + JS_ASSERT(OBJ_IS_NATIVE(obj)); - map = obj->map; - JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); + JSScope *scope = OBJ_SCOPE(obj); LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_VOID); - if (map->freeslot == slot + 1) { - map->freeslot = slot; + if (scope->freeslot == slot + 1) { + scope->freeslot = slot; /* When shrinking, js_ReallocSlots always returns true. */ js_ReallocSlots(cx, obj, slot, JS_FALSE); @@ -3595,11 +3534,19 @@ void js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id) { JS_ASSERT(OBJ_IS_DELEGATE(cx, obj)); - PurgeProtoChain(cx, OBJ_GET_PROTO(cx, obj), id); - while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL) { - if (PurgeProtoChain(cx, obj, id)) - return; + + /* + * We must purge the scope chain only for Call objects as they are the only + * kind of cacheable non-global object that can gain properties after outer + * properties with the same names have been cached or traced. Call objects + * may gain such properties via eval introducing new vars; see bug 490364. + */ + if (STOBJ_GET_CLASS(obj) == &js_CallClass) { + while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL) { + if (PurgeProtoChain(cx, obj, id)) + break; + } } } @@ -3687,13 +3634,14 @@ JSBool js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs, uintN flags, intN shortid, JSProperty **propp, - JSBool cacheResult /* = JS_FALSE */) + uintN defineHow /* = 0 */) { JSClass *clasp; JSScope *scope; JSScopeProperty *sprop; bool added; + JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_DONT_PURGE)) == 0); js_LeaveTraceIfGlobalObject(cx, obj); /* Convert string indices to integers if appropriate. */ @@ -3746,17 +3694,20 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, #endif /* JS_HAS_GETTER_SETTER */ /* - * Purge the property cache of now-shadowed id in obj's scope chain. - * Do this early, before locking obj to avoid nesting locks. - * - * But first, purge the entire cache if obj is a prototype (we approximate - * this via OBJ_IS_DELEGATE) and we are defining a non-shadowable property - * on it (see bug 452189). + * Purge the property cache of any properties named by id that are about + * to be shadowed in obj's scope chain unless it is known a priori that it + * is not possible. We do this before locking obj to avoid nesting locks. + */ + if (!(defineHow & JSDNP_DONT_PURGE)) + js_PurgeScopeChain(cx, obj, id); + + /* + * Check whether a readonly property or setter is being defined on a known + * prototype object. See the comment in jscntxt.h before protoHazardShape's + * member declaration. */ if (OBJ_IS_DELEGATE(cx, obj) && (attrs & (JSPROP_READONLY | JSPROP_SETTER))) - js_PurgePropertyCache(cx, &JS_PROPERTY_CACHE(cx)); - else - js_PurgeScopeChain(cx, obj, id); + cx->runtime->protoHazardShape = js_GenerateShape(cx, false); /* Lock if object locking is required by this implementation. */ JS_LOCK_OBJ(cx, obj); @@ -3795,7 +3746,7 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, js_RemoveScopeProperty(cx, scope, id); goto error); - if (cacheResult) { + if (defineHow & JSDNP_CACHE_RESULT) { JS_ASSERT_NOT_ON_TRACE(cx); JSPropCacheEntry *entry; entry = js_FillPropertyCache(cx, obj, 0, 0, obj, sprop, added); @@ -3912,8 +3863,7 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, proto = OBJ_GET_PROTO(cx, proto)) { protoIndex++; } - scope = OBJ_SCOPE(obj2); - if (!MAP_IS_NATIVE(&scope->map)) { + if (!OBJ_IS_NATIVE(obj2)) { /* Whoops, newresolve handed back a foreign obj2. */ JS_ASSERT(obj2 != obj); ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp); @@ -3929,6 +3879,7 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, * not on obj's proto chain. That last case is a * "too bad!" case. */ + scope = OBJ_SCOPE(obj2); if (scope->object == obj2) sprop = SCOPE_GET_PROPERTY(scope, id); } @@ -3951,8 +3902,8 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, if (!ok) goto cleanup; JS_LOCK_OBJ(cx, obj); + JS_ASSERT(OBJ_IS_NATIVE(obj)); scope = OBJ_SCOPE(obj); - JS_ASSERT(MAP_IS_NATIVE(&scope->map)); if (scope->object == obj) sprop = SCOPE_GET_PROPERTY(scope, id); } @@ -4445,6 +4396,9 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult, JSPropertyOp getter, setter; bool added; + if (cacheResult) + JS_ASSERT_NOT_ON_TRACE(cx); + /* Convert string indices to integers if appropriate. */ CHECK_FOR_STRING_INDEX(id); @@ -4542,7 +4496,6 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult, /* Don't clone a shared prototype property. */ if (attrs & JSPROP_SHARED) { if (cacheResult) { - JS_ASSERT_NOT_ON_TRACE(cx); JSPropCacheEntry *entry; entry = js_FillPropertyCache(cx, obj, 0, protoIndex, pobj, sprop, false); TRACE_2(SetPropHit, entry, sprop); @@ -4626,7 +4579,6 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult, } if (cacheResult) { - JS_ASSERT_NOT_ON_TRACE(cx); JSPropCacheEntry *entry; entry = js_FillPropertyCache(cx, obj, 0, 0, obj, sprop, added); TRACE_2(SetPropHit, entry, sprop); @@ -5840,8 +5792,8 @@ js_TraceObject(JSTracer *trc, JSObject *obj) * above. */ nslots = STOBJ_NSLOTS(obj); - if (scope->object == obj && scope->map.freeslot < nslots) - nslots = scope->map.freeslot; + if (scope->object == obj && scope->freeslot < nslots) + nslots = scope->freeslot; for (i = 0; i != nslots; ++i) { v = STOBJ_GET_SLOT(obj, i); @@ -5875,7 +5827,7 @@ js_Clear(JSContext *cx, JSObject *obj) n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj)); while (--i >= n) STOBJ_SET_SLOT(obj, i, JSVAL_VOID); - scope->map.freeslot = n; + scope->freeslot = n; } JS_UNLOCK_OBJ(cx, obj); } @@ -5924,8 +5876,8 @@ js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v) } /* Whether or not we grew nslots, we may need to advance freeslot. */ - if (scope->object == obj && slot >= scope->map.freeslot) - scope->map.freeslot = slot + 1; + if (scope->object == obj && slot >= scope->freeslot) + scope->freeslot = slot + 1; STOBJ_SET_SLOT(obj, slot, v); GC_POKE(cx, JS_NULL); @@ -6129,7 +6081,7 @@ js_DumpObject(JSObject *obj) uint32 i, slots; JSClass *clasp; jsuint reservedEnd; - JSBool sharesScope = JS_FALSE; + bool sharesScope = false; fprintf(stderr, "object %p\n", (void *) obj); clasp = STOBJ_GET_CLASS(obj); @@ -6184,7 +6136,9 @@ js_DumpObject(JSObject *obj) if (clasp->flags & JSCLASS_HAS_PRIVATE) reservedEnd++; reservedEnd += JSCLASS_RESERVED_SLOTS(clasp); - slots = sharesScope ? reservedEnd : obj->map->freeslot; + slots = (OBJ_IS_NATIVE(obj) && !sharesScope) + ? OBJ_SCOPE(obj)->freeslot + : STOBJ_NSLOTS(obj); for (i = 0; i < slots; i++) { fprintf(stderr, " %3d ", i); if (i == JSSLOT_PRIVATE && (clasp->flags & JSCLASS_HAS_PRIVATE)) { diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 4757c051dc3..e1fb9ef4e88 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -56,9 +56,14 @@ JS_BEGIN_EXTERN_C /* For detailed comments on these function pointer types, see jsprvtd.h. */ struct JSObjectOps { + /* + * Custom shared object map for non-native objects. For native objects + * this should be null indicating, that JSObject.map is an instance of + * JSScope. + */ + const JSObjectMap *objectMap; + /* Mandatory non-null function pointer members. */ - JSNewObjectMapOp newObjectMap; - JSObjectMapOp destroyObjectMap; JSLookupPropOp lookupProperty; JSDefinePropOp defineProperty; JSPropertyIdOp getProperty; @@ -83,9 +88,7 @@ struct JSObjectOps { }; struct JSObjectMap { - jsrefcount nrefs; /* count of all referencing objects */ JSObjectOps *ops; /* high level object operation vtable */ - uint32 freeslot; /* index of next free slot in object */ }; /* Shorthand macros for frequently-made calls. */ @@ -206,8 +209,8 @@ struct JSObject { /* * STOBJ prefix means Single Threaded Object. Use the following fast macros to - * directly manipulate slots in obj when only one thread can access obj and - * when obj->map->freeslot can be inconsistent with slots. + * directly manipulate slots in obj when only one thread can access obj, or + * when accessing read-only slots within JS_INITIAL_NSLOTS. */ #define STOBJ_NSLOTS(obj) \ @@ -266,7 +269,7 @@ STOBJ_GET_CLASS(const JSObject* obj) JSVAL_TO_PRIVATE(STOBJ_GET_SLOT(obj, JSSLOT_PRIVATE))) #define OBJ_CHECK_SLOT(obj,slot) \ - JS_ASSERT(slot < (obj)->map->freeslot) + JS_ASSERT_IF(OBJ_IS_NATIVE(obj), slot < OBJ_SCOPE(obj)->freeslot) #define LOCKED_OBJ_GET_SLOT(obj,slot) \ (OBJ_CHECK_SLOT(obj, slot), STOBJ_GET_SLOT(obj, slot)) @@ -368,12 +371,14 @@ STOBJ_GET_CLASS(const JSObject* obj) #define OBJ_GET_CLASS(cx,obj) STOBJ_GET_CLASS(obj) #define OBJ_GET_PRIVATE(cx,obj) STOBJ_GET_PRIVATE(obj) -/* Test whether a map or object is native. */ -#define MAP_IS_NATIVE(map) \ - JS_LIKELY((map)->ops == &js_ObjectOps || \ - (map)->ops->newObjectMap == js_ObjectOps.newObjectMap) +/* + * Test whether the object is native. FIXME bug 492938: consider how it would + * affect the performance to do just the !ops->objectMap check. + */ +#define OPS_IS_NATIVE(ops) \ + JS_LIKELY((ops) == &js_ObjectOps || !(ops)->objectMap) -#define OBJ_IS_NATIVE(obj) MAP_IS_NATIVE((obj)->map) +#define OBJ_IS_NATIVE(obj) OPS_IS_NATIVE((obj)->map->ops) extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps; extern JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps; @@ -502,23 +507,6 @@ extern const char js_defineSetter_str[]; extern const char js_lookupGetter_str[]; extern const char js_lookupSetter_str[]; -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 JSBool js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp); @@ -653,11 +641,17 @@ js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, #ifdef __cplusplus /* FIXME: bug 442399 removes this LiveConnect requirement. */ +/* + * Flags for the defineHow parameter of js_DefineNativeProperty. + */ +const uintN JSDNP_CACHE_RESULT = 1; /* an interpreter call from JSOP_INITPROP */ +const uintN JSDNP_DONT_PURGE = 2; /* suppress js_PurgeScopeChain */ + extern JSBool js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs, uintN flags, intN shortid, JSProperty **propp, - JSBool cacheResult = JS_FALSE); + uintN defineHow = 0); #endif /* diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index 7f4490d2aac..b24163c7f20 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -2297,7 +2297,7 @@ LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSTreeContext *tc, */ *pnup = outer_dn->dn_uses; outer_dn->dn_uses = dn; - outer_dn->pn_dflags |= (dn->pn_dflags & ~PND_PLACEHOLDER); + outer_dn->pn_dflags |= dn->pn_dflags & ~(PND_FORWARD | PND_PLACEHOLDER); dn->pn_defn = false; dn->pn_used = true; dn->pn_lexdef = outer_dn; @@ -2973,7 +2973,7 @@ BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) !js_ReallocSlots(cx, blockObj, slot + 1, JS_FALSE)) { return JS_FALSE; } - blockObj->map->freeslot = slot + 1; + OBJ_SCOPE(blockObj)->freeslot = slot + 1; STOBJ_SET_SLOT(blockObj, slot, PRIVATE_TO_JSVAL(pn)); return JS_TRUE; } @@ -6091,7 +6091,10 @@ CompExprTransplanter::transplant(JSParseNode *pn) case PN_BINARY: transplant(pn->pn_left); - transplant(pn->pn_right); + + /* Binary TOK_COLON nodes can have left == right. See bug 492714. */ + if (pn->pn_right != pn->pn_left) + transplant(pn->pn_right); break; case PN_UNARY: diff --git a/js/src/jsparse.h b/js/src/jsparse.h index d62d55d0bf3..b49803054b1 100644 --- a/js/src/jsparse.h +++ b/js/src/jsparse.h @@ -183,8 +183,10 @@ JS_BEGIN_EXTERN_C * TOK_RB list pn_head: list of pn_count array element exprs * [,,] holes are represented by TOK_COMMA nodes * pn_xflags: PN_ENDCOMMA 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 + * TOK_RC list pn_head: list of pn_count binary TOK_COLON nodes + * TOK_COLON binary key-value pair in object initializer or + * destructuring lhs + * pn_left: property id, pn_right: value * var {x} = object destructuring shorthand shares * PN_NAME node for x on left and right of TOK_COLON * node in TOK_RC's list, has PNX_DESTRUCT flag diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index 517ecde41a8..377c68cb92f 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -257,30 +257,6 @@ struct JSTempValueRooter { /* JSObjectOps function pointer typedefs. */ -/* - * Create a new subclass of JSObjectMap (see jsobj.h), with the nrefs and ops - * members initialized from the same-named parameters, and with the nslots and - * freeslot members initialized according to ops and clasp. Return null on - * error, non-null on success. - * - * JSObjectMaps are reference-counted by generic code in the engine. Usually, - * the nrefs parameter to JSObjectOps.newObjectMap will be 1, to count the ref - * returned to the caller on success. After a successful construction, some - * number of js_HoldObjectMap and js_DropObjectMap calls ensue. When nrefs - * reaches 0 due to a js_DropObjectMap call, JSObjectOps.destroyObjectMap will - * be called to dispose of the map. - */ -typedef JSObjectMap * -(* JSNewObjectMapOp)(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp, JSObject *obj); - -/* - * Generic type for an infallible JSObjectMap operation, used currently by - * JSObjectOps.destroyObjectMap. - */ -typedef void -(* JSObjectMapOp)(JSContext *cx, JSObjectMap *map); - /* * Look for id in obj and its prototype chain, returning false on error or * exception, true on success. On success, return null in *propp if id was diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index 9496535ccb0..546579a397b 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -77,23 +77,23 @@ js_GetMutableScope(JSContext *cx, JSObject *obj) * birth, and runtime clone of a block objects are never mutated. */ JS_ASSERT(STOBJ_GET_CLASS(obj) != &js_BlockClass); - newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj), - obj); + newscope = js_NewScope(cx, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj), obj); if (!newscope) return NULL; JS_LOCK_SCOPE(cx, newscope); - obj->map = js_HoldObjectMap(cx, &newscope->map); - JS_ASSERT(newscope->map.freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj))); + obj->map = &newscope->map; + + JS_ASSERT(newscope->freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj))); clasp = STOBJ_GET_CLASS(obj); if (clasp->reserveSlots) { freeslot = JSSLOT_FREE(clasp) + clasp->reserveSlots(cx, obj); if (freeslot > STOBJ_NSLOTS(obj)) freeslot = STOBJ_NSLOTS(obj); - if (newscope->map.freeslot < freeslot) - newscope->map.freeslot = freeslot; + if (newscope->freeslot < freeslot) + newscope->freeslot = freeslot; } - scope = (JSScope *) js_DropObjectMap(cx, &scope->map, obj); JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); + js_DropScope(cx, scope, obj); return newscope; } @@ -160,17 +160,19 @@ CreateScopeTable(JSContext *cx, JSScope *scope, JSBool report) } JSScope * -js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, - JSObject *obj) +js_NewScope(JSContext *cx, JSObjectOps *ops, JSClass *clasp, JSObject *obj) { - JSScope *scope; + JS_ASSERT(OPS_IS_NATIVE(ops)); + JS_ASSERT(obj); - scope = (JSScope *) JS_malloc(cx, sizeof(JSScope)); + JSScope *scope = (JSScope *) JS_malloc(cx, sizeof(JSScope)); if (!scope) return NULL; - js_InitObjectMap(&scope->map, nrefs, ops, clasp); + scope->map.ops = ops; scope->object = obj; + scope->nrefs = 1; + scope->freeslot = JSSLOT_FREE(clasp); scope->flags = 0; InitMinimalScope(cx, scope); @@ -203,6 +205,28 @@ js_DestroyScope(JSContext *cx, JSScope *scope) JS_free(cx, scope); } +void +js_HoldScope(JSScope *scope) +{ + JS_ASSERT(scope->nrefs >= 0); + JS_ATOMIC_INCREMENT(&scope->nrefs); +} + +JSBool +js_DropScope(JSContext *cx, JSScope *scope, JSObject *obj) +{ + JS_ASSERT(scope->nrefs > 0); + JS_ATOMIC_DECREMENT(&scope->nrefs); + + if (scope->nrefs == 0) { + js_DestroyScope(cx, scope); + return false; + } + if (scope->object == obj) + scope->object = NULL; + return true; +} + #ifdef JS_DUMP_PROPTREE_STATS typedef struct JSScopeStats { jsrefcount searches; diff --git a/js/src/jsscope.h b/js/src/jsscope.h index 47026d846bd..31133cc5b65 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -201,6 +201,8 @@ struct JSScope { JSTitle title; /* lock state */ #endif JSObject *object; /* object that owns this scope */ + jsrefcount nrefs; /* count of all referencing objects */ + uint32 freeslot; /* index of next free slot in object */ uint32 shape; /* property cache shape identifier */ uint8 flags; /* flags, see below */ int8 hashShift; /* multiplicative hash shift */ @@ -213,7 +215,8 @@ struct JSScope { #define JS_IS_SCOPE_LOCKED(cx, scope) JS_IS_TITLE_LOCKED(cx, &(scope)->title) -#define OBJ_SCOPE(obj) ((JSScope *)(obj)->map) +#define OBJ_SCOPE(obj) (JS_ASSERT(OBJ_IS_NATIVE(obj)), \ + (JSScope *) (obj)->map) #define OBJ_SHAPE(obj) (OBJ_SCOPE(obj)->shape) /* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */ @@ -329,20 +332,22 @@ struct JSScopeProperty { #define SPROP_INVALID_SLOT 0xffffffff -#define SLOT_IN_SCOPE(slot,scope) ((slot) < (scope)->map.freeslot) +#define SLOT_IN_SCOPE(slot,scope) ((slot) < (scope)->freeslot) #define SPROP_HAS_VALID_SLOT(sprop,scope) SLOT_IN_SCOPE((sprop)->slot, scope) #define SPROP_HAS_STUB_GETTER(sprop) (!(sprop)->getter) #define SPROP_HAS_STUB_SETTER(sprop) (!(sprop)->setter) static inline void -js_MakeScopeShapeUnique(JSContext* cx, JSScope* scope) { +js_MakeScopeShapeUnique(JSContext *cx, JSScope *scope) +{ js_LeaveTraceIfGlobalObject(cx, scope->object); scope->shape = js_GenerateShape(cx, JS_FALSE); } static inline void -js_ExtendScopeShape(JSContext *cx, JSScope *scope, JSScopeProperty *sprop) { +js_ExtendScopeShape(JSContext *cx, JSScope *scope, JSScopeProperty *sprop) +{ js_LeaveTraceIfGlobalObject(cx, scope->object); if (!scope->lastProp || scope->shape == scope->lastProp->shape) { @@ -394,12 +399,17 @@ extern JSScope * js_GetMutableScope(JSContext *cx, JSObject *obj); extern JSScope * -js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, - JSObject *obj); +js_NewScope(JSContext *cx, JSObjectOps *ops, JSClass *clasp, JSObject *obj); extern void js_DestroyScope(JSContext *cx, JSScope *scope); +extern void +js_HoldScope(JSScope *scope); + +extern JSBool +js_DropScope(JSContext *cx, JSScope *scope, JSObject *obj); + extern JS_FRIEND_API(JSScopeProperty **) js_SearchScope(JSScope *scope, jsid id, JSBool adding); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index a72ab26be64..65518b3327e 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -3413,13 +3413,13 @@ js_StartRecorder(JSContext* cx, VMSideExit* anchor, Fragment* f, TreeInfo* ti, VMSideExit* expectedInnerExit, jsbytecode* outer, uint32 outerArgc) { JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); - JS_ASSERT(f->root != f || !cx->fp->imacpc); - if (JS_TRACE_MONITOR(cx).needFlush) { FlushJITCache(cx); return false; } + JS_ASSERT(f->root != f || !cx->fp->imacpc); + /* start recording if no exception during construction */ tm->recorder = new (&gc) TraceRecorder(cx, anchor, f, ti, stackSlots, ngslots, typeMap, @@ -3776,7 +3776,11 @@ JS_REQUIRES_STACK static bool js_AttemptToStabilizeTree(JSContext* cx, VMSideExit* exit, jsbytecode* outer, uint32 outerArgc) { JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); - JS_ASSERT(!tm->needFlush); + if (tm->needFlush) { + FlushJITCache(cx); + return false; + } + VMFragment* from = (VMFragment*)exit->from->root; TreeInfo* from_ti = (TreeInfo*)from->vmprivate; @@ -3883,7 +3887,12 @@ js_AttemptToStabilizeTree(JSContext* cx, VMSideExit* exit, jsbytecode* outer, ui static JS_REQUIRES_STACK bool js_AttemptToExtendTree(JSContext* cx, VMSideExit* anchor, VMSideExit* exitedFrom, jsbytecode* outer) { - JS_ASSERT(!JS_TRACE_MONITOR(cx).needFlush); + JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); + if (tm->needFlush) { + FlushJITCache(cx); + return false; + } + Fragment* f = anchor->from->root; JS_ASSERT(f->vmprivate); TreeInfo* ti = (TreeInfo*)f->vmprivate; @@ -4598,10 +4607,10 @@ LeaveTree(InterpState& state, VMSideExit* lr) stack, NULL); JS_ASSERT(unsigned(slots) == innermost->numStackSlots); - if (innermost->nativeCalleeWord) { + if (innermost->nativeCalleeWord) SynthesizeSlowNativeFrame(cx, innermost); - cx->nativeVp = NULL; - } + + cx->nativeVp = NULL; #ifdef DEBUG // Verify that our state restoration worked. @@ -5282,15 +5291,16 @@ js_PurgeScriptFragments(JSContext* cx, JSScript* script) JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) { for (VMFragment **f = &(tm->vmfragments[i]); *f; ) { + VMFragment* frag = *f; /* Disable future use of any script-associated VMFragment.*/ - if (JS_UPTRDIFF((*f)->ip, script->code) < script->length) { + if (JS_UPTRDIFF(frag->ip, script->code) < script->length) { + JS_ASSERT(frag->root == frag); debug_only_v(printf("Disconnecting VMFragment %p " "with ip %p, in range [%p,%p).\n", - (void*)(*f), (*f)->ip, script->code, + (void*)frag, frag->ip, script->code, script->code + script->length)); - VMFragment* next = (*f)->next; - if (tm->fragmento) - tm->fragmento->clearFragment(*f); + VMFragment* next = frag->next; + js_TrashTree(cx, frag); *f = next; } else { f = &((*f)->next); @@ -5352,7 +5362,7 @@ js_OverfullFragmento(JSTraceMonitor* tm, Fragmento *fragmento) */ maxsz /= 16; } - return (fragmento->_stats.pages > (maxsz >> NJ_LOG2_PAGE_SIZE)); + return (fragmento->cacheUsed() > maxsz); } JS_FORCES_STACK JS_FRIEND_API(void) @@ -6291,21 +6301,25 @@ TraceRecorder::binary(LOpcode op) return JSRS_STOP; } -JS_STATIC_ASSERT(offsetof(JSObjectOps, newObjectMap) == 0); +JS_STATIC_ASSERT(offsetof(JSObjectOps, objectMap) == 0); bool TraceRecorder::map_is_native(JSObjectMap* map, LIns* map_ins, LIns*& ops_ins, size_t op_offset) { -#define OP(ops) (*(JSObjectOp*) ((char*)(ops) + op_offset)) - if (OP(map->ops) != OP(&js_ObjectOps)) - return false; + JS_ASSERT(op_offset < sizeof(JSObjectOps)); + JS_ASSERT(op_offset % sizeof(void *) == 0); - ops_ins = addName(lir->insLoad(LIR_ldp, map_ins, offsetof(JSObjectMap, ops)), "ops"); +#define OP(ops) (*(void **) ((uint8 *) (ops) + op_offset)) + void* ptr = OP(map->ops); + if (ptr != OP(&js_ObjectOps)) + return false; +#undef OP + + ops_ins = addName(lir->insLoad(LIR_ldp, map_ins, int(offsetof(JSObjectMap, ops))), "ops"); LIns* n = lir->insLoad(LIR_ldp, ops_ins, op_offset); guard(true, - addName(lir->ins2(LIR_eq, n, INS_CONSTFUNPTR(OP(&js_ObjectOps))), "guard(native-map)"), + addName(lir->ins2(LIR_eq, n, INS_CONSTPTR(ptr)), "guard(native-map)"), BRANCH_EXIT); -#undef OP return true; } @@ -6330,10 +6344,9 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2 LIns* ops_ins; // Interpreter calls to PROPERTY_CACHE_TEST guard on native object ops - // (newObjectMap == js_ObjectOps.newObjectMap) which is required to use - // native objects (those whose maps are scopes), or even more narrow - // conditions required because the cache miss case will call a particular - // object-op (js_GetProperty, js_SetProperty). + // which is required to use native objects (those whose maps are scopes), + // or even more narrow conditions required because the cache miss case + // will call a particular object-op (js_GetProperty, js_SetProperty). // // We parameterize using offsetof and guard on match against the hook at // the given offset in js_ObjectOps. TraceRecorder::record_JSOP_SETPROP @@ -6344,7 +6357,7 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2 // No need to guard native-ness of global object. JS_ASSERT(OBJ_IS_NATIVE(globalObj)); if (aobj != globalObj) { - size_t op_offset = offsetof(JSObjectOps, newObjectMap); + size_t op_offset = offsetof(JSObjectOps, objectMap); if (mode == JOF_PROP || mode == JOF_VARPROP) { JS_ASSERT(!(format & JOF_SET)); op_offset = offsetof(JSObjectOps, getProperty); @@ -6641,7 +6654,7 @@ TraceRecorder::getThis(LIns*& this_ins) JS_ASSERT(callDepth == 0); JSObject* thisObj = js_ComputeThisForFrame(cx, cx->fp); if (!thisObj) - ABORT_TRACE_ERROR("error in js_ComputeThis"); + ABORT_TRACE_ERROR("error in js_ComputeThisForFrame"); this_ins = INS_CONSTPTR(thisObj); /* @@ -6650,6 +6663,8 @@ TraceRecorder::getThis(LIns*& this_ins) return JSRS_CONTINUE; } + jsval& thisv = cx->fp->argv[-1]; + /* * Traces type-specialize between null and objects, so if we currently see a null * value in argv[-1], this trace will only match if we see null at runtime as well. @@ -6657,31 +6672,30 @@ TraceRecorder::getThis(LIns*& this_ins) * can only detect this condition prior to calling js_ComputeThisForFrame, since it * updates the interpreter's copy of argv[-1]. */ - if (JSVAL_IS_NULL(cx->fp->argv[-1])) { + if (JSVAL_IS_NULL(thisv)) { JSObject* thisObj = js_ComputeThisForFrame(cx, cx->fp); if (!thisObj) - ABORT_TRACE_ERROR("js_ComputeThis failed"); - JS_ASSERT(!JSVAL_IS_PRIMITIVE(cx->fp->argv[-1])); + ABORT_TRACE_ERROR("js_ComputeThisForName failed"); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(thisv)); if (thisObj != globalObj) ABORT_TRACE("global object was wrapped while recording"); this_ins = INS_CONSTPTR(thisObj); - set(&cx->fp->argv[-1], this_ins); + set(&thisv, this_ins); return JSRS_CONTINUE; } - this_ins = get(&cx->fp->argv[-1]); + this_ins = get(&thisv); /* - * When we inline through scripted functions, we have already previously touched the 'this' - * object and hence it is already guaranteed to be wrapped. Otherwise we have to explicitly - * check that the object has been wrapped. If not, we side exit and let the interpreter - * wrap it. + * mrbkap says its not necessary to ever call the thisObject hook if obj is not the global + * object, because the only implicit way to obtain a reference to an object that must be + * wrapped is via the global object. All other sources (API, explicit references) already + * are wrapped as we obtain them through XPConnect. The only exception are With objects, + * which have to call the getThis object hook. We don't trace those cases. */ - if (callDepth == 0) { - LIns* map_ins = lir->insLoad(LIR_ldp, this_ins, (int)offsetof(JSObject, map)); - LIns* ops_ins = lir->insLoad(LIR_ldp, map_ins, (int)offsetof(JSObjectMap, ops)); - LIns* op_ins = lir->insLoad(LIR_ldp, ops_ins, (int)offsetof(JSObjectOps, thisObject)); - guard(true, lir->ins_eq0(op_ins), MISMATCH_EXIT); - } + + if (guardClass(JSVAL_TO_OBJECT(thisv), this_ins, &js_WithClass, snapshot(MISMATCH_EXIT))) + ABORT_TRACE("can't trace getThis on With object"); + return JSRS_CONTINUE; } @@ -7567,6 +7581,10 @@ TraceRecorder::callNative(uintN argc, JSOp mode) return status; } + JSFastNative native = (JSFastNative)fun->u.n.native; + if (native == js_fun_apply || native == js_fun_call) + ABORT_TRACE("trying to call native apply or call"); + // Allocate the vp vector and emit code to root it. uintN vplen = 2 + JS_MAX(argc, FUN_MINARGS(fun)) + fun->u.n.extra; if (!(fun->flags & JSFUN_FAST_NATIVE)) @@ -7606,29 +7624,27 @@ TraceRecorder::callNative(uintN argc, JSOp mode) this_ins = INS_CONSTWORD(OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, funobj))); } else { this_ins = get(&vp[1]); - if (mode == JSOP_APPLY) { - // For JSOP_CALL or JSOP_NEW, the preceding JSOP_CALLNAME (or - // similar) instruction ensures that vp[1] is an appropriate - // |this|. In the case of JSOP_APPLY, that has not happened, so we - // must do the equivalent of js_ComputeThis. + /* + * For fast natives, 'null' or primitives are fine as as 'this' value. + * For slow natives we have to ensure the object is substituted for the + * appropriate global object or boxed object value. JSOP_NEW allocates its + * own object so its guaranteed to have a valid 'this' value. + */ + if (!(fun->flags & JSFUN_FAST_NATIVE)) { if (JSVAL_IS_NULL(vp[1])) { - // For fast natives, null is fine here. Slow natives require a - // call to js_ComputeGlobalThis, which we do not yet attempt on - // trace. - if (!(fun->flags & JSFUN_FAST_NATIVE)) - ABORT_TRACE("slowNative.apply(null, args)"); - } else if (!JSVAL_IS_PRIMITIVE(vp[1])) { - // Check that there is no thisObject hook to call. - if (JSVAL_TO_OBJECT(vp[1])->map->ops->thisObject) - ABORT_TRACE("|this| argument with thisObject hook"); - LIns* map_ins = lir->insLoad(LIR_ldp, this_ins, (int) offsetof(JSObject, map)); - LIns* ops_ins = lir->insLoad(LIR_ldp, map_ins, (int) offsetof(JSObjectMap, ops)); - LIns* hook_ins = lir->insLoad(LIR_ldp, ops_ins, - (int) offsetof(JSObjectOps, thisObject)); - guard(true, lir->ins_eq0(hook_ins), MISMATCH_EXIT); + JSObject* thisObj = js_ComputeThis(cx, JS_FALSE, vp + 2); + if (!thisObj) + ABORT_TRACE_ERROR("error in js_ComputeGlobalThis"); + this_ins = INS_CONSTPTR(thisObj); + } else if (!JSVAL_IS_OBJECT(vp[1])) { + ABORT_TRACE("slow native(primitive, args)"); } else { - if (!PRIMITIVE_THIS_TEST(fun, vp[1])) - ABORT_TRACE("fun.apply(primitive, args)"); + if (guardClass(JSVAL_TO_OBJECT(vp[1]), this_ins, &js_WithClass, snapshot(MISMATCH_EXIT))) + ABORT_TRACE("can't trace slow native invocation on With object"); + + this_ins = lir->ins_choose(lir->ins_eq0(stobj_get_fslot(this_ins, JSSLOT_PARENT)), + INS_CONSTPTR(globalObj), + this_ins); } } box_jsval(vp[1], this_ins); @@ -7956,11 +7972,8 @@ TraceRecorder::record_SetPropHit(JSPropCacheEntry* entry, JSScopeProperty* sprop LIns* obj_ins = get(&l); JSScope* scope = OBJ_SCOPE(obj); -#ifdef DEBUG JS_ASSERT(scope->object == obj); - JS_ASSERT(scope->shape == PCVCAP_SHAPE(entry->vcap)); JS_ASSERT(SCOPE_HAS_PROPERTY(scope, sprop)); -#endif if (!isValidSlot(scope, sprop)) return JSRS_STOP; @@ -7996,10 +8009,17 @@ TraceRecorder::record_SetPropHit(JSPropCacheEntry* entry, JSScopeProperty* sprop ABORT_TRACE("non-native map"); LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)), "shape"); - guard(true, addName(lir->ins2i(LIR_eq, shape_ins, entry->kshape), "guard(shape)"), + guard(true, addName(lir->ins2i(LIR_eq, shape_ins, entry->kshape), "guard(kshape)"), BRANCH_EXIT); - if (entry->kshape != PCVCAP_SHAPE(entry->vcap)) { + uint32 vshape = PCVCAP_SHAPE(entry->vcap); + if (entry->kshape != vshape) { + LIns *vshape_ins = lir->insLoad(LIR_ld, + lir->insLoad(LIR_ldp, cx_ins, offsetof(JSContext, runtime)), + offsetof(JSRuntime, protoHazardShape)); + guard(true, addName(lir->ins2i(LIR_eq, vshape_ins, vshape), "guard(vshape)"), + MISMATCH_EXIT); + LIns* args[] = { INS_CONSTPTR(sprop), obj_ins, cx_ins }; LIns* ok_ins = lir->insCall(&js_AddProperty_ci, args); guard(false, lir->ins_eq0(ok_ins), OOM_EXIT); @@ -8334,78 +8354,60 @@ TraceRecorder::record_JSOP_GETUPVAR() JSUpvarArray* uva = JS_SCRIPT_UPVARS(script); JS_ASSERT(index < uva->length); - uintN skip = UPVAR_FRAME_SKIP(uva->vector[index]); - if (skip > callDepth) { - /* - * The frame containing the upvar is not part of the trace, so we - * get the upvar value exactly as the interpreter does and unbox. - */ - jsval v = js_GetUpvar(cx, script->staticLevel, uva->vector[index]); - uint8 type = getCoercedType(v); - - LIns* outp = lir->insAlloc(sizeof(double)); - - LIns* args[] = { - outp, - lir->insImm(uva->vector[index]), - lir->insImm(script->staticLevel), - cx_ins - }; - const CallInfo* ci = &js_GetUpvarOnTrace_ci; - LIns* call_ins = lir->insCall(ci, args); - guard(true, - addName(lir->ins2(LIR_eq, call_ins, lir->insImm(type)), - "guard(type-stable upvar)"), - BRANCH_EXIT); - - LOpcode loadOp; - switch (type) { - case JSVAL_DOUBLE: - loadOp = LIR_ldq; - break; - case JSVAL_OBJECT: - case JSVAL_STRING: - case JSVAL_TFUN: - case JSVAL_TNULL: - loadOp = LIR_ldp; - break; - case JSVAL_INT: - case JSVAL_BOOLEAN: - loadOp = LIR_ld; - break; - case JSVAL_BOXED: - default: - JS_NOT_REACHED("found boxed type in an upvar type map entry"); - return JSRS_STOP; - } - - LIns* result = lir->insLoad(loadOp, outp, lir->insImm(0)); - if (type == JSVAL_INT) - result = lir->ins1(LIR_i2f, result); - stack(0, result); + /* + * Try to find the upvar in the current trace's tracker. + */ + jsval& v = js_GetUpvar(cx, script->staticLevel, uva->vector[index]); + LIns* upvar_ins = get(&v); + if (upvar_ins) { + stack(0, upvar_ins); return JSRS_CONTINUE; } /* - * At this point, the frame containing the upvar is part of the trace, - * so the upvar is in the tracker. We only need to update the tracker. + * The upvar is not in the current trace, so get the upvar value + * exactly as the interpreter does and unbox. */ - JSStackFrame* fp2 = cx->display[script->staticLevel - skip]; - JS_ASSERT(fp2->script); + LIns* outp = lir->insAlloc(sizeof(double)); + LIns* args[] = { + outp, + lir->insImm(uva->vector[index]), + lir->insImm(script->staticLevel), + cx_ins + }; + const CallInfo* ci = &js_GetUpvarOnTrace_ci; + LIns* call_ins = lir->insCall(ci, args); + uint8 type = getCoercedType(v); + guard(true, + addName(lir->ins2(LIR_eq, call_ins, lir->insImm(type)), + "guard(type-stable upvar)"), + BRANCH_EXIT); - uintN slot = UPVAR_FRAME_SLOT(uva->vector[index]); - jsval* vp; - if (!fp2->fun) { - vp = fp2->slots + fp2->script->nfixed; - } else if (slot < fp2->fun->nargs) { - vp = fp2->argv; - } else { - slot -= fp2->fun->nargs; - JS_ASSERT(slot < fp2->script->nslots); - vp = fp2->slots; + LOpcode loadOp; + switch (type) { + case JSVAL_DOUBLE: + loadOp = LIR_ldq; + break; + case JSVAL_OBJECT: + case JSVAL_STRING: + case JSVAL_TFUN: + case JSVAL_TNULL: + loadOp = LIR_ldp; + break; + case JSVAL_INT: + case JSVAL_BOOLEAN: + loadOp = LIR_ld; + break; + case JSVAL_BOXED: + default: + JS_NOT_REACHED("found boxed type in an upvar type map entry"); + return JSRS_STOP; } - stack(0, get(&vp[slot])); + LIns* result = lir->insLoad(loadOp, outp, lir->insImm(0)); + if (type == JSVAL_INT) + result = lir->ins1(LIR_i2f, result); + stack(0, result); return JSRS_CONTINUE; } @@ -10379,8 +10381,10 @@ TraceRecorder::record_JSOP_GETTHISPROP() LIns* this_ins; CHECK_STATUS(getThis(this_ins)); - /* its safe to just use cx->fp->thisp here because getThis() returns JSRS_STOP if thisp - is not available */ + /* + * It's safe to just use cx->fp->thisp here because getThis() returns JSRS_STOP if thisp + * is not available. + */ CHECK_STATUS(getProp(cx->fp->thisp, this_ins)); return JSRS_CONTINUE; } @@ -10622,13 +10626,20 @@ TraceRecorder::record_JSOP_NEWARRAY() guard(false, lir->ins_eq0(v_ins), OOM_EXIT); LIns* dslots_ins = NULL; + uint32 count = 0; for (uint32 i = 0; i < len; i++) { jsval& v = stackval(int(i) - int(len)); + if (v != JSVAL_HOLE) + count++; LIns* elt_ins = get(&v); box_jsval(v, elt_ins); stobj_set_dslot(v_ins, i, dslots_ins, elt_ins, "set_array_elt"); } + LIns* dummy = NULL; + if (count > 0) + stobj_set_slot(v_ins, JSSLOT_ARRAY_COUNT, dummy, INS_CONST(count)); + stack(-int(len), v_ins); return JSRS_CONTINUE; } diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index e2529e64ce9..949350d6499 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -4912,10 +4912,10 @@ xml_trace_vector(JSTracer *trc, JSXML **vec, uint32 len) } /* - * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to - * be native. Thus xml_lookupProperty must return a valid JSScopeProperty - * pointer parameter via *propp to signify "property found". Since the only - * call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from + * js_XMLObjectOps.newObjectMap is null, so XML objects appear to be native. + * Thus xml_lookupProperty must return a valid JSScopeProperty pointer + * parameter via *propp to signify "property found". Since the only call to + * xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from * js_FindProperty (in jsobj.c, called from jsinterp.c) or from JSOP_IN case * in the interpreter, the only time we add a JSScopeProperty here is when an * unqualified name is being accessed or when "name in xml" is called. @@ -5428,9 +5428,9 @@ out: return ok; } -/* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */ +/* Use NULL for objectMap so XML objects satisfy OBJ_IS_NATIVE tests. */ JS_FRIEND_DATA(JSObjectOps) js_XMLObjectOps = { - js_NewObjectMap, js_DestroyObjectMap, + NULL, xml_lookupProperty, xml_defineProperty, xml_getProperty, xml_setProperty, xml_getAttributes, xml_setAttributes, diff --git a/js/src/liveconnect/jsj_JavaArray.c b/js/src/liveconnect/jsj_JavaArray.c index b235cb15136..b67eb909ea1 100644 --- a/js/src/liveconnect/jsj_JavaArray.c +++ b/js/src/liveconnect/jsj_JavaArray.c @@ -404,10 +404,14 @@ JavaArray_checkAccess(JSContext *cx, JSObject *obj, jsid id, } } +extern JSObjectOps JavaArray_ops; + +static const JSObjectMap JavaArrayMap = { &JavaArray_ops }; + JSObjectOps JavaArray_ops = { + &JavaArrayMap, /* objectMap */ + /* Mandatory non-null function pointer members. */ - jsj_wrapper_newObjectMap, /* newObjectMap */ - jsj_wrapper_destroyObjectMap, /* destroyObjectMap */ JavaArray_lookupProperty, JavaArray_defineProperty, JavaArray_getPropertyById, /* getProperty */ diff --git a/js/src/liveconnect/jsj_JavaClass.c b/js/src/liveconnect/jsj_JavaClass.c index 2fe80c49476..238fc0aad71 100644 --- a/js/src/liveconnect/jsj_JavaClass.c +++ b/js/src/liveconnect/jsj_JavaClass.c @@ -529,10 +529,14 @@ done: return JS_TRUE; } +extern JSObjectOps JavaClass_ops; + +static const JSObjectMap JavaClassMap = { &JavaClass_ops }; + JSObjectOps JavaClass_ops = { + &JavaClassMap, /* objectMap */ + /* Mandatory non-null function pointer members. */ - jsj_wrapper_newObjectMap, /* newObjectMap */ - jsj_wrapper_destroyObjectMap, /* destroyObjectMap */ JavaClass_lookupProperty, JavaClass_defineProperty, JavaClass_getPropertyById, /* getProperty */ diff --git a/js/src/liveconnect/jsj_JavaObject.c b/js/src/liveconnect/jsj_JavaObject.c index ffa9bee1549..2d9a5fe32eb 100644 --- a/js/src/liveconnect/jsj_JavaObject.c +++ b/js/src/liveconnect/jsj_JavaObject.c @@ -999,32 +999,10 @@ JavaObject_checkAccess(JSContext *cx, JSObject *obj, jsid id, #define JSJ_SLOT_COUNT (JSSLOT_PRIVATE+1) -JSObjectMap * -jsj_wrapper_newObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp, JSObject *obj) -{ - JSObjectMap * map; - - map = (JSObjectMap *) JS_malloc(cx, sizeof(JSObjectMap)); - if (map) { - map->nrefs = nrefs; - map->ops = ops; - map->freeslot = JSJ_SLOT_COUNT; - } - return map; -} - -void -jsj_wrapper_destroyObjectMap(JSContext *cx, JSObjectMap *map) -{ - JS_free(cx, map); -} - jsval jsj_wrapper_getRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot) { JS_ASSERT(slot < JSJ_SLOT_COUNT); - JS_ASSERT(obj->map->freeslot == JSJ_SLOT_COUNT); return STOBJ_GET_SLOT(obj, slot); } @@ -1032,15 +1010,18 @@ JSBool jsj_wrapper_setRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v) { JS_ASSERT(slot < JSJ_SLOT_COUNT); - JS_ASSERT(obj->map->freeslot == JSJ_SLOT_COUNT); STOBJ_SET_SLOT(obj, slot, v); return JS_TRUE; } +extern JSObjectOps JavaObject_ops; + +static const JSObjectMap JavaObjectMap = { &JavaObject_ops }; + JSObjectOps JavaObject_ops = { + &JavaObjectMap, /* objectMap */ + /* Mandatory non-null function pointer members. */ - jsj_wrapper_newObjectMap, /* newObjectMap */ - jsj_wrapper_destroyObjectMap, /* destroyObjectMap */ JavaObject_lookupProperty, JavaObject_defineProperty, JavaObject_getPropertyById, /* getProperty */ diff --git a/js/src/liveconnect/jsj_private.h b/js/src/liveconnect/jsj_private.h index a1bab8f113f..7a17f52057c 100644 --- a/js/src/liveconnect/jsj_private.h +++ b/js/src/liveconnect/jsj_private.h @@ -643,13 +643,6 @@ jsj_EnterJava(JSContext *cx, JNIEnv **envp); extern void jsj_ExitJava(JSJavaThreadState *jsj_env); -extern JSObjectMap * -jsj_wrapper_newObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp, JSObject *obj); - -extern void -jsj_wrapper_destroyObjectMap(JSContext *cx, JSObjectMap *map); - extern jsval jsj_wrapper_getRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot); diff --git a/js/src/trace-test.js b/js/src/trace-test.js index 54f0ede2806..c36651a20eb 100644 --- a/js/src/trace-test.js +++ b/js/src/trace-test.js @@ -5200,6 +5200,19 @@ function testConstructorBail() { } test(testConstructorBail); +function testNewArrayCount() +{ + var a = []; + for (var i = 0; i < 5; i++) + a = [0]; + assertEq(a.__count__, 1); + for (var i = 0; i < 5; i++) + a = [0, , 2]; + assertEq(a.__count__, 2); +} +test(testNewArrayCount); + + /***************************************************************************** * * * _____ _ _ _____ ______ _____ _______ * diff --git a/js/src/xpconnect/src/xpcwrappednativejsops.cpp b/js/src/xpconnect/src/xpcwrappednativejsops.cpp index dd3aa0db47e..a680dbf565a 100644 --- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp +++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp @@ -1399,7 +1399,7 @@ XPC_WN_GetObjectOpsWithCall(JSContext *cx, JSClass *clazz) JSBool xpc_InitWrappedNativeJSOps() { - if(!XPC_WN_NoCall_JSOps.newObjectMap) + if(!XPC_WN_NoCall_JSOps.lookupProperty) { memcpy(&XPC_WN_NoCall_JSOps, &js_ObjectOps, sizeof(JSObjectOps)); XPC_WN_NoCall_JSOps.enumerate = XPC_WN_JSOp_Enumerate;