From 2e0c76a3ce240a66fb4d68aff49b60d6ad72c13a Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Mon, 20 Sep 2010 12:05:21 -0700 Subject: [PATCH] Backed out changeset 09ffb30caa47 --- js/src/jsapi-tests/testConservativeGC.cpp | 4 +- js/src/jsapi.cpp | 13 +- js/src/jsarray.cpp | 212 ++++++++-- js/src/jsarray.h | 9 + js/src/jsbuiltins.cpp | 7 +- js/src/jsdate.cpp | 4 +- js/src/jsdbgapi.cpp | 10 +- js/src/jsemit.cpp | 29 +- js/src/jsfun.cpp | 109 ++++-- js/src/jsfun.h | 13 +- js/src/jsgc.cpp | 93 +---- js/src/jsgc.h | 73 +--- js/src/jsinterp.cpp | 26 +- js/src/jsiter.cpp | 6 +- js/src/jslock.cpp | 3 +- js/src/jsobj.cpp | 250 ++++++------ js/src/jsobj.h | 255 +++++++----- js/src/jsobjinlines.h | 368 ++++++++---------- js/src/jsopcode.cpp | 2 +- js/src/jsopcode.tbl | 2 +- js/src/jsproxy.cpp | 27 +- js/src/jsproxy.h | 8 +- js/src/jsregexp.h | 8 +- js/src/jsscope.cpp | 16 - js/src/jsscopeinlines.h | 40 +- js/src/jstracer.cpp | 156 ++++---- js/src/jstracer.h | 15 +- js/src/jsvalue.h | 12 - js/src/methodjit/BaseAssembler.h | 9 +- js/src/methodjit/Compiler.cpp | 30 +- js/src/methodjit/FastOps.cpp | 38 +- js/src/methodjit/InvokeHelpers.cpp | 3 +- js/src/methodjit/MonoIC.cpp | 4 + js/src/methodjit/NunboxAssembler.h | 16 +- js/src/methodjit/PolyIC.cpp | 86 ++-- js/src/methodjit/PunboxAssembler.h | 16 +- js/src/methodjit/StubCalls.cpp | 16 +- js/src/methodjit/StubCalls.h | 4 +- .../tests/basic/testHoleInDenseArray.js | 3 +- .../jaeger/bug563000/eif-trap-typechange.js | 2 +- js/src/xpconnect/src/xpcprivate.h | 6 +- js/src/xpconnect/wrappers/XrayWrapper.cpp | 2 +- 42 files changed, 1022 insertions(+), 983 deletions(-) diff --git a/js/src/jsapi-tests/testConservativeGC.cpp b/js/src/jsapi-tests/testConservativeGC.cpp index 84ea7485e681..3cce8b3bed12 100644 --- a/js/src/jsapi-tests/testConservativeGC.cpp +++ b/js/src/jsapi-tests/testConservativeGC.cpp @@ -52,8 +52,8 @@ bool checkObjectFields(JSObject *savedCopy, JSObject *obj) * doing memcmp. */ savedCopy->objShape = obj->objShape; - savedCopy->slots = obj->slots; - CHECK(!memcmp(savedCopy, obj, JSOBJECT_SIZE)); + savedCopy->dslots = obj->dslots; + CHECK(!memcmp(savedCopy, obj, sizeof(*obj))); return true; } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index e95db2f4b569..fec4e58c8daf 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -3031,6 +3031,8 @@ JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep) /* Walk slots in obj and if any value is a non-null object, seal it. */ for (uint32 i = 0, n = obj->slotSpan(); i != n; ++i) { const Value &v = obj->getSlot(i); + if (i == JSSLOT_PRIVATE && (obj->getClass()->flags & JSCLASS_HAS_PRIVATE)) + continue; if (v.isPrimitive()) continue; if (!JS_SealObject(cx, &v.toObject(), deep)) @@ -3829,7 +3831,8 @@ JS_Enumerate(JSContext *cx, JSObject *obj) * + native case here uses a Shape *, but that iterates in reverse! * + so we make non-native match, by reverse-iterating after JS_Enumerating */ -const uint32 JSSLOT_ITER_INDEX = 0; +const uint32 JSSLOT_ITER_INDEX = JSSLOT_PRIVATE + 1; +JS_STATIC_ASSERT(JSSLOT_ITER_INDEX < JS_INITIAL_NSLOTS); static void prop_iter_finalize(JSContext *cx, JSObject *obj) @@ -3838,7 +3841,7 @@ prop_iter_finalize(JSContext *cx, JSObject *obj) if (!pdata) return; - if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() >= 0) { + if (obj->fslots[JSSLOT_ITER_INDEX].toInt32() >= 0) { /* Non-native case: destroy the ida enumerated when obj was created. */ JSIdArray *ida = (JSIdArray *) pdata; JS_DestroyIdArray(cx, ida); @@ -3852,7 +3855,7 @@ prop_iter_trace(JSTracer *trc, JSObject *obj) if (!pdata) return; - if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() < 0) { + if (obj->fslots[JSSLOT_ITER_INDEX].toInt32() < 0) { /* Native case: just mark the next property to visit. */ ((Shape *) pdata)->trace(trc); } else { @@ -3918,7 +3921,7 @@ JS_NewPropertyIterator(JSContext *cx, JSObject *obj) /* iterobj cannot escape to other threads here. */ iterobj->setPrivate(const_cast(pdata)); - iterobj->getSlotRef(JSSLOT_ITER_INDEX).setInt32(index); + iterobj->fslots[JSSLOT_ITER_INDEX].setInt32(index); return iterobj; } @@ -3932,7 +3935,7 @@ JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp) CHECK_REQUEST(cx); assertSameCompartment(cx, iterobj); - i = iterobj->getSlot(JSSLOT_ITER_INDEX).toInt32(); + i = iterobj->fslots[JSSLOT_ITER_INDEX].toInt32(); if (i < 0) { /* Native case: private data is a property tree node pointer. */ obj = iterobj->getParent(); diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 7b7d9002a56e..fce71f262d63 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -310,6 +310,109 @@ BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom, return JS_TRUE; } +bool +JSObject::growDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap) +{ + JS_ASSERT(isDenseArray()); + JS_ASSERT(newcap >= ARRAY_CAPACITY_MIN); + JS_ASSERT(newcap >= oldcap); + + if (newcap > MAX_DSLOTS_LENGTH32) { + if (!JS_ON_TRACE(cx)) + js_ReportAllocationOverflow(cx); + return JS_FALSE; + } + + /* dslots can be NULL during array creation. */ + Value *slots = dslots ? dslots - 1 : NULL; + Value *newslots = (Value *) cx->realloc(slots, (size_t(newcap) + 1) * sizeof(Value)); + if (!newslots) + return false; + + dslots = newslots + 1; + setDenseArrayCapacity(newcap); + + Value *base = addressOfDenseArrayElement(0); + for (Value *vp = base + oldcap, *end = base + newcap; vp < end; ++vp) + vp->setMagic(JS_ARRAY_HOLE); + + return true; +} + +bool +JSObject::ensureDenseArrayElements(JSContext *cx, uint32 newcap) +{ + /* + * When a dense array with CAPACITY_DOUBLING_MAX or fewer slots needs to + * grow, double its capacity, to push() N elements in amortized O(N) time. + * + * Above this limit, grow by 12.5% each time. Speed is still amortized + * O(N), with a higher constant factor, and we waste less space. + */ + static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024; + + /* + * Round up all large allocations to a multiple of this (1MB), so as not + * to waste space if malloc gives us 1MB-sized chunks (as jemalloc does). + */ + static const size_t CAPACITY_CHUNK = 1024 * 1024 / sizeof(Value); + + uint32 oldcap = getDenseArrayCapacity(); + + if (newcap > oldcap) { + /* + * If this overflows uint32, newcap is very large. nextsize will end + * up being less than newcap, the code below will thus disregard it, + * and resizeDenseArrayElements() will fail. + * + * The way we use dslots[-1] forces a few +1s and -1s here. For + * example, (oldcap * 2 + 1) produces the sequence 7, 15, 31, 63, ... + * which makes the total allocation size (with dslots[-1]) a power + * of two. + */ + uint32 nextsize = (oldcap <= CAPACITY_DOUBLING_MAX) + ? oldcap * 2 + 1 + : oldcap + (oldcap >> 3); + + uint32 actualCapacity = JS_MAX(newcap, nextsize); + if (actualCapacity >= CAPACITY_CHUNK) + actualCapacity = JS_ROUNDUP(actualCapacity + 1, CAPACITY_CHUNK) - 1; /* -1 for dslots[-1] */ + else if (actualCapacity < ARRAY_CAPACITY_MIN) + actualCapacity = ARRAY_CAPACITY_MIN; + + if (!growDenseArrayElements(cx, oldcap, actualCapacity)) + return false; + } + return true; +} + +bool +JSObject::shrinkDenseArrayElements(JSContext *cx, uint32 newcap) +{ + JS_ASSERT(isDenseArray()); + JS_ASSERT(newcap < getDenseArrayCapacity()); + JS_ASSERT(dslots); + + uint32 fill = newcap; + + if (newcap < ARRAY_CAPACITY_MIN) + newcap = ARRAY_CAPACITY_MIN; + + Value *newslots = (Value *) cx->realloc(dslots - 1, (size_t(newcap) + 1) * sizeof(Value)); + if (!newslots) + return false; + + dslots = newslots + 1; + setDenseArrayCapacity(newcap); + + /* we refuse to shrink below a minimum value, so we have to clear the excess space */ + Value *base = addressOfDenseArrayElement(0); + while (fill < newcap) + base[fill++].setMagic(JS_ARRAY_HOLE); + + return true; +} + static bool ReallyBigIndexToId(JSContext* cx, jsdouble index, jsid* idp) { @@ -426,8 +529,8 @@ js_EnsureDenseArrayCapacity(JSContext *cx, JSObject *obj, jsint i) return true; if (INDEX_TOO_SPARSE(obj, u)) return false; + return obj->ensureDenseArrayElements(cx, u + 1); - return obj->ensureDenseArrayElements(cx, u + 1); } JS_DEFINE_CALLINFO_3(extern, BOOL, js_EnsureDenseArrayCapacity, CONTEXT, OBJECT, INT32, 0, nanojit::ACCSET_STORE_ANY) @@ -556,13 +659,13 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool str if (obj->isDenseArray()) { /* * Don't reallocate if we're not actually shrinking our slots. If we do - * shrink slots here, ensureDenseArrayElements will fill all slots to the + * shrink slots here, resizeDenseArrayElements will fill all slots to the * right of newlen with JS_ARRAY_HOLE. This permits us to disregard * length when reading from arrays as long we are within the capacity. */ jsuint oldcap = obj->getDenseArrayCapacity(); - if (oldcap > newlen) - obj->shrinkDenseArrayElements(cx, newlen); + if (oldcap > newlen && !obj->shrinkDenseArrayElements(cx, newlen)) + return false; obj->setArrayLength(newlen); } else if (oldlen - newlen < (1 << 24)) { do { @@ -870,11 +973,20 @@ array_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool return JS_TRUE; } +static void +array_finalize(JSContext *cx, JSObject *obj) +{ + obj->freeDenseArrayElements(cx); +} + static void array_trace(JSTracer *trc, JSObject *obj) { JS_ASSERT(obj->isDenseArray()); + if (!obj->dslots) + return; + size_t holes = 0; uint32 capacity = obj->getDenseArrayCapacity(); for (uint32 i = 0; i < capacity; i++) { @@ -894,7 +1006,7 @@ array_trace(JSTracer *trc, JSObject *obj) Class js_ArrayClass = { "Array", Class::NON_NATIVE | - JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_RESERVED_SLOTS(JSObject::DENSE_ARRAY_CLASS_RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Array), PropertyStub, /* addProperty */ PropertyStub, /* delProperty */ @@ -903,7 +1015,7 @@ Class js_ArrayClass = { EnumerateStub, ResolveStub, js_TryValueOf, - NULL, + array_finalize, NULL, /* reserved0 */ NULL, /* checkAccess */ NULL, /* call */ @@ -987,18 +1099,26 @@ JSObject::makeDenseArraySlow(JSContext *cx) * the same initial shape. */ JSObject *arrayProto = getProto(); - if (!InitScopeForObject(cx, this, &js_SlowArrayClass, arrayProto, FINALIZE_OBJECT0)) + if (!InitScopeForObject(cx, this, &js_SlowArrayClass, arrayProto)) return false; - uint32 capacity = getDenseArrayCapacity(); + uint32 capacity; - /* - * Begin with the length property to share more of the property tree. - * The getter/setter here will directly access the object's private value. - */ + if (dslots) { + capacity = getDenseArrayCapacity(); + dslots[-1].setPrivateUint32(JS_INITIAL_NSLOTS + capacity); + } else { + /* + * Array.prototype is constructed as a dense array, but is immediately slowified before + * we have time to set capacity. + */ + capacity = 0; + } + + /* Begin with the length property to share more of the property tree. */ if (!addProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), array_length_getter, NULL, - SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0)) { + JSSLOT_ARRAY_LENGTH, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0)) { setMap(oldMap); return false; } @@ -1016,12 +1136,24 @@ JSObject::makeDenseArraySlow(JSContext *cx) continue; } - if (!addDataProperty(cx, id, i, JSPROP_ENUMERATE)) { + /* Assert that the length covering i fits in the alloted bits. */ + JS_ASSERT(JS_INITIAL_NSLOTS + i + 1 < NSLOTS_LIMIT); + + if (!addDataProperty(cx, id, JS_INITIAL_NSLOTS + i, JSPROP_ENUMERATE)) { setMap(oldMap); return false; } } + /* + * Render our formerly-reserved non-private properties GC-safe. We do not + * need to make the length slot GC-safe because it is the private slot + * (this is statically asserted within JSObject) where the implementation + * can store an arbitrary value. + */ + JS_ASSERT(js_SlowArrayClass.flags & JSCLASS_HAS_PRIVATE); + voidDenseOnlyArraySlots(); + /* * Finally, update class. If |this| is Array.prototype, then js_InitClass * will create an emptyShape whose class is &js_SlowArrayClass, to ensure @@ -1363,6 +1495,7 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector JS_ASSERT(obj->isDenseArray()); obj->setArrayLength(length); + obj->setDenseArrayCapacity(0); if (!vector || !length) return true; if (!obj->ensureDenseArrayElements(cx, length)) @@ -2321,8 +2454,10 @@ array_concat(JSContext *cx, uintN argc, Value *vp) /* * Clone aobj but pass the minimum of its length and capacity, to * handle a = [1,2,3]; a.length = 10000 "dense" cases efficiently. In - * the normal case where length is <= capacity, nobj and aobj will have - * the same capacity. + * such a case we'll pass 8 (not 3) due to ARRAY_CAPACITY_MIN, which + * will cause nobj to be over-allocated to 16. But in the normal case + * where length is <= capacity, nobj and aobj will have the same + * capacity. */ length = aobj->getArrayLength(); jsuint capacity = aobj->getDenseArrayCapacity(); @@ -2842,12 +2977,10 @@ static JSFunctionSpec array_static_methods[] = { JS_FS_END }; -/* The count here is a guess for the final capacity. */ static inline JSObject * -NewDenseArrayObject(JSContext *cx, jsuint count) +NewDenseArrayObject(JSContext *cx) { - JSFinalizeGCThingKind kind = GuessObjectGCKind(count); - return NewNonFunction(cx, &js_ArrayClass, NULL, NULL, kind); + return NewNonFunction(cx, &js_ArrayClass, NULL, NULL); } JSBool @@ -2856,6 +2989,12 @@ js_Array(JSContext *cx, uintN argc, Value *vp) jsuint length; const Value *vector; + /* Whether called with 'new' or not, use a new Array object. */ + JSObject *obj = NewDenseArrayObject(cx); + if (!obj) + return JS_FALSE; + vp->setObject(*obj); + if (argc == 0) { length = 0; vector = NULL; @@ -2872,12 +3011,6 @@ js_Array(JSContext *cx, uintN argc, Value *vp) vector = NULL; } - /* Whether called with 'new' or not, use a new Array object. */ - JSObject *obj = NewDenseArrayObject(cx, length); - if (!obj) - return JS_FALSE; - vp->setObject(*obj); - return InitArrayObject(cx, obj, length, vector); } @@ -2889,15 +3022,15 @@ js_NewEmptyArray(JSContext* cx, JSObject* proto, int32 len) JS_ASSERT(proto->isArray()); - JSFinalizeGCThingKind kind = js_GetGCObjectKind(len); - JSObject* obj = js_NewGCObject(cx, kind); + JSObject* obj = js_NewGCObject(cx); if (!obj) return NULL; - /* Initialize all fields of JSObject. */ - obj->init(cx, &js_ArrayClass, proto, proto->getParent(), - (void*) len, true); + /* Initialize all fields, calling init before setting obj->map. */ + obj->init(&js_ArrayClass, proto, proto->getParent(), NullValue(), cx); obj->setSharedNonNativeMap(); + obj->setArrayLength(len); + obj->setDenseArrayCapacity(0); return obj; } #ifdef JS_TRACER @@ -2911,7 +3044,7 @@ js_NewPreallocatedArray(JSContext* cx, JSObject* proto, int32 len) JSObject *obj = js_NewEmptyArray(cx, proto, len); if (!obj) return NULL; - if (!obj->ensureDenseArrayElements(cx, len)) + if (!obj->growDenseArrayElements(cx, 0, JS_MAX(len, ARRAY_CAPACITY_MIN))) return NULL; return obj; } @@ -2932,7 +3065,7 @@ js_InitArrayClass(JSContext *cx, JSObject *obj) * Assert that js_InitClass used the correct (slow array, not dense array) * class for proto's emptyShape class. */ - JS_ASSERT(proto->emptyShapes && proto->emptyShapes[0]->getClass() == proto->getClass()); + JS_ASSERT(proto->emptyShape->getClass() == proto->getClass()); proto->setArrayLength(0); return proto; @@ -2941,7 +3074,7 @@ js_InitArrayClass(JSContext *cx, JSObject *obj) JSObject * js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector) { - JSObject *obj = NewDenseArrayObject(cx, length); + JSObject *obj = NewDenseArrayObject(cx); if (!obj) return NULL; @@ -3062,7 +3195,7 @@ js_IsDensePrimitiveArray(JSObject *obj) jsuint capacity = obj->getDenseArrayCapacity(); for (jsuint i = 0; i < capacity; i++) { - if (obj->getDenseArrayElement(i).isObject()) + if (obj->dslots[i].isObject()) return JS_FALSE; } @@ -3086,9 +3219,10 @@ js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone) /* * Must use the minimum of original array's length and capacity, to handle - * |a = [1,2,3]; a.length = 10000| "dense" cases efficiently. In the normal - * case where length is <= capacity, the clone and original array will have - * the same capacity. + * |a = [1,2,3]; a.length = 10000| "dense" cases efficiently. In such a case + * we would use ARRAY_CAPACITY_MIN (not 3), which will cause the clone to be + * over-allocated. In the normal case where length is <= capacity the + * clone and original array will have the same capacity. */ jsuint jsvalCount = JS_MIN(obj->getDenseArrayCapacity(), length); @@ -3097,7 +3231,7 @@ js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone) return JS_FALSE; for (jsuint i = 0; i < jsvalCount; i++) { - const Value &val = obj->getDenseArrayElement(i); + const Value &val = obj->dslots[i]; if (val.isString()) { // Strings must be made immutable before being copied to a clone. diff --git a/js/src/jsarray.h b/js/src/jsarray.h index 442826cd2696..f4cd6cc17d1f 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -46,6 +46,8 @@ #include "jspubtd.h" #include "jsobj.h" +#define ARRAY_CAPACITY_MIN 7 + extern JSBool js_StringIsIndex(JSString *str, jsuint *indexp); @@ -137,6 +139,13 @@ js_InitArrayClass(JSContext *cx, JSObject *obj); extern bool js_InitContextBusyArrayTable(JSContext *cx); +/* + * Creates a new array with the given length and proto (NB: NULL is not + * translated to Array.prototype), with len slots preallocated. + */ +extern JSObject * JS_FASTCALL +js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len); + extern JSObject * js_NewArrayObject(JSContext *cx, jsuint length, const js::Value *vector); diff --git a/js/src/jsbuiltins.cpp b/js/src/jsbuiltins.cpp index 18b421615ab5..ca2e9afd0235 100644 --- a/js/src/jsbuiltins.cpp +++ b/js/src/jsbuiltins.cpp @@ -302,14 +302,11 @@ js_NewNullClosure(JSContext* cx, JSObject* funobj, JSObject* proto, JSObject* pa JSFunction *fun = (JSFunction*) funobj; JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun); - JSObject* closure = js_NewGCObject(cx, FINALIZE_OBJECT2); + JSObject* closure = js_NewGCObject(cx); if (!closure) return NULL; - if (!closure->initSharingEmptyShape(cx, &js_FunctionClass, proto, parent, - fun, FINALIZE_OBJECT2)) { - return NULL; - } + closure->initSharingEmptyShape(&js_FunctionClass, proto, parent, fun, cx); return closure; } JS_DEFINE_CALLINFO_4(extern, OBJECT, js_NewNullClosure, CONTEXT, OBJECT, OBJECT, OBJECT, diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 1c1bf840a162..5b30834e9692 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -2563,9 +2563,7 @@ JS_FRIEND_API(JSObject *) js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time) { JSObject *obj = NewBuiltinClassInstance(cx, &js_DateClass); - if (!obj || !obj->ensureSlots(cx, JSObject::DATE_CLASS_RESERVED_SLOTS)) - return NULL; - if (!SetUTCTime(cx, obj, msec_time)) + if (!obj || !SetUTCTime(cx, obj, msec_time)) return NULL; return obj; } diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index 6bdc56025388..e98043341339 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -1645,7 +1645,15 @@ JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure) JS_PUBLIC_API(size_t) JS_GetObjectTotalSize(JSContext *cx, JSObject *obj) { - return obj->slotsAndStructSize(); + size_t nbytes = (obj->isFunction() && obj->getPrivate() == obj) + ? sizeof(JSFunction) + : sizeof *obj; + + if (obj->dslots) { + nbytes += (obj->dslots[-1].toPrivateUint32() - JS_INITIAL_NSLOTS + 1) + * sizeof obj->dslots[0]; + } + return nbytes; } static size_t diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index bc4ea344ca7d..bc8f64b040c0 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -235,7 +235,7 @@ UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target) JS_ASSERT(nuses == 0); blockObj = cg->objectList.lastbox->object; JS_ASSERT(blockObj->isStaticBlock()); - JS_ASSERT(blockObj->getSlot(JSSLOT_BLOCK_DEPTH).isUndefined()); + JS_ASSERT(blockObj->fslots[JSSLOT_BLOCK_DEPTH].isUndefined()); OBJ_SET_BLOCK_DEPTH(cx, blockObj, cg->stackDepth); ndefs = OBJ_BLOCK_COUNT(cx, blockObj); @@ -1599,8 +1599,9 @@ js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, JSStmtInfo *stmt JS_ASSERT(shape->hasShortID()); if (slotp) { - JS_ASSERT(obj->getSlot(JSSLOT_BLOCK_DEPTH).isInt32()); - *slotp = obj->getSlot(JSSLOT_BLOCK_DEPTH).toInt32() + shape->shortid; + JS_ASSERT(obj->fslots[JSSLOT_BLOCK_DEPTH].isInt32()); + *slotp = obj->fslots[JSSLOT_BLOCK_DEPTH].toInt32() + + shape->shortid; } return stmt; } @@ -1867,12 +1868,11 @@ EmitEnterBlock(JSContext *cx, JSParseNode *pn, JSCodeGenerator *cg) } /* - * Clear blockObj->dslots and ensure a prompt safe crash if by accident - * some code tries to get a slot from a compiler-created Block prototype - * instead of from a clone. + * Shrink slots to free blockObj->dslots and ensure a prompt safe crash if + * by accident some code tries to get a slot from a compiler-created Block + * prototype instead of from a clone. */ - if (blockObj->hasSlotsArray()) - blockObj->removeSlotsArray(cx); + blockObj->shrinkSlots(cx, base); return true; } @@ -4353,17 +4353,8 @@ EmitFunctionDefNop(JSContext *cx, JSCodeGenerator *cg, uintN index) static bool EmitNewInit(JSContext *cx, JSCodeGenerator *cg, JSProtoKey key, JSParseNode *pn, int sharpnum) { - /* - * Watch for overflow on the initializer size. This isn't problematic because - * (a) we'll be reporting an error for the initializer shortly, and (b) - * the count is only used as a hint for the interpreter and JITs, and does not - * need to be correct. - */ - uint16 count = pn->pn_count; - if (count >= JS_BIT(16)) - count = JS_BIT(16) - 1; - - EMIT_UINT16PAIR_IMM_OP(JSOP_NEWINIT, (uint16) key, count); + if (js_Emit2(cx, cg, JSOP_NEWINIT, (jsbytecode) key) < 0) + return false; #if JS_HAS_SHARP_VARS if (cg->hasSharps()) { if (pn->pn_count != 0) diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index fe22e9ed7424..99e1669690fe 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -182,8 +182,7 @@ NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee) if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) return NULL; - JS_ASSERT(js_GetGCKindSlots(FINALIZE_OBJECT2) == JSObject::ARGS_CLASS_RESERVED_SLOTS); - JSObject *argsobj = js_NewGCObject(cx, FINALIZE_OBJECT2); + JSObject *argsobj = js_NewGCObject(cx); if (!argsobj) return NULL; @@ -194,10 +193,10 @@ NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee) SetValueRangeToUndefined(data->slots, argc); /* Can't fail from here on, so initialize everything in argsobj. */ - argsobj->init(cx, callee.getFunctionPrivate()->inStrictMode() + argsobj->init(callee.getFunctionPrivate()->inStrictMode() ? &StrictArgumentsClass : &js_ArgumentsClass, - proto, parent, NULL, false); + proto, parent, NULL, cx); argsobj->setMap(cx->runtime->emptyArgumentsShape); @@ -965,20 +964,15 @@ CalleeGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp) static JSObject * NewCallObject(JSContext *cx, JSFunction *fun, JSObject &scopeChain, JSObject &callee) { - size_t vars = fun->countArgsAndVars(); - size_t slots = JSObject::CALL_RESERVED_SLOTS + vars; - JSFinalizeGCThingKind kind = js_GetGCObjectKind(slots); - - JSObject *callobj = js_NewGCObject(cx, kind); + JSObject *callobj = js_NewGCObject(cx); if (!callobj) return NULL; - /* Init immediately to avoid GC seeing a half-init'ed object. */ - callobj->init(cx, &js_CallClass, NULL, &scopeChain, NULL, false); + callobj->init(&js_CallClass, NULL, &scopeChain, NULL, cx); callobj->setMap(fun->u.i.names); /* This must come after callobj->lastProp has been set. */ - if (!callobj->ensureInstanceReservedSlots(cx, vars)) + if (!callobj->ensureInstanceReservedSlots(cx, fun->countArgsAndVars())) return NULL; #ifdef DEBUG @@ -998,11 +992,11 @@ NewCallObject(JSContext *cx, JSFunction *fun, JSObject &scopeChain, JSObject &ca static inline JSObject * NewDeclEnvObject(JSContext *cx, JSStackFrame *fp) { - JSObject *envobj = js_NewGCObject(cx, FINALIZE_OBJECT2); + JSObject *envobj = js_NewGCObject(cx); if (!envobj) return NULL; - envobj->init(cx, &js_DeclEnvClass, NULL, &fp->scopeChain(), fp, false); + envobj->init(&js_DeclEnvClass, NULL, &fp->scopeChain(), fp, cx); envobj->setMap(cx->runtime->emptyDeclEnvShape); return envobj; } @@ -1077,9 +1071,34 @@ JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CreateCallObjectOnTrace, CONTEXT, FUNCTI inline static void CopyValuesToCallObject(JSObject &callobj, uintN nargs, Value *argv, uintN nvars, Value *slots) { - Value *base = callobj.getSlots() + JSObject::CALL_RESERVED_SLOTS; - memcpy(base, argv, nargs * sizeof(Value)); - memcpy(base + nargs, slots, nvars * sizeof(Value)); + /* Copy however many args fit into fslots. */ + uintN first = JSSLOT_PRIVATE + JSObject::CALL_RESERVED_SLOTS + 1; + JS_ASSERT(first <= JS_INITIAL_NSLOTS); + + Value *vp = &callobj.fslots[first]; + uintN len = Min(nargs, uintN(JS_INITIAL_NSLOTS) - first); + + memcpy(vp, argv, len * sizeof(Value)); + vp += len; + + nargs -= len; + if (nargs != 0) { + /* Copy any remaining args into dslots. */ + vp = callobj.dslots; + memcpy(vp, argv + len, nargs * sizeof(Value)); + vp += nargs; + } else { + /* Copy however many vars fit into any remaining fslots. */ + first += len; + len = JS_MIN(nvars, JS_INITIAL_NSLOTS - first); + memcpy(vp, slots, len * sizeof(Value)); + slots += len; + nvars -= len; + vp = callobj.dslots; + } + + /* Copy any remaining vars into dslots. */ + memcpy(vp, slots, nvars * sizeof(Value)); } void @@ -1098,23 +1117,28 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp) JS_ASSERT(fun == callobj.getCallObjCalleeFunction()); uintN n = fun->countArgsAndVars(); + /* + * Since for a call object all fixed slots happen to be taken, we can copy + * arguments and variables straight into JSObject.dslots. + */ + JS_STATIC_ASSERT(JS_INITIAL_NSLOTS - JSSLOT_PRIVATE == + 1 + JSObject::CALL_RESERVED_SLOTS); if (n != 0) { - JS_ASSERT(JSFunction::CLASS_RESERVED_SLOTS + n <= callobj.numSlots()); + JS_ASSERT(JSFunction::FIRST_FREE_SLOT + n <= callobj.numSlots()); uint32 nargs = fun->nargs; uint32 nvars = fun->u.i.nvars; - Value *base = callobj.getSlots() + JSObject::CALL_RESERVED_SLOTS; - #ifdef JS_METHODJIT + JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_PRIVATE + JSObject::CALL_RESERVED_SLOTS + 1); JSScript *script = fun->u.i.script; - memcpy(base, fp->formalArgs(), nargs * sizeof(Value)); + memcpy(callobj.dslots, fp->formalArgs(), nargs * sizeof(Value)); if (!script->jit || script->usesEval) { - memcpy(base + nargs, fp->slots(), nvars * sizeof(Value)); + memcpy(callobj.dslots + nargs, fp->slots(), nvars * sizeof(Value)); } else if (script->jit) { for (uint32 i = 0; i < script->jit->nescaping; i++) { uint32 e = script->jit->escaping[i]; - base[nargs + e] = fp->slots()[e]; + callobj.dslots[nargs + e] = fp->slots()[e]; } } #else @@ -1209,13 +1233,16 @@ CallPropertyOp(JSContext *cx, JSObject *obj, jsid id, Value *vp, } if (!fp) { - i += JSObject::CALL_RESERVED_SLOTS; if (kind == JSCPK_VAR) i += fun->nargs; else JS_ASSERT(kind == JSCPK_ARG); - array = obj->getSlots(); + const uintN first = JSSLOT_PRIVATE + JSObject::CALL_RESERVED_SLOTS + 1; + JS_ASSERT(first == JSSLOT_FREE(&js_CallClass)); + JS_ASSERT(first <= JS_INITIAL_NSLOTS); + + array = (i < JS_INITIAL_NSLOTS - first) ? obj->fslots : obj->dslots; } else if (kind == JSCPK_ARG) { array = fp->formalArgs(); } else { @@ -1357,11 +1384,14 @@ call_trace(JSTracer *trc, JSObject *obj) * cycles involving Call objects whose frames are active without this * hiding hack. */ - uintN first = JSObject::CALL_RESERVED_SLOTS; - uintN count = fp->fun()->countArgsAndVars(); + uintN first = JSSLOT_PRIVATE + JSObject::CALL_RESERVED_SLOTS + 1; + JS_ASSERT(first <= JS_INITIAL_NSLOTS); - JS_ASSERT(obj->numSlots() >= first + count); - SetValueRangeToUndefined(obj->getSlots() + first, count); + uintN count = fp->fun()->countArgsAndVars(); + uintN fixed = JS_MIN(count, JS_INITIAL_NSLOTS - first); + + SetValueRangeToUndefined(&obj->fslots[first], fixed); + SetValueRangeToUndefined(obj->dslots, count - fixed); } MaybeMarkGenerator(trc, obj); @@ -2375,8 +2405,8 @@ JSObject::initBoundFunction(JSContext *cx, const Value &thisArg, JS_ASSERT(isFunction()); flags |= JSObject::BOUND_FUNCTION; - getSlotRef(JSSLOT_BOUND_FUNCTION_THIS) = thisArg; - getSlotRef(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).setPrivateUint32(argslen); + fslots[JSSLOT_BOUND_FUNCTION_THIS] = thisArg; + fslots[JSSLOT_BOUND_FUNCTION_ARGS_COUNT].setPrivateUint32(argslen); if (argslen != 0) { /* FIXME? Burn memory on an empty scope whose shape covers the args slots. */ EmptyShape *empty = EmptyShape::create(cx, clasp); @@ -2389,8 +2419,9 @@ JSObject::initBoundFunction(JSContext *cx, const Value &thisArg, if (!ensureInstanceReservedSlots(cx, argslen)) return false; - JS_ASSERT(numSlots() >= argslen + FUN_CLASS_RESERVED_SLOTS); - memcpy(getSlots() + FUN_CLASS_RESERVED_SLOTS, args, argslen * sizeof(Value)); + JS_ASSERT(dslots); + JS_ASSERT(dslots[-1].toPrivateUint32() >= argslen); + memcpy(&dslots[0], args, argslen * sizeof(Value)); } return true; } @@ -2411,7 +2442,7 @@ JSObject::getBoundFunctionThis() const JS_ASSERT(isFunction()); JS_ASSERT(isBoundFunction()); - return getSlot(JSSLOT_BOUND_FUNCTION_THIS); + return fslots[JSSLOT_BOUND_FUNCTION_THIS]; } inline const js::Value * @@ -2420,10 +2451,10 @@ JSObject::getBoundFunctionArguments(uintN &argslen) const JS_ASSERT(isFunction()); JS_ASSERT(isBoundFunction()); - argslen = getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32(); - JS_ASSERT_IF(argslen > 0, numSlots() >= argslen); - - return getSlots() + FUN_CLASS_RESERVED_SLOTS; + argslen = fslots[JSSLOT_BOUND_FUNCTION_ARGS_COUNT].toPrivateUint32(); + JS_ASSERT_IF(argslen > 0, dslots); + JS_ASSERT_IF(argslen > 0, dslots[-1].toPrivateUint32() >= argslen); + return &dslots[0]; } namespace { @@ -3110,7 +3141,7 @@ JSFunction::addLocal(JSContext *cx, JSAtom *atom, JSLocalKind kind) uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED; uint16 *indexp; PropertyOp getter, setter; - uint32 slot = JSObject::CALL_RESERVED_SLOTS; + uint32 slot = JSSLOT_START(&js_CallClass) + JSObject::CALL_RESERVED_SLOTS; if (kind == JSLOCAL_ARG) { JS_ASSERT(u.i.nupvars == 0); diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 9149c130e00b..40ae52236467 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -134,8 +134,6 @@ enum JSLocalKind { struct JSFunction : public JSObject { - /* Functions always have two fixed slots (FUN_CLASS_RESERVED_SLOTS). */ - uint16 nargs; /* maximum number of specified arguments, reflected as f.length/f.arity */ uint16 flags; /* flags, see JSFUN_* below and in jsapi.h */ @@ -271,7 +269,7 @@ struct JSFunction : public JSObject public: void setJoinable() { JS_ASSERT(FUN_INTERPRETED(this)); - getSlotRef(METHOD_ATOM_SLOT).setNull(); + fslots[METHOD_ATOM_SLOT].setNull(); flags |= JSFUN_JOINABLE; } @@ -281,14 +279,14 @@ struct JSFunction : public JSObject * flattened upvars. */ JSAtom *methodAtom() const { - return (joinable() && getSlot(METHOD_ATOM_SLOT).isString()) - ? STRING_TO_ATOM(getSlot(METHOD_ATOM_SLOT).toString()) + return (joinable() && fslots[METHOD_ATOM_SLOT].isString()) + ? STRING_TO_ATOM(fslots[METHOD_ATOM_SLOT].toString()) : NULL; } void setMethodAtom(JSAtom *atom) { JS_ASSERT(joinable()); - getSlotRef(METHOD_ATOM_SLOT).setString(ATOM_TO_STRING(atom)); + fslots[METHOD_ATOM_SLOT].setString(ATOM_TO_STRING(atom)); } js::Native maybeNative() const { @@ -300,8 +298,9 @@ struct JSFunction : public JSObject return u.i.script; } - /* Number of extra fixed function object slots. */ + /* Number of extra fixed function object slots besides JSSLOT_PRIVATE. */ static const uint32 CLASS_RESERVED_SLOTS = JSObject::FUN_CLASS_RESERVED_SLOTS; + static const uint32 FIRST_FREE_SLOT = JSSLOT_PRIVATE + CLASS_RESERVED_SLOTS + 1; }; JS_STATIC_ASSERT(sizeof(JSFunction) % JS_GCTHING_ALIGN == 0); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index c9fac489b2cb..75152c88d730 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -235,7 +235,7 @@ const size_t GC_MARK_BITMAP_WORDS = GC_CELLS_PER_ARENA / JS_BITS_PER_WORD; JS_STATIC_ASSERT(sizeof(jsbitmap) == sizeof(jsuword)); JS_STATIC_ASSERT(sizeof(JSString) % GC_CELL_SIZE == 0); -JS_STATIC_ASSERT(JSOBJECT_SIZE % GC_CELL_SIZE == 0); +JS_STATIC_ASSERT(sizeof(JSObject) % GC_CELL_SIZE == 0); JS_STATIC_ASSERT(sizeof(JSFunction) % GC_CELL_SIZE == 0); #ifdef JSXML JS_STATIC_ASSERT(sizeof(JSXML) % GC_CELL_SIZE == 0); @@ -771,15 +771,9 @@ static inline size_t GetFinalizableThingSize(unsigned thingKind) { JS_STATIC_ASSERT(JS_EXTERNAL_STRING_LIMIT == 8); - JS_STATIC_ASSERT(FINALIZE_OBJECT_LAST - FINALIZE_OBJECT0 == 5); static const uint8 map[FINALIZE_LIMIT] = { - JSOBJECT_SIZE, /* FINALIZE_OBJECT0 */ - JSOBJECT_SIZE + 2 * sizeof(Value), /* FINALIZE_OBJECT2 */ - JSOBJECT_SIZE + 4 * sizeof(Value), /* FINALIZE_OBJECT4 */ - JSOBJECT_SIZE + 8 * sizeof(Value), /* FINALIZE_OBJECT8 */ - JSOBJECT_SIZE + 12 * sizeof(Value), /* FINALIZE_OBJECT12 */ - JSOBJECT_SIZE + 16 * sizeof(Value), /* FINALIZE_OBJECT16 */ + sizeof(JSObject), /* FINALIZE_OBJECT */ sizeof(JSFunction), /* FINALIZE_FUNCTION */ #if JS_HAS_XML_SUPPORT sizeof(JSXML), /* FINALIZE_XML */ @@ -806,12 +800,7 @@ GetFinalizableTraceKind(size_t thingKind) JS_STATIC_ASSERT(JS_EXTERNAL_STRING_LIMIT == 8); static const uint8 map[FINALIZE_LIMIT] = { - JSTRACE_OBJECT, /* FINALIZE_OBJECT0 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT2 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT4 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT8 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT12 */ - JSTRACE_OBJECT, /* FINALIZE_OBJECT16 */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT */ JSTRACE_OBJECT, /* FINALIZE_FUNCTION */ #if JS_HAS_XML_SUPPORT /* FINALIZE_XML */ JSTRACE_XML, @@ -1560,22 +1549,8 @@ CheckGCFreeListLink(JSGCThing *thing) JS_ASSERT_IF(thing->link, thing < thing->link); } -JSFinalizeGCThingKind js_GCObjectSlotsToThingKind[] = { - /* 0 */ FINALIZE_OBJECT0, FINALIZE_OBJECT2, FINALIZE_OBJECT2, FINALIZE_OBJECT4, - /* 4 */ FINALIZE_OBJECT4, FINALIZE_OBJECT8, FINALIZE_OBJECT8, FINALIZE_OBJECT8, - /* 8 */ FINALIZE_OBJECT8, FINALIZE_OBJECT12, FINALIZE_OBJECT12, FINALIZE_OBJECT12, - /* 12 */ FINALIZE_OBJECT12, FINALIZE_OBJECT16, FINALIZE_OBJECT16, FINALIZE_OBJECT16, - /* 16 */ FINALIZE_OBJECT16, FINALIZE_OBJECT0, FINALIZE_OBJECT0, FINALIZE_OBJECT0, - /* 20 */ FINALIZE_OBJECT0, FINALIZE_OBJECT0, FINALIZE_OBJECT0, FINALIZE_OBJECT0, - /* 24 */ FINALIZE_OBJECT0, FINALIZE_OBJECT0, FINALIZE_OBJECT0, FINALIZE_OBJECT0, - /* 28 */ FINALIZE_OBJECT0, FINALIZE_OBJECT0, FINALIZE_OBJECT0, FINALIZE_OBJECT0, - /* 32 */ FINALIZE_OBJECT0 -}; - -JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_GCObjectSlotsToThingKind) == SLOTS_TO_THING_KIND_LIMIT); - void * -js_NewFinalizableGCThing(JSContext *cx, JSFinalizeGCThingKind thingKind) +js_NewFinalizableGCThing(JSContext *cx, unsigned thingKind) { JS_ASSERT(thingKind < FINALIZE_LIMIT); #ifdef JS_THREADSAFE @@ -1613,13 +1588,6 @@ js_NewFinalizableGCThing(JSContext *cx, JSFinalizeGCThingKind thingKind) return thing; } -JSFinalizeGCThingKind -js_KindFromGCThing(const void *thing) -{ - JSGCArenaInfo *info = JSGCArenaInfo::fromGCThing((void*)thing); - return (JSFinalizeGCThingKind) info->list->thingKind; -} - JSBool js_LockGCThingRT(JSRuntime *rt, void *thing) { @@ -1676,14 +1644,8 @@ JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind) JS_CALL_OBJECT_TRACER(trc, proto, "proto"); if (JSObject *parent = obj->getParent()) JS_CALL_OBJECT_TRACER(trc, parent, "parent"); - - if (obj->emptyShapes) { - int count = FINALIZE_OBJECT_LAST - FINALIZE_OBJECT0 + 1; - for (int i = 0; i < count; i++) { - if (obj->emptyShapes[i]) - obj->emptyShapes[i]->trace(trc); - } - } + if (obj->emptyShape) + obj->emptyShape->trace(trc); /* Delegate to ops or the native marking op. */ JSTraceOp op = obj->getOps()->trace; @@ -2387,7 +2349,7 @@ js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data) inline void FinalizeObject(JSContext *cx, JSObject *obj, unsigned thingKind) { - JS_ASSERT((thingKind >= FINALIZE_OBJECT0 && thingKind <= FINALIZE_OBJECT_LAST) || + JS_ASSERT(thingKind == FINALIZE_OBJECT || thingKind == FINALIZE_FUNCTION); /* Cope with stillborn objects that have no map. */ @@ -2526,14 +2488,14 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str) } } -template static void FinalizeArenaList(JSContext *cx, unsigned thingKind) { - JS_STATIC_ASSERT(!(SIZE & GC_CELL_MASK)); + JS_STATIC_ASSERT(!(sizeof(T) & GC_CELL_MASK)); JSGCArenaList *arenaList = &cx->runtime->gcArenaList[thingKind]; - JS_ASSERT(SIZE == arenaList->thingSize); + JS_ASSERT(sizeof(T) == arenaList->thingSize); JSGCArena **ap = &arenaList->head; JSGCArena *a = *ap; @@ -2554,8 +2516,7 @@ FinalizeArenaList(JSContext *cx, unsigned thingKind) bool allClear = true; jsuword thing = a->toPageStart(); - jsuword thingsEnd = thing + GC_ARENA_SIZE - (GC_ARENA_SIZE % SIZE); - JS_ASSERT((thingsEnd - thing) % SIZE == 0); + jsuword thingsEnd = thing + GC_ARENA_SIZE / sizeof(T) * sizeof(T); jsuword nextFree = reinterpret_cast(ainfo->freeList); if (!nextFree) { @@ -2567,7 +2528,7 @@ FinalizeArenaList(JSContext *cx, unsigned thingKind) jsuword gcCellIndex = 0; jsbitmap *bitmap = a->getMarkBitmap(); - for (;; thing += SIZE, gcCellIndex += SIZE >> GC_CELL_SHIFT) { + for (;; thing += sizeof(T), gcCellIndex += sizeof(T) >> GC_CELL_SHIFT) { if (thing == nextFree) { if (thing == thingsEnd) break; @@ -2587,7 +2548,7 @@ FinalizeArenaList(JSContext *cx, unsigned thingKind) T *t = reinterpret_cast(thing); finalizer(cx, t, thingKind); #ifdef DEBUG - memset(t, JS_FREE_PATTERN, SIZE); + memset(t, JS_FREE_PATTERN, sizeof(T)); #endif } JSGCThing *t = reinterpret_cast(thing); @@ -2615,12 +2576,12 @@ FinalizeArenaList(JSContext *cx, unsigned thingKind) * Forget just assembled free list head for the arena and * add the arena itself to the destroy list. */ - JS_ASSERT(nfree == ThingsPerArena(SIZE)); + JS_ASSERT(nfree == ThingsPerArena(sizeof(T))); *ap = ainfo->prev; ReleaseGCArena(cx->runtime, a); METER(nkilledarenas++); } else { - JS_ASSERT(nfree < ThingsPerArena(SIZE)); + JS_ASSERT(nfree < ThingsPerArena(sizeof(T))); *tailp = NULL; ainfo->freeList = freeList; ap = &ainfo->prev; @@ -2923,21 +2884,10 @@ MarkAndSweep(JSContext *cx GCTIMER_PARAM) * object's finalizer can access them even if they will be freed. */ JS_ASSERT(!rt->gcEmptyArenaList); - FinalizeArenaList(cx, FINALIZE_OBJECT0); - FinalizeArenaList(cx, FINALIZE_OBJECT2); - FinalizeArenaList(cx, FINALIZE_OBJECT4); - FinalizeArenaList(cx, FINALIZE_OBJECT8); - FinalizeArenaList(cx, FINALIZE_OBJECT12); - FinalizeArenaList(cx, FINALIZE_OBJECT16); - FinalizeArenaList(cx, FINALIZE_FUNCTION); + FinalizeArenaList(cx, FINALIZE_OBJECT); + FinalizeArenaList(cx, FINALIZE_FUNCTION); #if JS_HAS_XML_SUPPORT - FinalizeArenaList(cx, FINALIZE_XML); + FinalizeArenaList(cx, FINALIZE_XML); #endif TIMESTAMP(sweepObjectEnd); @@ -2947,13 +2897,12 @@ MarkAndSweep(JSContext *cx GCTIMER_PARAM) */ rt->deflatedStringCache->sweep(cx); - FinalizeArenaList(cx, FINALIZE_SHORT_STRING); - FinalizeArenaList(cx, FINALIZE_STRING); + FinalizeArenaList(cx, FINALIZE_SHORT_STRING); + FinalizeArenaList(cx, FINALIZE_STRING); for (unsigned i = FINALIZE_EXTERNAL_STRING0; i <= FINALIZE_EXTERNAL_STRING_LAST; ++i) { - FinalizeArenaList(cx, i); + FinalizeArenaList(cx, i); } rt->gcNewArenaTriggerBytes = rt->gcBytes < GC_ARENA_ALLOCATION_TRIGGER ? diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 659e6dceecfd..e2bc0af2f6d4 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -236,13 +236,7 @@ js_WaitForGC(JSRuntime *rt); * ordinary string to simplify js_GetExternalStringGCType. */ enum JSFinalizeGCThingKind { - FINALIZE_OBJECT0, - FINALIZE_OBJECT2, - FINALIZE_OBJECT4, - FINALIZE_OBJECT8, - FINALIZE_OBJECT12, - FINALIZE_OBJECT16, - FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16, + FINALIZE_OBJECT, FINALIZE_FUNCTION, #if JS_HAS_XML_SUPPORT FINALIZE_XML, @@ -275,61 +269,12 @@ IsFinalizableStringKind(unsigned thingKind) * in the partially initialized thing. */ extern void * -js_NewFinalizableGCThing(JSContext *cx, JSFinalizeGCThingKind thingKind); - -/* Get the kind which was used when making a GC thing. */ -extern JSFinalizeGCThingKind -js_KindFromGCThing(const void *thing); - -/* Maximum number of fixed slots for an object. */ -const size_t JSOBJECT_FIXED_SLOTS_LIMIT = 16; - -/* Capacity for js_GCObjectSlotsToThingKind */ -const size_t SLOTS_TO_THING_KIND_LIMIT = 33; - -/* Get the best kind to use when making an object with the given slot count. */ -static inline JSFinalizeGCThingKind -js_GetGCObjectKind(size_t numSlots) -{ - extern JSFinalizeGCThingKind js_GCObjectSlotsToThingKind[]; - - if (numSlots >= SLOTS_TO_THING_KIND_LIMIT) - return FINALIZE_OBJECT0; - return js_GCObjectSlotsToThingKind[numSlots]; -} - -/* Get the number of fixed slots and initial capacity associated with a kind. */ -static inline size_t -js_GetGCKindSlots(JSFinalizeGCThingKind thingKind) -{ - /* Using a switch in hopes that thingKind will usually be a compile-time constant. */ - switch (thingKind) { - case FINALIZE_OBJECT0: - return 0; - case FINALIZE_OBJECT2: - return 2; - case FINALIZE_OBJECT4: - return 4; - case FINALIZE_OBJECT8: - return 8; - case FINALIZE_OBJECT12: - return 12; - case FINALIZE_OBJECT16: - return 16; - default: - JS_NOT_REACHED("Bad object finalize kind"); - return 0; - } -} +js_NewFinalizableGCThing(JSContext *cx, unsigned thingKind); static inline JSObject * -js_NewGCObject(JSContext *cx, JSFinalizeGCThingKind thingKind) +js_NewGCObject(JSContext *cx) { - JS_ASSERT(thingKind >= FINALIZE_OBJECT0 && thingKind <= FINALIZE_OBJECT_LAST); - JSObject *obj = (JSObject *) js_NewFinalizableGCThing(cx, thingKind); - if (obj) - obj->capacity = js_GetGCKindSlots(thingKind); - return obj; + return (JSObject *) js_NewFinalizableGCThing(cx, FINALIZE_OBJECT); } static inline JSString * @@ -351,7 +296,7 @@ js_NewGCExternalString(JSContext *cx, uintN type) { JS_ASSERT(type < JS_EXTERNAL_STRING_LIMIT); type += FINALIZE_EXTERNAL_STRING0; - return (JSString *) js_NewFinalizableGCThing(cx, JSFinalizeGCThingKind(type)); + return (JSString *) js_NewFinalizableGCThing(cx, type); } static inline JSFunction * @@ -359,13 +304,12 @@ js_NewGCFunction(JSContext *cx) { JSFunction* obj = (JSFunction *)js_NewFinalizableGCThing(cx, FINALIZE_FUNCTION); - if (obj) { - obj->capacity = JSObject::FUN_CLASS_RESERVED_SLOTS; #ifdef DEBUG + if (obj) { memset((uint8 *) obj + sizeof(JSObject), JS_FREE_PATTERN, sizeof(JSFunction) - sizeof(JSObject)); -#endif } +#endif return obj; } @@ -472,13 +416,10 @@ class GCHelperThread { void freeLater(void *ptr) { JS_ASSERT(!sweeping); - js_free(ptr); - /* if (freeCursor != freeCursorEnd) *freeCursor++ = ptr; else replenishAndFreeLater(ptr); - */ } }; diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index fce60660cee4..a17339227a86 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -330,8 +330,8 @@ ComputeThisFromArgv(JSContext *cx, Value *argv) #if JS_HAS_NO_SUCH_METHOD -const uint32 JSSLOT_FOUND_FUNCTION = 0; -const uint32 JSSLOT_SAVED_ID = 1; +const uint32 JSSLOT_FOUND_FUNCTION = JSSLOT_PRIVATE; +const uint32 JSSLOT_SAVED_ID = JSSLOT_PRIVATE + 1; Class js_NoSuchMethodClass = { "NoSuchMethod", @@ -382,7 +382,7 @@ js_OnUnknownMethod(JSContext *cx, Value *vp) vp[0] = IdToValue(id); } #endif - obj = js_NewGCObject(cx, FINALIZE_OBJECT2); + obj = js_NewGCObject(cx); if (!obj) return false; @@ -393,9 +393,8 @@ js_OnUnknownMethod(JSContext *cx, Value *vp) * NoSuchMethod helper objects own no manually allocated resources. */ obj->map = NULL; - obj->init(cx, &js_NoSuchMethodClass, NULL, NULL, NULL, false); - obj->setSlot(JSSLOT_FOUND_FUNCTION, tvr.value()); - obj->setSlot(JSSLOT_SAVED_ID, vp[0]); + obj->init(&js_NoSuchMethodClass, NULL, NULL, tvr.value(), cx); + obj->fslots[JSSLOT_SAVED_ID] = vp[0]; vp[0].setObject(*obj); } return true; @@ -413,9 +412,9 @@ NoSuchMethod(JSContext *cx, uintN argc, Value *vp, uint32 flags) JSObject *obj = &vp[0].toObject(); JS_ASSERT(obj->getClass() == &js_NoSuchMethodClass); - args.callee() = obj->getSlot(JSSLOT_FOUND_FUNCTION); + args.callee() = obj->fslots[JSSLOT_FOUND_FUNCTION]; args.thisv() = vp[1]; - args[0] = obj->getSlot(JSSLOT_SAVED_ID); + args[0] = obj->fslots[JSSLOT_SAVED_ID]; JSObject *argsobj = js_NewArrayObject(cx, argc, vp + 2); if (!argsobj) return JS_FALSE; @@ -5652,20 +5651,15 @@ END_CASE(JSOP_NEWARRAY) BEGIN_CASE(JSOP_NEWINIT) { - jsint i = GET_UINT16(regs.pc); - jsint count = GET_UINT16(regs.pc + UINT16_LEN); - + jsint i = GET_INT8(regs.pc); JS_ASSERT(i == JSProto_Array || i == JSProto_Object); JSObject *obj; - - JSFinalizeGCThingKind kind = GuessObjectGCKind(count); - if (i == JSProto_Array) { - obj = NewArrayWithKind(cx, kind); + obj = js_NewArrayObject(cx, 0, NULL); if (!obj) goto error; } else { - obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); + obj = NewBuiltinClassInstance(cx, &js_ObjectClass); if (!obj) goto error; } diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 0993eb3142be..49373b95b09c 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -288,7 +288,7 @@ EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uint if (pobj->getArrayLength() > 0) { size_t capacity = pobj->getDenseArrayCapacity(); - Value *vp = pobj->getDenseArrayElements(); + Value *vp = pobj->dslots; for (size_t i = 0; i < capacity; ++i, ++vp) { if (!vp->isMagic(JS_ARRAY_HOLE)) { /* Dense arrays never get so large that i would not fit into an integer id. */ @@ -455,10 +455,10 @@ NewIteratorObject(JSContext *cx, uintN flags) * helper objects) expect it to have a non-null map pointer, so we * share an empty Enumerator scope in the runtime. */ - JSObject *obj = js_NewGCObject(cx, FINALIZE_OBJECT0); + JSObject *obj = js_NewGCObject(cx); if (!obj) return false; - obj->init(cx, &js_IteratorClass, NULL, NULL, NULL, false); + obj->init(&js_IteratorClass, NULL, NULL, NullValue(), cx); obj->setMap(cx->runtime->emptyEnumeratorShape); return obj; } diff --git a/js/src/jslock.cpp b/js/src/jslock.cpp index c278614afb4b..25dd48a84205 100644 --- a/js/src/jslock.cpp +++ b/js/src/jslock.cpp @@ -507,7 +507,8 @@ FinishSharingTitle(JSContext *cx, JSTitle *title) JSObject *obj = TITLE_TO_OBJECT(title); if (obj) { uint32 nslots = obj->slotSpan(); - for (uint32 i = 0; i != nslots; ++i) { + JS_ASSERT(nslots >= JSSLOT_START(obj->getClass())); + for (uint32 i = JSSLOT_START(obj->getClass()); i != nslots; ++i) { Value v = obj->getSlot(i); if (v.isString() && !js_MakeStringImmutable(cx, v.toString())) { diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 02f0fa02372a..8ccfc972ee07 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2610,17 +2610,15 @@ js_NewInstance(JSContext *cx, JSObject *callee) static JS_ALWAYS_INLINE JSObject* NewObjectWithClassProto(JSContext *cx, Class *clasp, JSObject *proto, - /*JSFinalizeGCThingKind*/ unsigned _kind) + const Value &privateSlotValue) { JS_ASSERT(clasp->isNative()); - JSFinalizeGCThingKind kind = JSFinalizeGCThingKind(_kind); - JSObject* obj = js_NewGCObject(cx, kind); + JSObject* obj = js_NewGCObject(cx); if (!obj) return NULL; - if (!obj->initSharingEmptyShape(cx, clasp, proto, proto->getParent(), NULL, kind)) - return NULL; + obj->initSharingEmptyShape(clasp, proto, proto->getParent(), privateSlotValue, cx); return obj; } @@ -2628,7 +2626,8 @@ JSObject* FASTCALL js_Object_tn(JSContext* cx, JSObject* proto) { JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE)); - return NewObjectWithClassProto(cx, &js_ObjectClass, proto, FINALIZE_OBJECT8); + + return NewObjectWithClassProto(cx, &js_ObjectClass, proto, UndefinedValue()); } JS_DEFINE_TRCINFO_1(js_Object, @@ -2640,7 +2639,7 @@ js_NonEmptyObject(JSContext* cx, JSObject* proto) { JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE)); - JSObject *obj = NewObjectWithClassProto(cx, &js_ObjectClass, proto, FINALIZE_OBJECT8); + JSObject *obj = NewObjectWithClassProto(cx, &js_ObjectClass, proto, UndefinedValue()); return (obj && obj->ensureClassReservedSlotsForEmptyObject(cx)) ? obj : NULL; } @@ -2651,11 +2650,7 @@ JSObject* FASTCALL js_String_tn(JSContext* cx, JSObject* proto, JSString* str) { JS_ASSERT(JS_ON_TRACE(cx)); - JSObject *obj = NewObjectWithClassProto(cx, &js_StringClass, proto, FINALIZE_OBJECT2); - if (!obj) - return NULL; - obj->setPrimitiveThis(StringValue(str)); - return obj; + return NewObjectWithClassProto(cx, &js_StringClass, proto, StringValue(str)); } JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, STRING, 0, nanojit::ACCSET_STORE_ANY) @@ -2930,13 +2925,11 @@ js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) { JSObject *obj; - obj = js_NewGCObject(cx, FINALIZE_OBJECT2); + obj = js_NewGCObject(cx); if (!obj) return NULL; - JSStackFrame *priv = js_FloatingFrameIfGenerator(cx, cx->fp()); - - obj->init(cx, &js_WithClass, proto, parent, priv, false); + obj->init(&js_WithClass, proto, parent, js_FloatingFrameIfGenerator(cx, cx->fp()), cx); obj->setMap(cx->runtime->emptyWithShape); OBJ_SET_BLOCK_DEPTH(cx, obj, depth); @@ -2956,11 +2949,11 @@ js_NewBlockObject(JSContext *cx) * Null obj's proto slot so that Object.prototype.* does not pollute block * scopes and to give the block object its own scope. */ - JSObject *blockObj = js_NewGCObject(cx, FINALIZE_OBJECT2); + JSObject *blockObj = js_NewGCObject(cx); if (!blockObj) return NULL; - blockObj->init(cx, &js_BlockClass, NULL, NULL, NULL, false); + blockObj->init(&js_BlockClass, NULL, NULL, NullValue(), cx); blockObj->setMap(cx->runtime->emptyBlockShape); return blockObj; } @@ -2970,24 +2963,20 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp) { JS_ASSERT(proto->isStaticBlock()); - size_t count = OBJ_BLOCK_COUNT(cx, proto); - JSFinalizeGCThingKind kind = js_GetGCObjectKind(count + 1); - - JSObject *clone = js_NewGCObject(cx, kind); + JSObject *clone = js_NewGCObject(cx); if (!clone) return NULL; JSStackFrame *priv = js_FloatingFrameIfGenerator(cx, fp); /* The caller sets parent on its own. */ - clone->init(cx, &js_BlockClass, proto, NULL, priv, false); + clone->init(&js_BlockClass, proto, NULL, priv, cx); + clone->fslots[JSSLOT_BLOCK_DEPTH] = proto->fslots[JSSLOT_BLOCK_DEPTH]; clone->setMap(proto->map); - if (!clone->ensureInstanceReservedSlots(cx, count + 1)) + if (!clone->ensureInstanceReservedSlots(cx, OBJ_BLOCK_COUNT(cx, proto))) return NULL; - clone->setSlot(JSSLOT_BLOCK_DEPTH, proto->getSlot(JSSLOT_BLOCK_DEPTH)); - JS_ASSERT(clone->isClonedBlock()); return clone; } @@ -2995,6 +2984,9 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp) JS_REQUIRES_STACK JSBool js_PutBlockObject(JSContext *cx, JSBool normalUnwind) { + /* Blocks have one fixed slot available for the first local.*/ + JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2); + JSStackFrame *const fp = cx->fp(); JSObject *obj = &fp->scopeChain(); JS_ASSERT(obj->isClonedBlock()); @@ -3002,7 +2994,7 @@ js_PutBlockObject(JSContext *cx, JSBool normalUnwind) /* Block objects should have all reserved slots allocated early. */ uintN count = OBJ_BLOCK_COUNT(cx, obj); - JS_ASSERT(obj->numSlots() >= JSSLOT_BLOCK_DEPTH + 1 + count); + JS_ASSERT(obj->numSlots() == JSSLOT_BLOCK_DEPTH + 1 + count); /* The block and its locals must be on the current stack for GC safety. */ uintN depth = OBJ_BLOCK_DEPTH(cx, obj); @@ -3014,8 +3006,15 @@ js_PutBlockObject(JSContext *cx, JSBool normalUnwind) if (normalUnwind) { uintN slot = JSSLOT_BLOCK_DEPTH + 1; + uintN flen = JS_MIN(count, JS_INITIAL_NSLOTS - slot); + uintN stop = slot + flen; + depth += fp->numFixed(); - memcpy(obj->getSlots() + slot, fp->slots() + depth, count * sizeof(Value)); + while (slot < stop) + obj->fslots[slot++] = fp->slots()[depth++]; + count -= flen; + if (count != 0) + memcpy(obj->dslots, fp->slots() + depth, count * sizeof(Value)); } /* We must clear the private slot even with errors. */ @@ -3096,7 +3095,7 @@ GetObjectSize(JSObject *obj) { return (obj->isFunction() && !obj->getPrivate()) ? sizeof(JSFunction) - : JSOBJECT_SIZE + obj->numFixedSlots() * sizeof(Value); + : sizeof(JSObject); } /* @@ -3105,31 +3104,17 @@ GetObjectSize(JSObject *obj) * transitions are inherently not thread-safe. Don't perform a swap operation on objects * shared across threads or, or bad things will happen. You have been warned. */ -bool -JSObject::swap(JSContext *cx, JSObject *other) +void +JSObject::swap(JSObject *other) { size_t size = GetObjectSize(this); JS_ASSERT(size == GetObjectSize(other)); - bool thisInline = !hasSlotsArray(); - bool otherInline = !other->hasSlotsArray(); - - char tmp[tl::Max::result]; - JS_ASSERT(size <= sizeof(tmp)); - /* Trade the guts of the objects. */ + char tmp[tl::Max::result]; memcpy(tmp, this, size); memcpy(this, other, size); memcpy(other, tmp, size); - - /* Fixup pointers for inline slots on the objects. */ - if (thisInline) - other->slots = other->fixedSlots; - if (otherInline) - this->slots = this->fixedSlots; - - return true; } #if JS_HAS_XDR @@ -3315,12 +3300,13 @@ DefineStandardSlot(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom, const Shape *shape = obj->nativeLookup(id); if (!shape) { - uint32 slot = 2 * JSProto_LIMIT + key; - if (!js_SetReservedSlot(cx, obj, slot, v)) { + uint32 index = 2 * JSProto_LIMIT + key; + if (!js_SetReservedSlot(cx, obj, index, v)) { JS_UNLOCK_OBJ(cx, obj); return false; } + uint32 slot = JSSLOT_START(obj->getClass()) + index; shape = obj->addProperty(cx, id, PropertyStub, PropertyStub, slot, attrs, 0, 0); JS_UNLOCK_OBJ(cx, obj); @@ -3486,7 +3472,7 @@ js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, */ JS_ASSERT_IF(proto->clasp != clasp, clasp == &js_ArrayClass && proto->clasp == &js_SlowArrayClass); - if (!proto->getEmptyShape(cx, proto->clasp, FINALIZE_OBJECT0)) + if (!proto->getEmptyShape(cx, proto->clasp)) goto bad; /* If this is a standard class, cache its prototype. */ @@ -3504,103 +3490,111 @@ bad: } bool -JSObject::allocSlots(JSContext *cx, size_t newcap) +JSObject::allocSlots(JSContext *cx, size_t nslots) { - uint32 oldcap = numSlots(); + JS_ASSERT(!dslots); + JS_ASSERT(nslots > JS_INITIAL_NSLOTS); - JS_ASSERT(newcap >= oldcap && !hasSlotsArray()); - - if (newcap > NSLOTS_LIMIT) { - if (!JS_ON_TRACE(cx)) - js_ReportAllocationOverflow(cx); + size_t nwords = slotsToDynamicWords(nslots); + dslots = (Value*) cx->malloc(nwords * sizeof(Value)); + if (!dslots) return false; - } - Value *tmpslots = (Value*) cx->malloc(newcap * sizeof(Value)); - if (!tmpslots) - return false; /* Leave slots at inline buffer. */ - slots = tmpslots; - capacity = newcap; - - /* Copy over anything from the inline buffer. */ - memcpy(slots, fixedSlots, oldcap * sizeof(Value)); - ClearValueRange(slots + oldcap, newcap - oldcap, isDenseArray()); + dslots++; + dslots[-1].setPrivateUint32(nslots); + SetValueRangeToUndefined(dslots, nslots - JS_INITIAL_NSLOTS); return true; } bool -JSObject::growSlots(JSContext *cx, size_t newcap) +JSObject::growSlots(JSContext *cx, size_t nslots) { /* - * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to - * grow, double its capacity, to add N elements in amortized O(N) time. - * - * Above this limit, grow by 12.5% each time. Speed is still amortized - * O(N), with a higher constant factor, and we waste less space. + * Minimal number of dynamic slots to allocate. */ - static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024; - static const size_t CAPACITY_CHUNK = CAPACITY_DOUBLING_MAX / sizeof(Value); + const size_t MIN_DYNAMIC_WORDS = 4; - uint32 oldcap = numSlots(); - JS_ASSERT(oldcap < newcap); + /* + * The limit to switch to linear allocation strategy from the power of 2 + * growth no to waste too much memory. + */ + const size_t LINEAR_GROWTH_STEP = JS_BIT(16); - uint32 nextsize = (oldcap <= CAPACITY_DOUBLING_MAX) - ? oldcap * 2 - : oldcap + (oldcap >> 3); - - uint32 actualCapacity = JS_MAX(newcap, nextsize); - if (actualCapacity >= CAPACITY_CHUNK) - actualCapacity = JS_ROUNDUP(actualCapacity, CAPACITY_CHUNK); - else if (actualCapacity < SLOT_CAPACITY_MIN) - actualCapacity = SLOT_CAPACITY_MIN; + /* If we are allocating fslots, there is nothing to do. */ + if (nslots <= JS_INITIAL_NSLOTS) + return true; /* Don't let nslots get close to wrapping around uint32. */ - if (actualCapacity >= NSLOTS_LIMIT) { + if (nslots >= NSLOTS_LIMIT) { JS_ReportOutOfMemory(cx); return false; } - /* If nothing was allocated yet, treat it as initial allocation. */ - if (!hasSlotsArray()) - return allocSlots(cx, actualCapacity); + size_t nwords = slotsToDynamicWords(nslots); - Value *tmpslots = (Value*) cx->realloc(slots, actualCapacity * sizeof(Value)); - if (!tmpslots) - return false; /* Leave dslots as its old size. */ - slots = tmpslots; - capacity = actualCapacity; + /* + * Round up nslots so the number of bytes in dslots array is power + * of 2 to ensure exponential grouth. + */ + uintN log; + if (nwords <= MIN_DYNAMIC_WORDS) { + nwords = MIN_DYNAMIC_WORDS; + } else if (nwords < LINEAR_GROWTH_STEP) { + JS_CEILING_LOG2(log, nwords); + nwords = JS_BIT(log); + } else { + nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP); + } + nslots = dynamicWordsToSlots(nwords); + + /* + * If nothing was allocated yet, treat it as initial allocation (but with + * the exponential growth algorithm applied). + */ + if (!dslots) + return allocSlots(cx, nslots); + + size_t oldnslots = dslots[-1].toPrivateUint32(); + + Value *tmpdslots = (Value*) cx->realloc(dslots - 1, nwords * sizeof(Value)); + if (!tmpdslots) + return false; /* leave dslots at its old size */ + + dslots = tmpdslots; + dslots++; + dslots[-1].setPrivateUint32(nslots); /* Initialize the additional slots we added. */ - ClearValueRange(slots + oldcap, actualCapacity - oldcap, isDenseArray()); + JS_ASSERT(nslots > oldnslots); + Value *beg = dslots + (oldnslots - JS_INITIAL_NSLOTS); + Value *end = dslots + (nslots - JS_INITIAL_NSLOTS); + SetValueRangeToUndefined(beg, end); + return true; } void -JSObject::shrinkSlots(JSContext *cx, size_t newcap) +JSObject::shrinkSlots(JSContext *cx, size_t nslots) { - uint32 oldcap = numSlots(); - JS_ASSERT(newcap <= oldcap); - JS_ASSERT(newcap >= slotSpan()); - - if (oldcap <= SLOT_CAPACITY_MIN || !hasSlotsArray()) { - /* We won't shrink the slots any more. Clear excess holes. */ - ClearValueRange(slots + newcap, oldcap - newcap, isDenseArray()); + /* Nothing to shrink? */ + if (!dslots) return; - } - uint32 fill = newcap; - if (newcap < SLOT_CAPACITY_MIN) - newcap = SLOT_CAPACITY_MIN; + JS_ASSERT(dslots[-1].toPrivateUint32() > JS_INITIAL_NSLOTS); + JS_ASSERT(nslots <= dslots[-1].toPrivateUint32()); - Value *tmpslots = (Value*) cx->realloc(slots, newcap * sizeof(Value)); - if (!tmpslots) - return; /* Leave slots at its old size. */ - slots = tmpslots; - capacity = newcap; + if (nslots <= JS_INITIAL_NSLOTS) { + freeSlotsArray(cx); + dslots = NULL; + } else { + size_t nwords = slotsToDynamicWords(nslots); + Value *tmpdslots = (Value*) cx->realloc(dslots - 1, nwords * sizeof(Value)); + if (!tmpdslots) + return; /* leave dslots at its old size */ - if (fill < newcap) { - /* Clear any excess holes if we tried to shrink below SLOT_CAPACITY_MIN. */ - ClearValueRange(slots + fill, newcap - fill, isDenseArray()); + dslots = tmpdslots; + dslots++; + dslots[-1].setPrivateUint32(nslots); } } @@ -4676,12 +4670,10 @@ js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, u JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj)); slot = shape->slot; - if (slot != SHAPE_INVALID_SLOT) { + if (slot != SHAPE_INVALID_SLOT) *vp = pobj->lockedGetSlot(slot); - JS_ASSERT(!vp->isMagic()); - } else { + else vp->setUndefined(); - } if (shape->hasDefaultGetter()) return true; @@ -5919,6 +5911,7 @@ js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize) JSObject *obj = (JSObject *)trc->debugPrintArg; uint32 slot = (uint32)trc->debugPrintIndex; + JS_ASSERT(slot >= JSSLOT_START(obj->getClass())); const Shape *shape; if (obj->isNative()) { @@ -5933,8 +5926,9 @@ js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize) const char *slotname = NULL; Class *clasp = obj->getClass(); if (clasp->flags & JSCLASS_IS_GLOBAL) { + uint32 key = slot - JSSLOT_START(clasp); #define JS_PROTO(name,code,init) \ - if ((code) == slot) { slotname = js_##name##_str; goto found; } + if ((code) == key) { slotname = js_##name##_str; goto found; } #include "jsproto.tbl" #undef JS_PROTO } @@ -5962,7 +5956,7 @@ js_TraceObject(JSTracer *trc, JSObject *obj) JS_ASSERT(obj->isNative()); JSContext *cx = trc->context; - if (obj->hasSlotsArray() && !obj->nativeEmpty() && IS_GC_MARKING_TRACER(trc)) { + if (!obj->nativeEmpty() && IS_GC_MARKING_TRACER(trc)) { /* * Trim overlong dslots allocations from the GC, to avoid thrashing in * case of delete-happy code that settles down at a given population. @@ -6009,8 +6003,9 @@ js_TraceObject(JSTracer *trc, JSObject *obj) uint32 nslots = obj->numSlots(); if (!obj->nativeEmpty() && obj->slotSpan() < nslots) nslots = obj->slotSpan(); + JS_ASSERT(nslots >= JSSLOT_START(clasp)); - for (uint32 i = 0; i != nslots; ++i) { + for (uint32 i = JSSLOT_START(clasp); i != nslots; ++i) { const Value &v = obj->getSlot(i); JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i); MarkValueRaw(trc, v); @@ -6039,13 +6034,14 @@ js_ClearNative(JSContext *cx, JSObject *obj) } bool -js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, Value *vp) +js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, Value *vp) { if (!obj->isNative()) { vp->setUndefined(); return true; } + uint32 slot = JSSLOT_START(obj->getClass()) + index; JS_LOCK_OBJ(cx, obj); if (slot < obj->numSlots()) *vp = obj->getSlot(slot); @@ -6056,12 +6052,13 @@ js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, Value *vp) } bool -js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, const Value &v) +js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, const Value &v) { if (!obj->isNative()) return true; Class *clasp = obj->getClass(); + uint32 slot = JSSLOT_START(clasp) + index; JS_LOCK_OBJ(cx, obj); if (slot >= obj->numSlots()) { @@ -6371,8 +6368,11 @@ js_DumpObject(JSObject *obj) dumpValue(ObjectOrNullValue(obj->getParent())); fputc('\n', stderr); - if (clasp->flags & JSCLASS_HAS_PRIVATE) + i = JSSLOT_PRIVATE; + if (clasp->flags & JSCLASS_HAS_PRIVATE) { + i = JSSLOT_PRIVATE + 1; fprintf(stderr, "private %p\n", obj->getPrivate()); + } fprintf(stderr, "slots:\n"); reservedEnd = i + JSCLASS_RESERVED_SLOTS(clasp); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 9b45883ddb6c..d2d6beb7bdbc 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -235,6 +235,17 @@ js_TypeOf(JSContext *cx, JSObject *obj); struct NativeIterator; +const uint32 JS_INITIAL_NSLOTS = 3; + +/* + * The first available slot to store generic value. For JSCLASS_HAS_PRIVATE + * classes the slot stores a pointer to private data stuffed in a Value. + * Such pointer is stored as is without an overhead of PRIVATE_TO_JSVAL + * tagging and should be accessed using the (get|set)Private methods of + * JSObject. + */ +const uint32 JSSLOT_PRIVATE = 0; + struct JSFunction; /* @@ -263,9 +274,9 @@ struct JSFunction; * Both these flag bits are initially zero; they may be set or queried using * the (is|set)(Delegate|System) inline methods. * - * The dslots member is a pointer to the slot vector for the object, - * either an inline array starting at data or a dynamically allocated array. - * In both cases, capacity gives the number of available slots. + * The dslots member is null or a pointer into a dynamically allocated vector + * of Values for reserved and dynamic slots. If dslots is not null, dslots[-1] + * records the number of available slots. */ struct JSObject { /* @@ -333,22 +344,18 @@ struct JSObject { uint32 flags; /* flags */ uint32 objShape; /* copy of lastProp->shape, or override if different */ + JSObject *proto; /* object's prototype */ + JSObject *parent; /* object's parent */ + js::Value *dslots; /* dynamically allocated slots */ + + /* Empty shape of kids if prototype, located here to align fslots on 32 bit targets. */ + js::EmptyShape *emptyShape; + + js::Value fslots[JS_INITIAL_NSLOTS]; /* small number of fixed slots */ #ifdef JS_THREADSAFE JSTitle title; #endif - /* If prototype, lazily filled array of empty shapes for each object size. */ - js::EmptyShape **emptyShapes; - - JSObject *proto; /* object's prototype */ - JSObject *parent; /* object's parent */ - void *privateData; /* private data */ - jsuword capacity; /* capacity of slots */ - js::Value *slots; /* dynamically allocated slots, - or pointer to fixedSlots */ - js::Value fixedSlots[2]; /* inline slots for object. count is not fixed, - may be less or greater than two */ - /* * Return an immutable, shareable, empty shape with the same clasp as this * and the same slotSpan as this had when empty. @@ -357,8 +364,7 @@ struct JSObject { * used as the scope of a new object whose prototype is |proto|. */ inline bool canProvideEmptyShape(js::Class *clasp); - inline js::EmptyShape *getEmptyShape(JSContext *cx, js::Class *aclasp, - /* JSFinalizeGCThingKind */ unsigned kind); + inline js::EmptyShape *getEmptyShape(JSContext *cx, js::Class *aclasp); bool isNative() const { return map->isNative(); } @@ -524,27 +530,29 @@ struct JSObject { inline bool hasPropertyTable() const; - uint32 numSlots() const { return capacity; } + uint32 numSlots(void) const { + return dslots ? dslots[-1].toPrivateUint32() : uint32(JS_INITIAL_NSLOTS); + } size_t slotsAndStructSize(uint32 nslots) const; size_t slotsAndStructSize() const { return slotsAndStructSize(numSlots()); } - inline size_t numFixedSlots() const; + private: + static size_t slotsToDynamicWords(size_t nslots) { + JS_ASSERT(nslots > JS_INITIAL_NSLOTS); + return nslots + 1 - JS_INITIAL_NSLOTS; + } + + static size_t dynamicWordsToSlots(size_t nwords) { + JS_ASSERT(nwords > 1); + return nwords - 1 + JS_INITIAL_NSLOTS; + } public: - /* Minimum size for dynamically allocated slots. */ - static const uint32 SLOT_CAPACITY_MIN = 8; - bool allocSlots(JSContext *cx, size_t nslots); bool growSlots(JSContext *cx, size_t nslots); void shrinkSlots(JSContext *cx, size_t nslots); - bool ensureSlots(JSContext *cx, size_t nslots) { - if (numSlots() < nslots) - return growSlots(cx, nslots); - return true; - } - /* * Ensure that the object has at least JSCLASS_RESERVED_SLOTS(clasp) + * nreserved slots. @@ -563,14 +571,6 @@ struct JSObject { */ bool ensureInstanceReservedSlots(JSContext *cx, size_t nreserved); - /* - * Get a direct pointer to the object's slots. - * This can be reallocated if the object is modified, watch out! - */ - js::Value *getSlots() const { - return slots; - } - /* * NB: ensureClassReservedSlotsForEmptyObject asserts that nativeEmpty() * Use ensureClassReservedSlots for any object, either empty or already @@ -585,18 +585,26 @@ struct JSObject { bool containsSlot(uint32 slot) const { return slot < slotSpan(); } js::Value& getSlotRef(uintN slot) { - JS_ASSERT(slot < capacity); - return slots[slot]; + return (slot < JS_INITIAL_NSLOTS) + ? fslots[slot] + : (JS_ASSERT(slot < dslots[-1].toPrivateUint32()), + dslots[slot - JS_INITIAL_NSLOTS]); } const js::Value &getSlot(uintN slot) const { - JS_ASSERT(slot < capacity); - return slots[slot]; + return (slot < JS_INITIAL_NSLOTS) + ? fslots[slot] + : (JS_ASSERT(slot < dslots[-1].toPrivateUint32()), + dslots[slot - JS_INITIAL_NSLOTS]); } void setSlot(uintN slot, const js::Value &value) { - JS_ASSERT(slot < capacity); - slots[slot] = value; + if (slot < JS_INITIAL_NSLOTS) { + fslots[slot] = value; + } else { + JS_ASSERT(slot < dslots[-1].toPrivateUint32()); + dslots[slot - JS_INITIAL_NSLOTS] = value; + } } inline const js::Value &lockedGetSlot(uintN slot) const; @@ -605,7 +613,8 @@ struct JSObject { /* * These ones are for multi-threaded ("MT") objects. Use getSlot(), * getSlotRef(), setSlot() to directly manipulate slots in obj when only - * one thread can access obj. + * one thread can access obj, or when accessing read-only slots within + * JS_INITIAL_NSLOTS. */ inline js::Value getSlotMT(JSContext *cx, uintN slot); inline void setSlotMT(JSContext *cx, uintN slot, const js::Value &value); @@ -656,12 +665,12 @@ struct JSObject { void *getPrivate() const { JS_ASSERT(getClass()->flags & JSCLASS_HAS_PRIVATE); - return privateData; + return *(void **)&fslots[JSSLOT_PRIVATE]; } void setPrivate(void *data) { JS_ASSERT(getClass()->flags & JSCLASS_HAS_PRIVATE); - privateData = data; + *(void **)&fslots[JSSLOT_PRIVATE] = data; } /* @@ -669,7 +678,7 @@ struct JSObject { */ private: - static const uint32 JSSLOT_PRIMITIVE_THIS = 0; + static const uint32 JSSLOT_PRIMITIVE_THIS = JSSLOT_PRIVATE; public: inline const js::Value &getPrimitiveThis() const; @@ -679,16 +688,36 @@ struct JSObject { * Array-specific getters and setters (for both dense and slow arrays). */ + // Used by dense and slow arrays. + static const uint32 JSSLOT_ARRAY_LENGTH = JSSLOT_PRIVATE; + + static const uint32 JSSLOT_DENSE_ARRAY_CAPACITY = JSSLOT_PRIVATE + 1; + + // This assertion must remain true; see comment in js_MakeArraySlow(). + // (Nb: This method is never called, it just contains a static assertion. + // The static assertion isn't inline because that doesn't work on Mac.) + inline void staticAssertArrayLengthIsInPrivateSlot(); + + public: + static const uint32 DENSE_ARRAY_CLASS_RESERVED_SLOTS = 3; + inline uint32 getArrayLength() const; inline void setArrayLength(uint32 length); - inline uint32 getDenseArrayCapacity(); - inline js::Value* getDenseArrayElements(); - inline const js::Value &getDenseArrayElement(uintN idx); - inline js::Value* addressOfDenseArrayElement(uintN idx); - inline void setDenseArrayElement(uintN idx, const js::Value &val); - inline bool ensureDenseArrayElements(JSContext *cx, uintN cap); - inline void shrinkDenseArrayElements(JSContext *cx, uintN cap); + inline uint32 getDenseArrayCapacity() const; + inline void setDenseArrayCapacity(uint32 capacity); + + inline const js::Value &getDenseArrayElement(uint32 i) const; + inline js::Value *addressOfDenseArrayElement(uint32 i); + inline void setDenseArrayElement(uint32 i, const js::Value &v); + + inline js::Value *getDenseArrayElements() const; // returns pointer to the Array's elements array + bool growDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap); + bool ensureDenseArrayElements(JSContext *cx, uint32 newcap); + bool shrinkDenseArrayElements(JSContext *cx, uint32 newcap); + inline void freeDenseArrayElements(JSContext *cx); + + inline void voidDenseOnlyArraySlots(); // used when converting a dense array to a slow array JSBool makeDenseArraySlow(JSContext *cx); @@ -700,7 +729,7 @@ struct JSObject { /* * Reserved slot structure for Arguments objects: * - * private - the function's stack frame until the function + * JSSLOT_PRIVATE - the function's stack frame until the function * returns; also, JS_ARGUMENTS_OBJECT_ON_TRACE if * arguments was created on trace * JSSLOT_ARGS_LENGTH - the number of actual arguments and a flag @@ -718,13 +747,13 @@ struct JSObject { * Argument index i is stored in ArgumentsData.slots[i], accessible via * {get,set}ArgsElement(). */ - static const uint32 JSSLOT_ARGS_DATA = 1; + static const uint32 JSSLOT_ARGS_DATA = JSSLOT_PRIVATE + 2; public: /* Number of extra fixed arguments object slots besides JSSLOT_PRIVATE. */ - static const uint32 JSSLOT_ARGS_LENGTH = 0; + static const uint32 JSSLOT_ARGS_LENGTH = JSSLOT_PRIVATE + 1; static const uint32 ARGS_CLASS_RESERVED_SLOTS = 2; - static const uint32 ARGS_FIRST_FREE_SLOT = ARGS_CLASS_RESERVED_SLOTS + 1; + static const uint32 ARGS_FIRST_FREE_SLOT = JSSLOT_PRIVATE + ARGS_CLASS_RESERVED_SLOTS + 1; /* Lower-order bit stolen from the length slot. */ static const uint32 ARGS_LENGTH_OVERRIDDEN_BIT = 0x1; @@ -751,18 +780,19 @@ struct JSObject { inline void setArgsCallee(const js::Value &callee); inline const js::Value &getArgsElement(uint32 i) const; - inline js::Value *addressOfArgsElement(uint32 i); + inline js::Value *addressOfArgsElement(uint32 i) const; inline void setArgsElement(uint32 i, const js::Value &v); private: /* * Reserved slot structure for Arguments objects: + * */ - static const uint32 JSSLOT_CALL_CALLEE = 0; - static const uint32 JSSLOT_CALL_ARGUMENTS = 1; + static const uint32 JSSLOT_CALL_CALLEE = JSSLOT_PRIVATE + 1; + static const uint32 JSSLOT_CALL_ARGUMENTS = JSSLOT_PRIVATE + 2; public: - /* Number of reserved slots. */ + /* Number of extra fixed slots besides JSSLOT_PRIVATE. */ static const uint32 CALL_RESERVED_SLOTS = 2; inline JSObject &getCallObjCallee() const; @@ -776,23 +806,23 @@ struct JSObject { * Date-specific getters and setters. */ - static const uint32 JSSLOT_DATE_UTC_TIME = 0; + static const uint32 JSSLOT_DATE_UTC_TIME = JSSLOT_PRIVATE; /* * Cached slots holding local properties of the date. * These are undefined until the first actual lookup occurs * and are reset to undefined whenever the date's time is modified. */ - static const uint32 JSSLOT_DATE_COMPONENTS_START = 1; + static const uint32 JSSLOT_DATE_COMPONENTS_START = JSSLOT_PRIVATE + 1; - static const uint32 JSSLOT_DATE_LOCAL_TIME = 1; - static const uint32 JSSLOT_DATE_LOCAL_YEAR = 2; - static const uint32 JSSLOT_DATE_LOCAL_MONTH = 3; - static const uint32 JSSLOT_DATE_LOCAL_DATE = 4; - static const uint32 JSSLOT_DATE_LOCAL_DAY = 5; - static const uint32 JSSLOT_DATE_LOCAL_HOURS = 6; - static const uint32 JSSLOT_DATE_LOCAL_MINUTES = 7; - static const uint32 JSSLOT_DATE_LOCAL_SECONDS = 8; + static const uint32 JSSLOT_DATE_LOCAL_TIME = JSSLOT_PRIVATE + 1; + static const uint32 JSSLOT_DATE_LOCAL_YEAR = JSSLOT_PRIVATE + 2; + static const uint32 JSSLOT_DATE_LOCAL_MONTH = JSSLOT_PRIVATE + 3; + static const uint32 JSSLOT_DATE_LOCAL_DATE = JSSLOT_PRIVATE + 4; + static const uint32 JSSLOT_DATE_LOCAL_DAY = JSSLOT_PRIVATE + 5; + static const uint32 JSSLOT_DATE_LOCAL_HOURS = JSSLOT_PRIVATE + 6; + static const uint32 JSSLOT_DATE_LOCAL_MINUTES = JSSLOT_PRIVATE + 7; + static const uint32 JSSLOT_DATE_LOCAL_SECONDS = JSSLOT_PRIVATE + 8; static const uint32 DATE_CLASS_RESERVED_SLOTS = 9; @@ -811,18 +841,17 @@ struct JSObject { * Flat closures with one or more upvars snapshot the upvars' values into a * vector of js::Values referenced from this slot. */ - static const uint32 JSSLOT_FLAT_CLOSURE_UPVARS = 0; + static const uint32 JSSLOT_FLAT_CLOSURE_UPVARS = JSSLOT_PRIVATE + 1; /* * Null closures set or initialized as methods have these slots. See the * "method barrier" comments and methods. */ + static const uint32 JSSLOT_FUN_METHOD_ATOM = JSSLOT_PRIVATE + 1; + static const uint32 JSSLOT_FUN_METHOD_OBJ = JSSLOT_PRIVATE + 2; - static const uint32 JSSLOT_FUN_METHOD_ATOM = 0; - static const uint32 JSSLOT_FUN_METHOD_OBJ = 1; - - static const uint32 JSSLOT_BOUND_FUNCTION_THIS = 0; - static const uint32 JSSLOT_BOUND_FUNCTION_ARGS_COUNT = 1; + static const uint32 JSSLOT_BOUND_FUNCTION_THIS = JSSLOT_PRIVATE + 1; + static const uint32 JSSLOT_BOUND_FUNCTION_ARGS_COUNT = JSSLOT_PRIVATE + 2; public: static const uint32 FUN_CLASS_RESERVED_SLOTS = 2; @@ -848,7 +877,7 @@ struct JSObject { */ private: - static const uint32 JSSLOT_REGEXP_LAST_INDEX = 0; + static const uint32 JSSLOT_REGEXP_LAST_INDEX = JSSLOT_PRIVATE + 1; public: static const uint32 REGEXP_CLASS_RESERVED_SLOTS = 1; @@ -877,12 +906,12 @@ struct JSObject { * - Others (js_XMLClass, js_XMLFilterClass) don't reserve any slots. */ private: - static const uint32 JSSLOT_NAME_PREFIX = 0; // shared - static const uint32 JSSLOT_NAME_URI = 1; // shared + static const uint32 JSSLOT_NAME_PREFIX = JSSLOT_PRIVATE; // shared + static const uint32 JSSLOT_NAME_URI = JSSLOT_PRIVATE + 1; // shared - static const uint32 JSSLOT_NAMESPACE_DECLARED = 2; + static const uint32 JSSLOT_NAMESPACE_DECLARED = JSSLOT_PRIVATE + 2; - static const uint32 JSSLOT_QNAME_LOCAL_NAME = 2; + static const uint32 JSSLOT_QNAME_LOCAL_NAME = JSSLOT_PRIVATE + 2; public: static const uint32 NAMESPACE_CLASS_RESERVED_SLOTS = 3; @@ -920,8 +949,14 @@ struct JSObject { inline bool isCallable(); /* The map field is not initialized here and should be set separately. */ - void init(JSContext *cx, js::Class *aclasp, JSObject *proto, JSObject *parent, - void *priv, bool useHoles); + inline void initCommon(js::Class *aclasp, JSObject *proto, JSObject *parent, + JSContext *cx); + inline void init(js::Class *aclasp, JSObject *proto, JSObject *parent, + JSContext *cx); + inline void init(js::Class *aclasp, JSObject *proto, JSObject *parent, + void *priv, JSContext *cx); + inline void init(js::Class *aclasp, JSObject *proto, JSObject *parent, + const js::Value &privateSlotValue, JSContext *cx); inline void finish(JSContext *cx); @@ -929,24 +964,22 @@ struct JSObject { * Like init, but also initializes map. The catch: proto must be the result * of a call to js_InitClass(...clasp, ...). */ - inline bool initSharingEmptyShape(JSContext *cx, - js::Class *clasp, + inline void initSharingEmptyShape(js::Class *clasp, + JSObject *proto, + JSObject *parent, + const js::Value &privateSlotValue, + JSContext *cx); + inline void initSharingEmptyShape(js::Class *clasp, JSObject *proto, JSObject *parent, void *priv, - /* JSFinalizeGCThingKind */ unsigned kind); + JSContext *cx); - inline bool hasSlotsArray() const { return (slots != &fixedSlots[0]); } + inline bool hasSlotsArray() const { return !!dslots; } /* This method can only be called when hasSlotsArray() returns true. */ inline void freeSlotsArray(JSContext *cx); - /* - * Frees slots array and cleans up object so it may continue to be used, - * although slotSpan may exceed numSlots. - */ - inline void removeSlotsArray(JSContext *cx); - inline bool hasProperty(JSContext *cx, jsid id, bool *foundp, uintN flags = 0); bool allocSlot(JSContext *cx, uint32 *slotp); @@ -1058,10 +1091,10 @@ struct JSObject { inline JSObject *getThrowTypeError() const; - bool swap(JSContext *cx, JSObject *obj); - const js::Shape *defineBlockVariable(JSContext *cx, jsid id, intN index); + void swap(JSObject *obj); + inline bool canHaveMethodBarrier() const; inline bool isArguments() const; @@ -1098,17 +1131,23 @@ struct JSObject { inline void initArrayClass(); }; +JS_STATIC_ASSERT(offsetof(JSObject, fslots) % sizeof(js::Value) == 0); +JS_STATIC_ASSERT(sizeof(JSObject) % JS_GCTHING_ALIGN == 0); + +#define JSSLOT_START(clasp) (((clasp)->flags & JSCLASS_HAS_PRIVATE) \ + ? JSSLOT_PRIVATE + 1 \ + : JSSLOT_PRIVATE) + +#define JSSLOT_FREE(clasp) (JSSLOT_START(clasp) \ + + JSCLASS_RESERVED_SLOTS(clasp)) + /* - * Base size of an object, excluding any fixed slots. Use instead of sizeof. - * fixedSlots has a declared capacity of two to match FUN_CLASS_RESERVED_SLOTS, - * and because some compilers don't like empty arrays in superclasses. + * Maximum capacity of the obj->dslots vector, net of the hidden slot at + * obj->dslots[-1] that is used to store the length of the vector biased by + * JS_INITIAL_NSLOTS (and again net of the slot at index -1). */ -static const size_t JSOBJECT_SIZE = offsetof(JSObject, fixedSlots); - -JS_STATIC_ASSERT(JSOBJECT_SIZE % sizeof(js::Value) == 0); -JS_STATIC_ASSERT(JSOBJECT_SIZE % JS_GCTHING_ALIGN == 0); - -#define JSSLOT_FREE(clasp) JSCLASS_RESERVED_SLOTS(clasp) +#define MAX_DSLOTS_LENGTH (~size_t(0) / sizeof(js::Value) - 1) +#define MAX_DSLOTS_LENGTH32 (~uint32(0) / sizeof(js::Value) - 1) #define OBJ_CHECK_SLOT(obj,slot) JS_ASSERT((obj)->containsSlot(slot)) @@ -1168,7 +1207,7 @@ inline bool JSObject::isBlock() const { return getClass() == &js_BlockClass; } /* * Block scope object macros. The slots reserved by js_BlockClass are: * - * private JSStackFrame * active frame pointer or null + * JSSLOT_PRIVATE JSStackFrame * active frame pointer or null * JSSLOT_BLOCK_DEPTH int depth of block slots in frame * * After JSSLOT_BLOCK_DEPTH come one or more slots for the block locals. @@ -1180,7 +1219,7 @@ inline bool JSObject::isBlock() const { return getClass() == &js_BlockClass; } * whose activation they were created (or null if the with or block object * outlives the frame). */ -static const uint32 JSSLOT_BLOCK_DEPTH = 0; +static const uint32 JSSLOT_BLOCK_DEPTH = JSSLOT_PRIVATE + 1; inline bool JSObject::isStaticBlock() const @@ -1194,7 +1233,7 @@ JSObject::isClonedBlock() const return isBlock() && !!getProto(); } -static const uint32 JSSLOT_WITH_THIS = 1; +static const uint32 JSSLOT_WITH_THIS = JSSLOT_PRIVATE + 2; #define OBJ_BLOCK_COUNT(cx,obj) \ (obj)->propertyCount() diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 989bf7f35536..ed20a7cfd361 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -224,7 +224,8 @@ JSObject::setSlotMT(JSContext *cx, uintN slot, const js::Value &value) inline js::Value JSObject::getReservedSlot(uintN index) const { - return (index < numSlots()) ? getSlot(index) : js::UndefinedValue(); + uint32 slot = JSSLOT_START(getClass()) + index; + return (slot < numSlots()) ? getSlot(slot) : js::UndefinedValue(); } inline bool @@ -243,100 +244,115 @@ inline const js::Value & JSObject::getPrimitiveThis() const { JS_ASSERT(isPrimitive()); - return getSlot(JSSLOT_PRIMITIVE_THIS); + return fslots[JSSLOT_PRIMITIVE_THIS]; } inline void JSObject::setPrimitiveThis(const js::Value &pthis) { JS_ASSERT(isPrimitive()); - setSlot(JSSLOT_PRIMITIVE_THIS, pthis); + fslots[JSSLOT_PRIMITIVE_THIS] = pthis; } -inline size_t -JSObject::numFixedSlots() const +inline void +JSObject::staticAssertArrayLengthIsInPrivateSlot() { - if (isFunction()) - return JSObject::FUN_CLASS_RESERVED_SLOTS; - if (!hasSlotsArray()) - return capacity; - JSFinalizeGCThingKind kind = js_KindFromGCThing(this); - return js_GetGCKindSlots(kind); -} - -inline size_t -JSObject::slotsAndStructSize(uint32 nslots) const -{ - bool isFun = isFunction() && this == (JSObject*) getPrivate(); - - int ndslots = hasSlotsArray() ? nslots : 0; - int nfslots = isFun ? 0 : numFixedSlots(); - - return sizeof(js::Value) * (ndslots + nfslots) - + isFun ? sizeof(JSFunction) : sizeof(JSObject); + JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH == JSSLOT_PRIVATE); } inline uint32 JSObject::getArrayLength() const { JS_ASSERT(isArray()); - return (uint32)(size_t) getPrivate(); + return fslots[JSSLOT_ARRAY_LENGTH].toPrivateUint32(); } inline void JSObject::setArrayLength(uint32 length) { JS_ASSERT(isArray()); - setPrivate((void*) length); + fslots[JSSLOT_ARRAY_LENGTH].setPrivateUint32(length); } inline uint32 -JSObject::getDenseArrayCapacity() +JSObject::getDenseArrayCapacity() const { JS_ASSERT(isDenseArray()); - return numSlots(); + return fslots[JSSLOT_DENSE_ARRAY_CAPACITY].toPrivateUint32(); } -inline js::Value* -JSObject::getDenseArrayElements() +inline void +JSObject::setDenseArrayCapacity(uint32 capacity) { JS_ASSERT(isDenseArray()); - return getSlots(); + JS_ASSERT(!capacity == !dslots); + fslots[JSSLOT_DENSE_ARRAY_CAPACITY].setPrivateUint32(capacity); +} + +inline size_t +JSObject::slotsAndStructSize(uint32 nslots) const +{ + int ndslots; + if (isDenseArray()) + ndslots = getDenseArrayCapacity() + 1; + else { + ndslots = nslots - JS_INITIAL_NSLOTS; + if (ndslots <= 0) + ndslots = 0; + else + ndslots++; /* number of total slots is stored at index -1 */ + } + + return sizeof(js::Value) * ndslots + + (isFunction() && !getPrivate()) ? sizeof(JSFunction) : sizeof(JSObject); } inline const js::Value & -JSObject::getDenseArrayElement(uintN idx) +JSObject::getDenseArrayElement(uint32 i) const { JS_ASSERT(isDenseArray()); - return getSlot(idx); + JS_ASSERT(i < getDenseArrayCapacity()); + return dslots[i]; } inline js::Value * -JSObject::addressOfDenseArrayElement(uintN idx) +JSObject::addressOfDenseArrayElement(uint32 i) { JS_ASSERT(isDenseArray()); - return &getSlotRef(idx); + JS_ASSERT(i < getDenseArrayCapacity()); + return &dslots[i]; } inline void -JSObject::setDenseArrayElement(uintN idx, const js::Value &val) +JSObject::setDenseArrayElement(uint32 i, const js::Value &v) { JS_ASSERT(isDenseArray()); - setSlot(idx, val); + JS_ASSERT(i < getDenseArrayCapacity()); + dslots[i] = v; } -inline bool -JSObject::ensureDenseArrayElements(JSContext *cx, uintN cap) +inline js::Value * +JSObject::getDenseArrayElements() const { JS_ASSERT(isDenseArray()); - return ensureSlots(cx, cap); + return dslots; } inline void -JSObject::shrinkDenseArrayElements(JSContext *cx, uintN cap) +JSObject::freeDenseArrayElements(JSContext *cx) { JS_ASSERT(isDenseArray()); - shrinkSlots(cx, cap); + if (dslots) { + cx->free(dslots - 1); + dslots = NULL; + } +} + +inline void +JSObject::voidDenseOnlyArraySlots() +{ + JS_ASSERT(isDenseArray()); + fslots[JSSLOT_DENSE_ARRAY_CAPACITY].setUndefined(); } inline void @@ -345,7 +361,7 @@ JSObject::setArgsLength(uint32 argc) JS_ASSERT(isArguments()); JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); JS_ASSERT(UINT32_MAX > (uint64(argc) << ARGS_PACKED_BITS_COUNT)); - getSlotRef(JSSLOT_ARGS_LENGTH).setInt32(argc << ARGS_PACKED_BITS_COUNT); + fslots[JSSLOT_ARGS_LENGTH].setInt32(argc << ARGS_PACKED_BITS_COUNT); JS_ASSERT(!isArgsLengthOverridden()); } @@ -353,7 +369,7 @@ inline uint32 JSObject::getArgsInitialLength() const { JS_ASSERT(isArguments()); - uint32 argc = uint32(getSlot(JSSLOT_ARGS_LENGTH).toInt32()) >> ARGS_PACKED_BITS_COUNT; + uint32 argc = uint32(fslots[JSSLOT_ARGS_LENGTH].toInt32()) >> ARGS_PACKED_BITS_COUNT; JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); return argc; } @@ -362,14 +378,14 @@ inline void JSObject::setArgsLengthOverridden() { JS_ASSERT(isArguments()); - getSlotRef(JSSLOT_ARGS_LENGTH).getInt32Ref() |= ARGS_LENGTH_OVERRIDDEN_BIT; + fslots[JSSLOT_ARGS_LENGTH].getInt32Ref() |= ARGS_LENGTH_OVERRIDDEN_BIT; } inline bool JSObject::isArgsLengthOverridden() const { JS_ASSERT(isArguments()); - const js::Value &v = getSlot(JSSLOT_ARGS_LENGTH); + const js::Value &v = fslots[JSSLOT_ARGS_LENGTH]; return v.toInt32() & ARGS_LENGTH_OVERRIDDEN_BIT; } @@ -377,14 +393,14 @@ inline js::ArgumentsData * JSObject::getArgsData() const { JS_ASSERT(isArguments()); - return (js::ArgumentsData *) getSlot(JSSLOT_ARGS_DATA).toPrivate(); + return (js::ArgumentsData *) fslots[JSSLOT_ARGS_DATA].toPrivate(); } inline void JSObject::setArgsData(js::ArgumentsData *data) { JS_ASSERT(isArguments()); - getSlotRef(JSSLOT_ARGS_DATA).setPrivate(data); + fslots[JSSLOT_ARGS_DATA].setPrivate(data); } inline const js::Value & @@ -408,7 +424,7 @@ JSObject::getArgsElement(uint32 i) const } inline js::Value * -JSObject::addressOfArgsElement(uint32 i) +JSObject::addressOfArgsElement(uint32 i) const { JS_ASSERT(isArguments()); JS_ASSERT(i < getArgsInitialLength()); @@ -428,49 +444,49 @@ JSObject::setCallObjCallee(JSObject &callee) { JS_ASSERT(isCall()); JS_ASSERT(callee.isFunction()); - return getSlotRef(JSSLOT_CALL_CALLEE).setObject(callee); + return fslots[JSSLOT_CALL_CALLEE].setObject(callee); } inline JSObject & JSObject::getCallObjCallee() const { JS_ASSERT(isCall()); - return getSlot(JSSLOT_CALL_CALLEE).toObject(); + return fslots[JSSLOT_CALL_CALLEE].toObject(); } inline JSFunction * JSObject::getCallObjCalleeFunction() const { JS_ASSERT(isCall()); - return getSlot(JSSLOT_CALL_CALLEE).toObject().getFunctionPrivate(); + return fslots[JSSLOT_CALL_CALLEE].toObject().getFunctionPrivate(); } inline const js::Value & JSObject::getCallObjArguments() const { JS_ASSERT(isCall()); - return getSlot(JSSLOT_CALL_ARGUMENTS); + return fslots[JSSLOT_CALL_ARGUMENTS]; } inline void JSObject::setCallObjArguments(const js::Value &v) { JS_ASSERT(isCall()); - setSlot(JSSLOT_CALL_ARGUMENTS, v); + fslots[JSSLOT_CALL_ARGUMENTS] = v; } inline const js::Value & JSObject::getDateUTCTime() const { JS_ASSERT(isDate()); - return getSlot(JSSLOT_DATE_UTC_TIME); + return fslots[JSSLOT_DATE_UTC_TIME]; } inline void JSObject::setDateUTCTime(const js::Value &time) { JS_ASSERT(isDate()); - setSlot(JSSLOT_DATE_UTC_TIME, time); + fslots[JSSLOT_DATE_UTC_TIME] = time; } inline js::Value * @@ -478,7 +494,7 @@ JSObject::getFlatClosureUpvars() const { JS_ASSERT(isFunction()); JS_ASSERT(FUN_FLAT_CLOSURE(getFunctionPrivate())); - return (js::Value *) getSlot(JSSLOT_FLAT_CLOSURE_UPVARS).toPrivate(); + return (js::Value *) fslots[JSSLOT_FLAT_CLOSURE_UPVARS].toPrivate(); } inline js::Value @@ -492,21 +508,20 @@ JSObject::setFlatClosureUpvars(js::Value *upvars) { JS_ASSERT(isFunction()); JS_ASSERT(FUN_FLAT_CLOSURE(getFunctionPrivate())); - getSlotRef(JSSLOT_FLAT_CLOSURE_UPVARS).setPrivate(upvars); + fslots[JSSLOT_FLAT_CLOSURE_UPVARS].setPrivate(upvars); } inline bool JSObject::hasMethodObj(const JSObject& obj) const { - return JSSLOT_FUN_METHOD_OBJ < numSlots() && - getSlot(JSSLOT_FUN_METHOD_OBJ).isObject() && - &getSlot(JSSLOT_FUN_METHOD_OBJ).toObject() == &obj; + return fslots[JSSLOT_FUN_METHOD_OBJ].isObject() && + &fslots[JSSLOT_FUN_METHOD_OBJ].toObject() == &obj; } inline void JSObject::setMethodObj(JSObject& obj) { - getSlotRef(JSSLOT_FUN_METHOD_OBJ).setObject(obj); + fslots[JSSLOT_FUN_METHOD_OBJ].setObject(obj); } inline NativeIterator * @@ -525,74 +540,76 @@ inline jsval JSObject::getNamePrefix() const { JS_ASSERT(isNamespace() || isQName()); - return js::Jsvalify(getSlot(JSSLOT_NAME_PREFIX)); + return js::Jsvalify(fslots[JSSLOT_NAME_PREFIX]); } inline void JSObject::setNamePrefix(jsval prefix) { JS_ASSERT(isNamespace() || isQName()); - setSlot(JSSLOT_NAME_PREFIX, js::Valueify(prefix)); + fslots[JSSLOT_NAME_PREFIX] = js::Valueify(prefix); } inline jsval JSObject::getNameURI() const { JS_ASSERT(isNamespace() || isQName()); - return js::Jsvalify(getSlot(JSSLOT_NAME_URI)); + return js::Jsvalify(fslots[JSSLOT_NAME_URI]); } inline void JSObject::setNameURI(jsval uri) { JS_ASSERT(isNamespace() || isQName()); - setSlot(JSSLOT_NAME_URI, js::Valueify(uri)); + fslots[JSSLOT_NAME_URI] = js::Valueify(uri); } inline jsval JSObject::getNamespaceDeclared() const { JS_ASSERT(isNamespace()); - return js::Jsvalify(getSlot(JSSLOT_NAMESPACE_DECLARED)); + return js::Jsvalify(fslots[JSSLOT_NAMESPACE_DECLARED]); } inline void JSObject::setNamespaceDeclared(jsval decl) { JS_ASSERT(isNamespace()); - setSlot(JSSLOT_NAMESPACE_DECLARED, js::Valueify(decl)); + fslots[JSSLOT_NAMESPACE_DECLARED] = js::Valueify(decl); } inline jsval JSObject::getQNameLocalName() const { JS_ASSERT(isQName()); - return js::Jsvalify(getSlot(JSSLOT_QNAME_LOCAL_NAME)); + return js::Jsvalify(fslots[JSSLOT_QNAME_LOCAL_NAME]); } inline void JSObject::setQNameLocalName(jsval name) { JS_ASSERT(isQName()); - setSlot(JSSLOT_QNAME_LOCAL_NAME, js::Valueify(name)); + fslots[JSSLOT_QNAME_LOCAL_NAME] = js::Valueify(name); } inline JSObject * JSObject::getWithThis() const { - return &getSlot(JSSLOT_WITH_THIS).toObject(); + return &fslots[JSSLOT_WITH_THIS].toObject(); } inline void JSObject::setWithThis(JSObject *thisp) { - getSlotRef(JSSLOT_WITH_THIS).setObject(*thisp); + fslots[JSSLOT_WITH_THIS].setObject(*thisp); } inline void -JSObject::init(JSContext *cx, js::Class *aclasp, JSObject *proto, JSObject *parent, - void *priv, bool useHoles) +JSObject::initCommon(js::Class *aclasp, JSObject *proto, JSObject *parent, + JSContext *cx) { + JS_STATIC_ASSERT(JSSLOT_PRIVATE + 3 == JS_INITIAL_NSLOTS); + clasp = aclasp; flags = 0; @@ -609,22 +626,43 @@ JSObject::init(JSContext *cx, js::Class *aclasp, JSObject *proto, JSObject *pare setProto(proto); setParent(parent); + fslots[JSSLOT_PRIVATE + 1].setUndefined(); + fslots[JSSLOT_PRIVATE + 2].setUndefined(); - privateData = priv; - slots = &fixedSlots[0]; - - /* - * Fill the fixed slots with undefined or array holes. This object must - * already have its capacity filled in, as by js_NewGCObject. - */ - JS_ASSERT(capacity == numFixedSlots()); - ClearValueRange(fixedSlots, capacity, useHoles); + dslots = NULL; #ifdef JS_THREADSAFE js_InitTitle(cx, &title); #endif - emptyShapes = NULL; + emptyShape = NULL; +} + +inline void +JSObject::init(js::Class *aclasp, JSObject *proto, JSObject *parent, + const js::Value &privateSlotValue, JSContext *cx) +{ + initCommon(aclasp, proto, parent, cx); + fslots[JSSLOT_PRIVATE] = privateSlotValue; +} + +inline void +JSObject::init(js::Class *aclasp, JSObject *proto, JSObject *parent, + void *priv, JSContext *cx) +{ + initCommon(aclasp, proto, parent, cx); + *(void **)&fslots[JSSLOT_PRIVATE] = priv; +} + +inline void +JSObject::init(js::Class *aclasp, JSObject *proto, JSObject *parent, + JSContext *cx) +{ + initCommon(aclasp, proto, parent, cx); + if (clasp->flags & JSCLASS_HAS_PRIVATE) + *(void **)&fslots[JSSLOT_PRIVATE] = NULL; + else + fslots[JSSLOT_PRIVATE].setUndefined(); } inline void @@ -641,45 +679,40 @@ JSObject::finish(JSContext *cx) #endif } -inline bool -JSObject::initSharingEmptyShape(JSContext *cx, - js::Class *aclasp, +inline void +JSObject::initSharingEmptyShape(js::Class *aclasp, JSObject *proto, JSObject *parent, - void *privateValue, - /* JSFinalizeGCThingKind */ unsigned kind) + const js::Value &privateSlotValue, + JSContext *cx) { - init(cx, aclasp, proto, parent, privateValue, false); - - JS_ASSERT(!isDenseArray()); - - js::EmptyShape *empty = proto->getEmptyShape(cx, aclasp, JSFinalizeGCThingKind(kind)); - if (!empty) - return false; + init(aclasp, proto, parent, privateSlotValue, cx); + js::EmptyShape *empty = proto->emptyShape; + JS_ASSERT(empty->getClass() == aclasp); + setMap(empty); +} + +inline void +JSObject::initSharingEmptyShape(js::Class *aclasp, + JSObject *proto, + JSObject *parent, + void *priv, + JSContext *cx) +{ + init(aclasp, proto, parent, priv, cx); + + js::EmptyShape *empty = proto->emptyShape; + JS_ASSERT(empty->getClass() == aclasp); setMap(empty); - return true; } inline void JSObject::freeSlotsArray(JSContext *cx) { JS_ASSERT(hasSlotsArray()); - cx->free(slots); -} - -inline void -JSObject::removeSlotsArray(JSContext *cx) -{ - JS_ASSERT(hasSlotsArray()); - - size_t fixed = numFixedSlots(); - JS_ASSERT(fixed <= capacity); - memcpy(fixedSlots, slots, fixed * sizeof(js::Value)); - - freeSlotsArray(cx); - slots = &fixedSlots[0]; - capacity = fixed; + JS_ASSERT(dslots[-1].toPrivateUint32() > JS_INITIAL_NSLOTS); + cx->free(dslots - 1); } inline bool @@ -758,8 +791,7 @@ class AutoPropertyDescriptorRooter : private AutoGCRooter, public PropertyDescri }; static inline bool -InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* proto, - JSFinalizeGCThingKind kind) +InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* proto) { JS_ASSERT(clasp->isNative()); JS_ASSERT(proto == obj->getProto()); @@ -770,7 +802,7 @@ InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* pro if (proto) { JS_LOCK_OBJ(cx, proto); if (proto->canProvideEmptyShape(clasp)) { - empty = proto->getEmptyShape(cx, clasp, kind); + empty = proto->getEmptyShape(cx, clasp); JS_UNLOCK_OBJ(cx, proto); if (!empty) goto bad; @@ -780,11 +812,13 @@ InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* pro } if (!empty) { + uint32 freeslot = JSSLOT_FREE(clasp); + JS_ASSERT(freeslot >= JSSLOT_PRIVATE); + empty = js::EmptyShape::create(cx, clasp); if (!empty) goto bad; - uint32 freeslot = JSSLOT_FREE(clasp); - if (freeslot > obj->numSlots() && !obj->allocSlots(cx, freeslot)) + if (freeslot > JS_INITIAL_NSLOTS && !obj->allocSlots(cx, freeslot)) goto bad; } @@ -804,8 +838,7 @@ InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* pro * and its parent global as parent. */ static inline JSObject * -NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, - JSObject *parent, JSFinalizeGCThingKind kind) +NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent) { JS_ASSERT(proto); JS_ASSERT(proto->isNative()); @@ -815,19 +848,18 @@ NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, * Allocate an object from the GC heap and initialize all its fields before * doing any operation that can potentially trigger GC. */ - JSObject* obj = js_NewGCObject(cx, kind); + JSObject* obj = js_NewGCObject(cx); if (obj) { /* * Default parent to the parent of the prototype, which was set from * the parent of the prototype's constructor. */ - bool useHoles = (clasp == &js_ArrayClass); - obj->init(cx, clasp, proto, parent, NULL, useHoles); + obj->init(clasp, proto, parent, cx); JS_LOCK_OBJ(cx, proto); JS_ASSERT(proto->canProvideEmptyShape(clasp)); - js::EmptyShape *empty = proto->getEmptyShape(cx, clasp, kind); + js::EmptyShape *empty = proto->getEmptyShape(cx, clasp); JS_UNLOCK_OBJ(cx, proto); if (empty) @@ -839,13 +871,6 @@ NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, return obj; } -static inline JSObject * -NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent) -{ - JSFinalizeGCThingKind kind = js_GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); - return NewNativeClassInstance(cx, clasp, proto, parent, kind); -} - bool FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject **protop, Class *clasp); @@ -857,7 +882,7 @@ FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject * right default proto and parent for clasp in cx. */ static inline JSObject * -NewBuiltinClassInstance(JSContext *cx, Class *clasp, JSFinalizeGCThingKind kind) +NewBuiltinClassInstance(JSContext *cx, Class *clasp) { VOUCH_DOES_NOT_REQUIRE_STACK(); @@ -886,14 +911,7 @@ NewBuiltinClassInstance(JSContext *cx, Class *clasp, JSFinalizeGCThingKind kind) return NULL; } - return NewNativeClassInstance(cx, clasp, proto, global, kind); -} - -static inline JSObject * -NewBuiltinClassInstance(JSContext *cx, Class *clasp) -{ - JSFinalizeGCThingKind kind = js_GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); - return NewBuiltinClassInstance(cx, clasp, kind); + return NewNativeClassInstance(cx, clasp, proto, global); } static inline JSProtoKey @@ -944,8 +962,7 @@ namespace detail { template static JS_ALWAYS_INLINE JSObject * -NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, - JSFinalizeGCThingKind kind) +NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) { /* Bootstrap the ur-object, and make it the default prototype object. */ if (withProto == WithProto::Class && !proto) { @@ -954,7 +971,8 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, return NULL; if (!proto && !js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) return NULL; - } + } + /* * Allocate an object from the GC heap and initialize all its fields before @@ -963,23 +981,18 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, * * The should be specialized by the template. */ - JSObject* obj = isFunction ? js_NewGCFunction(cx) : js_NewGCObject(cx, kind); + JSObject* obj = isFunction ? js_NewGCFunction(cx) : js_NewGCObject(cx); if (!obj) goto out; - /* This needs to match up with the size of JSFunction::data_padding. */ - JS_ASSERT_IF(isFunction, kind == FINALIZE_OBJECT2); - /* * Default parent to the parent of the prototype, which was set from * the parent of the prototype's constructor. */ - obj->init(cx, clasp, proto, - (!parent && proto) ? proto->getParent() : parent, - NULL, clasp == &js_ArrayClass); + obj->init(clasp, proto, (!parent && proto) ? proto->getParent() : parent, cx); if (clasp->isNative()) { - if (!InitScopeForObject(cx, obj, clasp, proto, kind)) { + if (!InitScopeForObject(cx, obj, clasp, proto)) { obj = NULL; goto out; } @@ -991,73 +1004,28 @@ out: Probes::createObject(cx, obj); return obj; } -} /* namespace detail */ +} static JS_ALWAYS_INLINE JSObject * NewFunction(JSContext *cx, JSObject *parent) { - return detail::NewObject(cx, &js_FunctionClass, NULL, parent, - FINALIZE_OBJECT2); -} - -template -static JS_ALWAYS_INLINE JSObject * -NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, - JSFinalizeGCThingKind kind) -{ - return detail::NewObject(cx, clasp, proto, parent, kind); + return detail::NewObject(cx, &js_FunctionClass, NULL, parent); } template static JS_ALWAYS_INLINE JSObject * NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) { - JSFinalizeGCThingKind kind = js_GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); - return detail::NewObject(cx, clasp, proto, parent, kind); + return detail::NewObject(cx, clasp, proto, parent); } template static JS_ALWAYS_INLINE JSObject * NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) { - if (clasp == &js_FunctionClass) { - return detail::NewObject(cx, clasp, proto, parent, - FINALIZE_OBJECT2); - } else { - JSFinalizeGCThingKind kind = js_GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); - return detail::NewObject(cx, clasp, proto, parent, kind); - } -} - -/* Creates a new array with a zero length and the given finalize kind. */ -static inline JSObject * -NewArrayWithKind(JSContext* cx, JSFinalizeGCThingKind kind) -{ - return NewNonFunction(cx, &js_ArrayClass, NULL, NULL, kind); -} - -/* - * As for js_GetGCObjectKind, where numSlots is a guess at the final size of - * the object, zero if the final size is unknown. - */ -static inline JSFinalizeGCThingKind -GuessObjectGCKind(size_t numSlots) -{ - return numSlots ? js_GetGCObjectKind(numSlots) : FINALIZE_OBJECT8; -} - -/* - * Get the GC kind to use for scripted 'new' on the given class. - * FIXME bug 547327: estimate the size from the allocation site. - */ -static inline JSFinalizeGCThingKind -NewObjectGCKind(JSContext *cx, js::Class *clasp) -{ - if (clasp == &js_ArrayClass || clasp == &js_SlowArrayClass) - return FINALIZE_OBJECT8; - if (clasp == &js_FunctionClass) - return FINALIZE_OBJECT2; - return FINALIZE_OBJECT4; + return (clasp == &js_FunctionClass) + ? detail::NewObject(cx, clasp, proto, parent) + : detail::NewObject(cx, clasp, proto, parent); } } /* namespace js */ diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index d15ed16decc2..2d6fbd9ab5df 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -4483,7 +4483,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_NEWINIT: { - i = GET_UINT16(pc); + i = GET_INT8(pc); LOCAL_ASSERT(i == JSProto_Array || i == JSProto_Object); todo = ss->sprinter.offset; diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 82c9e02b7ecc..ff44f0001846 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -244,7 +244,7 @@ OPDEF(JSOP_SETLOCAL, 87,"setlocal", NULL, 3, 1, 1, 3, JOF_LOCAL| OPDEF(JSOP_UINT16, 88, "uint16", NULL, 3, 0, 1, 16, JOF_UINT16) /* Object and array literal support. */ -OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 5, 0, 1, 19, JOF_UINT16PAIR) +OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 2, 0, 1, 19, JOF_INT8) OPDEF(JSOP_ENDINIT, 90, "endinit", NULL, 1, 0, 0, 19, JOF_BYTE) OPDEF(JSOP_INITPROP, 91, "initprop", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) OPDEF(JSOP_INITELEM, 92, "initelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING) diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index b283cceedb7b..72dd361ed73e 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -1207,8 +1207,8 @@ static JSFunctionSpec static_methods[] = { extern Class CallableObjectClass; -static const uint32 JSSLOT_CALLABLE_CALL = 0; -static const uint32 JSSLOT_CALLABLE_CONSTRUCT = 1; +static const uint32 JSSLOT_CALLABLE_CALL = JSSLOT_PRIVATE; +static const uint32 JSSLOT_CALLABLE_CONSTRUCT = JSSLOT_PRIVATE + 1; static JSBool callable_Call(JSContext *cx, uintN argc, Value *vp) @@ -1219,7 +1219,7 @@ callable_Call(JSContext *cx, uintN argc, Value *vp) JSObject *callable = &JS_CALLEE(cx, vp).toObject(); JS_ASSERT(callable->getClass() == &CallableObjectClass); - const Value &fval = callable->getSlot(JSSLOT_CALLABLE_CALL); + const Value &fval = callable->fslots[JSSLOT_CALLABLE_CALL]; Value rval; bool ok = ExternalInvoke(cx, thisobj, fval, argc, JS_ARGV(cx, vp), &rval); *vp = rval; @@ -1235,10 +1235,10 @@ callable_Construct(JSContext *cx, uintN argc, Value *vp) JSObject *callable = &vp[0].toObject(); JS_ASSERT(callable->getClass() == &CallableObjectClass); - Value fval = callable->getSlot(JSSLOT_CALLABLE_CONSTRUCT); + Value fval = callable->fslots[JSSLOT_CALLABLE_CONSTRUCT]; if (fval.isUndefined()) { /* We don't have an explicit constructor so allocate a new object and use the call. */ - fval = callable->getSlot(JSSLOT_CALLABLE_CALL); + fval = callable->fslots[JSSLOT_CALLABLE_CALL]; JS_ASSERT(fval.isObject()); /* callable is the constructor, so get callable.prototype is the proto of the new object. */ @@ -1260,7 +1260,7 @@ callable_Construct(JSContext *cx, uintN argc, Value *vp) /* If the call returns an object, return that, otherwise the original newobj. */ Value rval; - if (!ExternalInvoke(cx, newobj, callable->getSlot(JSSLOT_CALLABLE_CALL), + if (!ExternalInvoke(cx, newobj, callable->fslots[JSSLOT_CALLABLE_CALL], argc, vp + 2, &rval)) { return false; } @@ -1318,19 +1318,15 @@ FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp) JSObject *parent = proxy->getParent(); Class *clasp = proxy->isFunctionProxy() ? &CallableObjectClass : &js_ObjectClass; - /* - * Make a blank object from the recipe fix provided to us. This must have - * number of fixed slots as the proxy so that we can swap their contents. - */ - JSFinalizeGCThingKind kind = js_KindFromGCThing(proxy); - JSObject *newborn = NewNonFunction(cx, clasp, proto, parent, kind); + /* Make a blank object from the recipe fix provided to us. */ + JSObject *newborn = NewNonFunction(cx, clasp, proto, parent); if (!newborn) return NULL; AutoObjectRooter tvr2(cx, newborn); if (clasp == &CallableObjectClass) { - newborn->setSlot(JSSLOT_CALLABLE_CALL, GetCall(proxy)); - newborn->setSlot(JSSLOT_CALLABLE_CONSTRUCT, GetConstruct(proxy)); + newborn->fslots[JSSLOT_CALLABLE_CALL] = GetCall(proxy); + newborn->fslots[JSSLOT_CALLABLE_CONSTRUCT] = GetConstruct(proxy); } { @@ -1340,8 +1336,7 @@ FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp) } /* Trade spaces between the newborn object and the proxy. */ - if (!proxy->swap(cx, newborn)) - return false; + proxy->swap(newborn); /* The GC will dispose of the proxy object. */ diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 9effa1b1b1cc..00568de77d47 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -126,11 +126,11 @@ class JSProxy { }; /* Shared between object and function proxies. */ -const uint32 JSSLOT_PROXY_HANDLER = 0; -const uint32 JSSLOT_PROXY_PRIVATE = 1; +const uint32 JSSLOT_PROXY_HANDLER = JSSLOT_PRIVATE + 0; +const uint32 JSSLOT_PROXY_PRIVATE = JSSLOT_PRIVATE + 1; /* Function proxies only. */ -const uint32 JSSLOT_PROXY_CALL = 2; -const uint32 JSSLOT_PROXY_CONSTRUCT = 3; +const uint32 JSSLOT_PROXY_CALL = JSSLOT_PRIVATE + 2; +const uint32 JSSLOT_PROXY_CONSTRUCT = JSSLOT_PRIVATE + 3; extern JS_FRIEND_API(js::Class) ObjectProxyClass; extern JS_FRIEND_API(js::Class) FunctionProxyClass; diff --git a/js/src/jsregexp.h b/js/src/jsregexp.h index 8a9c78bb641a..1043056da0b1 100644 --- a/js/src/jsregexp.h +++ b/js/src/jsregexp.h @@ -174,28 +174,28 @@ inline const js::Value & JSObject::getRegExpLastIndex() const { JS_ASSERT(isRegExp()); - return getSlot(JSSLOT_REGEXP_LAST_INDEX); + return fslots[JSSLOT_REGEXP_LAST_INDEX]; } inline void JSObject::setRegExpLastIndex(const js::Value &v) { JS_ASSERT(isRegExp()); - setSlot(JSSLOT_REGEXP_LAST_INDEX, v); + fslots[JSSLOT_REGEXP_LAST_INDEX] = v; } inline void JSObject::setRegExpLastIndex(jsdouble d) { JS_ASSERT(isRegExp()); - setSlot(JSSLOT_REGEXP_LAST_INDEX, js::NumberValue(d)); + fslots[JSSLOT_REGEXP_LAST_INDEX] = js::NumberValue(d); } inline void JSObject::zeroRegExpLastIndex() { JS_ASSERT(isRegExp()); - getSlotRef(JSSLOT_REGEXP_LAST_INDEX).setInt32(0); + fslots[JSSLOT_REGEXP_LAST_INDEX].setInt32(0); } namespace js { class AutoStringRooter; } diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index bb69fe3f55a8..bfa4fc3a5ac3 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -1128,22 +1128,6 @@ JSObject::removeProperty(JSContext *cx, jsid id) */ JS_ASSERT(shape == lastProp); removeLastProperty(); - - /* - * Revert to fixed slots if this was the first dynamically allocated slot, - * preserving invariant that objects with the same shape use the fixed - * slots in the same way. - */ - size_t fixed = numFixedSlots(); - if (shape->slot == fixed) { - JS_ASSERT(hasSlotsArray()); - JS_ASSERT_IF(!lastProp->isEmptyShape() && lastProp->hasSlot(), - lastProp->slot == fixed - 1); - memcpy(fixedSlots, slots, fixed * sizeof(Value)); - freeSlotsArray(cx); - slots = fixedSlots; - capacity = fixed; - } } updateShape(cx); diff --git a/js/src/jsscopeinlines.h b/js/src/jsscopeinlines.h index 7bbc596d0687..c26f172e31ef 100644 --- a/js/src/jsscopeinlines.h +++ b/js/src/jsscopeinlines.h @@ -60,45 +60,19 @@ js::Shape::freeTable(JSContext *cx) } inline js::EmptyShape * -JSObject::getEmptyShape(JSContext *cx, js::Class *aclasp, - /* JSFinalizeGCThingKind */ unsigned kind) +JSObject::getEmptyShape(JSContext *cx, js::Class *aclasp) { - JS_ASSERT(kind >= FINALIZE_OBJECT0 && kind <= FINALIZE_OBJECT_LAST); - int i = kind - FINALIZE_OBJECT0; - - if (!emptyShapes) { - int count = FINALIZE_OBJECT_LAST - FINALIZE_OBJECT0 + 1; - emptyShapes = (js::EmptyShape**) cx->calloc(sizeof(js::EmptyShape*) * count); - if (!emptyShapes) - return NULL; - - /* - * Always fill in emptyShapes[0], so canProvideEmptyShape works. - * Other empty shapes are filled in lazily. - */ - emptyShapes[0] = js::EmptyShape::create(cx, aclasp); - if (!emptyShapes[0]) { - cx->free(emptyShapes); - emptyShapes = NULL; - return NULL; - } - } - - JS_ASSERT(aclasp == emptyShapes[0]->getClass()); - - if (!emptyShapes[i]) { - emptyShapes[i] = js::EmptyShape::create(cx, aclasp); - if (!emptyShapes[i]) - return NULL; - } - - return emptyShapes[i]; + if (emptyShape) + JS_ASSERT(aclasp == emptyShape->getClass()); + else + emptyShape = js::EmptyShape::create(cx, aclasp); + return emptyShape; } inline bool JSObject::canProvideEmptyShape(js::Class *aclasp) { - return !emptyShapes || emptyShapes[0]->getClass() == aclasp; + return !emptyShape || emptyShape->getClass() == aclasp; } inline void diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index b8bae18f6cf7..d5fab9c44035 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -2673,7 +2673,9 @@ ptrdiff_t TraceRecorder::nativeGlobalSlot(const Value* p) const { JS_ASSERT(isGlobal(p)); - return ptrdiff_t(p - globalObj->slots); + if (size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS) + return ptrdiff_t(p - globalObj->fslots); + return ptrdiff_t((p - globalObj->dslots) + JS_INITIAL_NSLOTS); } /* Determine the offset in the native global frame for a jsval we track. */ @@ -2687,7 +2689,8 @@ TraceRecorder::nativeGlobalOffset(const Value* p) const bool TraceRecorder::isGlobal(const Value* p) const { - return (size_t(p - globalObj->slots) < globalObj->numSlots()); + return ((size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS) || + (size_t(p - globalObj->dslots) < (globalObj->numSlots() - JS_INITIAL_NSLOTS))); } bool @@ -3444,7 +3447,7 @@ struct ArgClosureTraits // Get the offset of our object slots from the object's dslots pointer. static inline uint32 slot_offset(JSObject* obj) { - return JSObject::CALL_RESERVED_SLOTS; + return JSSLOT_START(&js_CallClass) + JSObject::CALL_RESERVED_SLOTS; } // Get the maximum slot index of this type that should be allowed @@ -3475,7 +3478,7 @@ struct VarClosureTraits } static inline uint32 slot_offset(JSObject* obj) { - return JSObject::CALL_RESERVED_SLOTS + + return JSSLOT_START(&js_CallClass) + JSObject::CALL_RESERVED_SLOTS + obj->getCallObjCalleeFunction()->nargs; } @@ -3940,7 +3943,7 @@ TraceRecorder::known(JSObject** p) } /* - * The slots of the global object are sometimes reallocated by the interpreter. + * The dslots of the global object are sometimes reallocated by the interpreter. * This function check for that condition and re-maps the entries of the tracker * accordingly. */ @@ -3949,8 +3952,8 @@ TraceRecorder::checkForGlobalObjectReallocationHelper() { debug_only_print0(LC_TMTracer, "globalObj->dslots relocated, updating tracker\n"); Value* src = global_dslots; - Value* dst = globalObj->getSlots(); - jsuint length = globalObj->capacity; + Value* dst = globalObj->dslots; + jsuint length = globalObj->dslots[-1].toPrivateUint32() - JS_INITIAL_NSLOTS; LIns** map = (LIns**)alloca(sizeof(LIns*) * length); for (jsuint n = 0; n < length; ++n) { map[n] = tracker.get(src); @@ -3958,7 +3961,7 @@ TraceRecorder::checkForGlobalObjectReallocationHelper() } for (jsuint n = 0; n < length; ++n) tracker.set(dst++, map[n]); - global_dslots = globalObj->getSlots(); + global_dslots = globalObj->dslots; } /* Determine whether the current branch is a loop edge (taken or not taken). */ @@ -8720,7 +8723,7 @@ TraceRecorder::incProp(jsint incr, bool pre) CHECK_STATUS_A(inc(v, v_ins, incr, pre)); LIns* dslots_ins = NULL; - stobj_set_slot(obj, obj_ins, slot, dslots_ins, v, v_ins); + stobj_set_slot(obj_ins, slot, dslots_ins, v, v_ins); return ARECORD_CONTINUE; } @@ -9458,7 +9461,7 @@ TraceRecorder::guardPropertyCacheHit(LIns* obj_ins, void TraceRecorder::stobj_set_fslot(LIns *obj_ins, unsigned slot, const Value &v, LIns* v_ins) { - box_value_into(v, v_ins, obj_ins, offsetof(JSObject, fixedSlots) + slot * sizeof(Value), ACCSET_OTHER); + box_value_into(v, v_ins, obj_ins, offsetof(JSObject, fslots) + slot * sizeof(Value), ACCSET_OTHER); } void @@ -9466,32 +9469,49 @@ TraceRecorder::stobj_set_dslot(LIns *obj_ins, unsigned slot, LIns*& dslots_ins, const Value &v, LIns* v_ins) { if (!dslots_ins) - dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER); + dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACCSET_OTHER); box_value_into(v, v_ins, dslots_ins, slot * sizeof(Value), ACCSET_OTHER); } void -TraceRecorder::stobj_set_slot(JSObject *obj, LIns* obj_ins, unsigned slot, LIns*& dslots_ins, +TraceRecorder::stobj_set_slot(LIns* obj_ins, unsigned slot, LIns*& dslots_ins, const Value &v, LIns* v_ins) { - /* - * A shape guard must have already been generated for obj, which will - * ensure that future objects have the same number of fixed slots. - */ - if (!obj->hasSlotsArray()) { - JS_ASSERT(slot < obj->numSlots()); + if (slot < JS_INITIAL_NSLOTS) stobj_set_fslot(obj_ins, slot, v, v_ins); - } else { - stobj_set_dslot(obj_ins, slot, dslots_ins, v, v_ins); - } + else + stobj_set_dslot(obj_ins, slot - JS_INITIAL_NSLOTS, dslots_ins, v, v_ins); } #if JS_BITS_PER_WORD == 32 || JS_BITS_PER_WORD == 64 -LIns* -TraceRecorder::stobj_get_slot_uint32(LIns* obj_ins, unsigned slot) +void +TraceRecorder::set_array_fslot(LIns *obj_ins, unsigned slot, uint32 val) { - LIns *vaddr_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER); - return lir->insLoad(LIR_ldi, vaddr_ins, slot * sizeof(Value) + sPayloadOffset, ACCSET_OTHER); + /* + * We can assume the destination fslot has been appropriately tagged so we + * can just overwrite the 32-bit payload. + */ + lir->insStore(INS_CONSTU(val), obj_ins, + offsetof(JSObject, fslots) + slot * sizeof(Value) + sPayloadOffset, + ACCSET_OTHER); +} + +LIns* +TraceRecorder::stobj_get_fslot_uint32(LIns* obj_ins, unsigned slot) +{ + JS_ASSERT(slot < JS_INITIAL_NSLOTS); + return lir->insLoad(LIR_ldi, obj_ins, + offsetof(JSObject, fslots) + slot * sizeof(Value) + sPayloadOffset, + ACCSET_OTHER); +} + +LIns* +TraceRecorder::stobj_set_fslot_uint32(LIns* value_ins, LIns* obj_ins, unsigned slot) +{ + JS_ASSERT(slot < JS_INITIAL_NSLOTS); + return lir->insStore(LIR_sti, value_ins, obj_ins, + offsetof(JSObject, fslots) + slot * sizeof(Value) + sPayloadOffset, + ACCSET_OTHER); } #endif @@ -9500,14 +9520,12 @@ TraceRecorder::unbox_slot(JSObject *obj, LIns *obj_ins, uint32 slot, VMSideExit { LIns *vaddr_ins; ptrdiff_t offset; - - /* Same guarantee about fixed slots as stobj_set_slot. */ - if (!obj->hasSlotsArray()) { + if (slot < JS_INITIAL_NSLOTS) { vaddr_ins = obj_ins; - offset = offsetof(JSObject, fixedSlots) + slot * sizeof(Value); + offset = offsetof(JSObject, fslots) + slot * sizeof(Value); } else { - vaddr_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER); - offset = slot * sizeof(Value); + vaddr_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACCSET_OTHER); + offset = (slot - JS_INITIAL_NSLOTS) * sizeof(Value); } const Value &v = obj->getSlot(slot); @@ -9517,11 +9535,12 @@ TraceRecorder::unbox_slot(JSObject *obj, LIns *obj_ins, uint32 slot, VMSideExit #if JS_BITS_PER_WORD == 32 LIns* -TraceRecorder::stobj_get_const_private_ptr(LIns *obj_ins, unsigned slot) +TraceRecorder::stobj_get_fslot_private_ptr(LIns *obj_ins, unsigned slot) { - LIns *vaddr_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER); - return lir->insLoad(LIR_ldi, vaddr_ins, - slot * sizeof(Value) + sPayloadOffset, ACCSET_OTHER, LOAD_CONST); + JS_ASSERT(slot < JS_INITIAL_NSLOTS && slot != JSSLOT_PRIVATE); + return lir->insLoad(LIR_ldi, obj_ins, + offsetof(JSObject, fslots) + slot * sizeof(Value) + sPayloadOffset, + ACCSET_OTHER, LOAD_CONST); } void @@ -9678,12 +9697,13 @@ TraceRecorder::box_value_for_native_call(const Value &v, LIns *v_ins) #elif JS_BITS_PER_WORD == 64 LIns* -TraceRecorder::stobj_get_const_private_ptr(LIns *obj_ins, unsigned slot) +TraceRecorder::stobj_get_fslot_private_ptr(LIns *obj_ins, unsigned slot) { /* N.B. On 64-bit, privates are encoded differently from other pointers. */ - LIns *vaddr_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER); - LIns *v_ins = lir->insLoad(LIR_ldq, vaddr_ins, - slot * sizeof(Value) + sPayloadOffset, ACCSET_OTHER, LOAD_CONST); + JS_ASSERT(slot < JS_INITIAL_NSLOTS && slot != JSSLOT_PRIVATE); + LIns *v_ins = lir->insLoad(LIR_ldq, obj_ins, + offsetof(JSObject, fslots) + slot * sizeof(Value), + ACCSET_OTHER, LOAD_CONST); return lir->ins2ImmI(LIR_lshq, v_ins, 1); } @@ -9854,17 +9874,8 @@ TraceRecorder::stobj_get_parent(nanojit::LIns* obj_ins) LIns* TraceRecorder::stobj_get_private(nanojit::LIns* obj_ins) { - return lir->insLoad(LIR_ldp, obj_ins, - offsetof(JSObject, privateData), - ACCSET_OTHER); -} - -LIns* -TraceRecorder::stobj_get_private_uint32(nanojit::LIns* obj_ins) -{ - return lir->insLoad(LIR_ldi, obj_ins, - offsetof(JSObject, privateData), - ACCSET_OTHER); + JS_STATIC_ASSERT(JSSLOT_PRIVATE == 0); + return lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, fslots), ACCSET_OTHER); } LIns* @@ -10410,7 +10421,7 @@ TraceRecorder::newArguments(LIns* callee_ins, bool strict) guard(false, lir->insEqP_0(argsobj_ins), OOM_EXIT); if (strict) { - LIns* argsData_ins = stobj_get_const_private_ptr(argsobj_ins, JSObject::JSSLOT_ARGS_DATA); + LIns* argsData_ins = stobj_get_fslot_private_ptr(argsobj_ins, JSObject::JSSLOT_ARGS_DATA); ptrdiff_t slotsOffset = offsetof(ArgumentsData, slots); cx->fp()->forEachCanonicalActualArg(BoxArg(this, slotsOffset, argsData_ins)); } @@ -10805,7 +10816,7 @@ TraceRecorder::getClassPrototype(JSObject* ctor, LIns*& proto_ins) // that pval is usable. JS_ASSERT(!pval.isPrimitive()); JSObject *proto = &pval.toObject(); - JS_ASSERT_IF(clasp != &js_ArrayClass, proto->emptyShapes[0]->getClass() == clasp); + JS_ASSERT_IF(clasp != &js_ArrayClass, proto->emptyShape->getClass() == clasp); proto_ins = INS_CONSTOBJ(proto); return RECORD_CONTINUE; @@ -10829,8 +10840,7 @@ TraceRecorder::getClassPrototype(JSProtoKey key, LIns*& proto_ins) /* Double-check that a native proto has a matching emptyShape. */ if (key != JSProto_Array) { JS_ASSERT(proto->isNative()); - JS_ASSERT(proto->emptyShapes); - EmptyShape *empty = proto->emptyShapes[0]; + EmptyShape *empty = proto->emptyShape; JS_ASSERT(empty); JS_ASSERT(JSCLASS_CACHED_PROTO_KEY(empty->getClass()) == key); } @@ -11744,7 +11754,7 @@ TraceRecorder::nativeSet(JSObject* obj, LIns* obj_ins, const Shape* shape, set(&obj->getSlotRef(slot), v_ins); } else { LIns* dslots_ins = NULL; - stobj_set_slot(obj, obj_ins, slot, dslots_ins, v, v_ins); + stobj_set_slot(obj_ins, slot, dslots_ins, v, v_ins); } } @@ -11913,7 +11923,7 @@ TraceRecorder::setCallProp(JSObject *callobj, LIns *callobj_ins, const Shape *sh JS_ASSERT(shape->hasShortID()); LIns* dslots_ins = NULL; - stobj_set_dslot(callobj_ins, slot, dslots_ins, v, v_ins); + stobj_set_slot(callobj_ins, slot, dslots_ins, v, v_ins); return RECORD_CONTINUE; } @@ -12866,14 +12876,22 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) // be an integer. idx_ins = makeNumberInt32(idx_ins); + if (MAX_DSLOTS_LENGTH > MAX_DSLOTS_LENGTH32) { + /* + * Check for negative values bleeding through on 64-bit machines only, + * since we can't allocate large enough arrays for this on 32-bit + * machines. + */ + guard(true, lir->ins2ImmI(LIR_gei, idx_ins, 0), mismatchExit); + } + if (!js_EnsureDenseArrayCapacity(cx, obj, idx.toInt32())) RETURN_STOP_A("couldn't ensure dense array capacity for setelem"); // Grow the array if the index exceeds the capacity. This happens // rarely, eg. less than 1% of the time in SunSpider. LIns* capacity_ins = - addName(lir->insLoad(LIR_ldi, obj_ins, - offsetof(JSObject, capacity), ACCSET_OTHER), + addName(stobj_get_fslot_uint32(obj_ins, JSObject::JSSLOT_DENSE_ARRAY_CAPACITY), "capacity"); LIns* br = lir->insBranch(LIR_jt, lir->ins2(LIR_ltui, idx_ins, capacity_ins), NULL); LIns* args[] = { idx_ins, obj_ins, cx_ins }; @@ -12883,7 +12901,7 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) // Get the address of the element. LIns *dslots_ins = - addName(lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER), "dslots"); + addName(lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACCSET_OTHER), "dslots"); JS_ASSERT(sizeof(Value) == 8); // The |3| in the following statement requires this. LIns *addr_ins = lir->ins2(LIR_addp, dslots_ins, lir->ins2ImmI(LIR_lshp, lir->insUI2P(idx_ins), 3)); @@ -13089,7 +13107,7 @@ TraceRecorder::record_JSOP_GETFCSLOT() JSObject& callee = cx->fp()->callee(); LIns* callee_ins = get(&cx->fp()->calleeValue()); - LIns* upvars_ins = stobj_get_const_private_ptr(callee_ins, + LIns* upvars_ins = stobj_get_fslot_private_ptr(callee_ins, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS); unsigned index = GET_UINT16(cx->regs->pc); @@ -13349,7 +13367,7 @@ TraceRecorder::record_JSOP_APPLY() length = aobj->getArrayLength(); guard(true, lir->ins2ImmI(LIR_eqi, - stobj_get_private_uint32(aobj_ins), + stobj_get_fslot_uint32(aobj_ins, JSObject::JSSLOT_ARRAY_LENGTH), length), BRANCH_EXIT); } else if (aobj->isArguments()) { @@ -13726,10 +13744,8 @@ TraceRecorder::denseArrayElement(Value& oval, Value& ival, Value*& vp, LIns*& v_ * the correct value. */ LIns* capacity_ins = - addName(lir->insLoad(LIR_ldi, obj_ins, - offsetof(JSObject, capacity), ACCSET_OTHER), + addName(stobj_get_fslot_uint32(obj_ins, JSObject::JSSLOT_DENSE_ARRAY_CAPACITY), "capacity"); - jsuint capacity = obj->getDenseArrayCapacity(); bool within = (jsuint(idx) < capacity); if (!within) { @@ -13749,8 +13765,8 @@ TraceRecorder::denseArrayElement(Value& oval, Value& ival, Value*& vp, LIns*& v_ /* Load the value and guard on its type to unbox it. */ LIns* dslots_ins = - addName(lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER), "dslots"); - vp = &obj->slots[jsuint(idx)]; + addName(lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACCSET_OTHER), "dslots"); + vp = &obj->dslots[jsuint(idx)]; JS_ASSERT(sizeof(Value) == 8); // The |3| in the following statement requires this. addr_ins = lir->ins2(LIR_addp, dslots_ins, lir->ins2ImmI(LIR_lshp, lir->insUI2P(idx_ins), 3)); @@ -14061,7 +14077,7 @@ TraceRecorder::record_JSOP_UINT16() JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_NEWINIT() { - JSProtoKey key = JSProtoKey(GET_UINT16(cx->regs->pc)); + JSProtoKey key = JSProtoKey(GET_INT8(cx->regs->pc)); LIns* proto_ins; CHECK_STATUS_A(getClassPrototype(key, proto_ins)); @@ -15022,7 +15038,7 @@ TraceRecorder::record_JSOP_LAMBDA_FC() if (fun->u.i.nupvars) { JSUpvarArray *uva = fun->u.i.script->upvars(); - LIns* upvars_ins = stobj_get_const_private_ptr(closure_ins, + LIns* upvars_ins = stobj_get_fslot_private_ptr(closure_ins, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS); for (uint32 i = 0, n = uva->length; i < n; i++) { @@ -15106,7 +15122,7 @@ TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins) { // The following implements JSObject::isArgsLengthOverridden on trace. // ARGS_LENGTH_OVERRIDDEN_BIT is set if length was overridden. - LIns *len_ins = stobj_get_slot_uint32(argsobj_ins, JSObject::JSSLOT_ARGS_LENGTH); + LIns *len_ins = stobj_get_fslot_uint32(argsobj_ins, JSObject::JSSLOT_ARGS_LENGTH); LIns *ovr_ins = lir->ins2(LIR_andi, len_ins, INS_CONST(JSObject::ARGS_LENGTH_OVERRIDDEN_BIT)); guard(true, lir->insEqI_0(ovr_ins), snapshot(BRANCH_EXIT)); return len_ins; @@ -15813,7 +15829,7 @@ TraceRecorder::record_JSOP_LENGTH() JS_ASSERT(obj->isSlowArray()); guardClass(obj_ins, &js_SlowArrayClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); } - v_ins = lir->ins1(LIR_i2d, stobj_get_private_uint32(obj_ins)); + v_ins = lir->ins1(LIR_i2d, stobj_get_fslot_uint32(obj_ins, JSObject::JSSLOT_ARRAY_LENGTH)); } else if (OkToTraceTypedArrays && js_IsTypedArray(obj)) { // Ensure array is a typed array and is the same type as what was written guardClass(obj_ins, obj->getClass(), snapshot(BRANCH_EXIT), LOAD_NORMAL); diff --git a/js/src/jstracer.h b/js/src/jstracer.h index e2a407cc9b95..bb4ad57ed05a 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1067,7 +1067,7 @@ class TraceRecorder * entries of the tracker accordingly. */ JS_REQUIRES_STACK void checkForGlobalObjectReallocation() { - if (global_dslots != globalObj->getSlots()) + if (global_dslots != globalObj->dslots) checkForGlobalObjectReallocationHelper(); } JS_REQUIRES_STACK void checkForGlobalObjectReallocationHelper(); @@ -1173,20 +1173,21 @@ class TraceRecorder nanojit::LIns* v_ins); void stobj_set_dslot(nanojit::LIns *obj_ins, unsigned slot, nanojit::LIns*& dslots_ins, const Value &v, nanojit::LIns* v_ins); - void stobj_set_slot(JSObject *obj, nanojit::LIns* obj_ins, unsigned slot, + void stobj_set_slot(nanojit::LIns* obj_ins, unsigned slot, nanojit::LIns*& dslots_ins, const Value &v, nanojit::LIns* v_ins); + void set_array_fslot(nanojit::LIns *obj_ins, unsigned slot, uint32 val); - nanojit::LIns* stobj_get_slot_uint32(nanojit::LIns* obj_ins, unsigned slot); + nanojit::LIns* stobj_get_fslot_private_ptr(nanojit::LIns* obj_ins, + unsigned slot); + nanojit::LIns* stobj_get_fslot_uint32(nanojit::LIns* obj_ins, unsigned slot); + nanojit::LIns* stobj_set_fslot_uint32(nanojit::LIns* value_ins, nanojit::LIns* obj_ins, + unsigned slot); nanojit::LIns* unbox_slot(JSObject *obj, nanojit::LIns *obj_ins, uint32 slot, VMSideExit *exit); nanojit::LIns* stobj_get_parent(nanojit::LIns* obj_ins); nanojit::LIns* stobj_get_private(nanojit::LIns* obj_ins); - nanojit::LIns* stobj_get_private_uint32(nanojit::LIns* obj_ins); nanojit::LIns* stobj_get_proto(nanojit::LIns* obj_ins); - /* For slots holding private pointers. */ - nanojit::LIns* stobj_get_const_private_ptr(nanojit::LIns *obj_ins, unsigned slot); - JS_REQUIRES_STACK AbortableRecordingStatus name(Value*& vp, nanojit::LIns*& ins, NameResult& nr); JS_REQUIRES_STACK AbortableRecordingStatus prop(JSObject* obj, nanojit::LIns* obj_ins, uint32 *slotp, nanojit::LIns** v_insp, diff --git a/js/src/jsvalue.h b/js/src/jsvalue.h index 69cebb2c094e..975f2664722c 100644 --- a/js/src/jsvalue.h +++ b/js/src/jsvalue.h @@ -818,18 +818,6 @@ PrivateValue(void *ptr) return v; } -static JS_ALWAYS_INLINE void -ClearValueRange(Value *vec, uintN len, bool useHoles) -{ - if (useHoles) { - for (uintN i = 0; i < len; i++) - vec[i].setMagic(JS_ARRAY_HOLE); - } else { - for (uintN i = 0; i < len; i++) - vec[i].setUndefined(); - } -} - /******************************************************************************/ /* diff --git a/js/src/methodjit/BaseAssembler.h b/js/src/methodjit/BaseAssembler.h index 2b7c24054a1f..b28218de7e8c 100644 --- a/js/src/methodjit/BaseAssembler.h +++ b/js/src/methodjit/BaseAssembler.h @@ -166,9 +166,14 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::ARMRegiste * Finds and returns the address of a known object and slot. */ Address objSlotRef(JSObject *obj, RegisterID reg, uint32 slot) { - move(ImmPtr(&obj->slots), reg); + if (slot < JS_INITIAL_NSLOTS) { + void *vp = &obj->getSlotRef(slot); + move(ImmPtr(vp), reg); + return Address(reg, 0); + } + move(ImmPtr(&obj->dslots), reg); loadPtr(reg, reg); - return Address(reg, slot * sizeof(Value)); + return Address(reg, (slot - JS_INITIAL_NSLOTS) * sizeof(Value)); } #ifdef JS_CPU_X86 diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index cfc9aff3b70c..396ac1b81ff3 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -50,7 +50,6 @@ #include "assembler/jit/ExecutableAllocator.h" #include "assembler/assembler/LinkBuffer.h" #include "FrameState-inl.h" -#include "jsobjinlines.h" #include "jsscriptinlines.h" #include "InlineFrameAssembler.h" @@ -1226,13 +1225,10 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_NEWINIT) { - jsint i = GET_UINT16(PC); - uint32 count = GET_UINT16(PC + UINT16_LEN); - + jsint i = GET_INT8(PC); JS_ASSERT(i == JSProto_Array || i == JSProto_Object); prepareStubCall(Uses(0)); - masm.move(Imm32(count), Registers::ArgReg1); if (i == JSProto_Array) stubCall(stubs::NewInitArray); else @@ -1442,8 +1438,8 @@ mjit::Compiler::generateMethod() RegisterID reg = frame.allocReg(); masm.loadPayload(Address(JSFrameReg, JSStackFrame::offsetOfCallee(fun)), reg); // obj->getFlatClosureUpvars() - masm.loadPtr(Address(reg, offsetof(JSObject, slots)), reg); - Address upvarAddress(reg, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS * sizeof(Value)); + Address upvarAddress(reg, offsetof(JSObject, fslots) + + JSObject::JSSLOT_FLAT_CLOSURE_UPVARS * sizeof(Value)); masm.loadPrivate(upvarAddress, reg); // push ((Value *) reg)[index] frame.freeReg(reg); @@ -2406,7 +2402,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck) #elif defined JS_PUNBOX64 Label dslotsLoadLabel = masm.label(); #endif - masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); + masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); /* Copy the slot value to the expression stack. */ Address slot(objReg, 1 << 24); @@ -2507,7 +2503,7 @@ mjit::Compiler::jsop_getelem_pic(FrameEntry *obj, FrameEntry *id, RegisterID obj #elif defined JS_PUNBOX64 Label dslotsLoadLabel = masm.label(); #endif - masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); + masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); /* Copy the slot value to the expression stack. */ Address slot(objReg, 1 << 24); @@ -2639,7 +2635,7 @@ mjit::Compiler::jsop_callprop_generic(JSAtom *atom) #elif defined JS_PUNBOX64 Label dslotsLoadLabel = masm.label(); #endif - masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); + masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); /* Copy the slot value to the expression stack. */ Address slot(objReg, 1 << 24); @@ -2797,7 +2793,7 @@ mjit::Compiler::jsop_callprop_obj(JSAtom *atom) #elif defined JS_PUNBOX64 Label dslotsLoadLabel = masm.label(); #endif - masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); + masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); /* Copy the slot value to the expression stack. */ Address slot(objReg, 1 << 24); @@ -2966,7 +2962,7 @@ mjit::Compiler::jsop_setprop(JSAtom *atom) #elif defined JS_PUNBOX64 Label dslotsLoadLabel = masm.label(); #endif - masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); + masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); /* Store RHS into object slot. */ Address slot(objReg, 1 << 24); @@ -3510,7 +3506,8 @@ mjit::Compiler::iter(uintN flags) stubcc.linkExit(nullIterator, Uses(1)); /* Get NativeIterator from iter obj. :FIXME: X64, also most of this function */ - masm.loadPtr(Address(ioreg, offsetof(JSObject, privateData)), nireg); + Address privSlot(ioreg, offsetof(JSObject, fslots) + sizeof(Value) * JSSLOT_PRIVATE); + masm.loadPtr(privSlot, nireg); /* Test for active iterator. */ Address flagsAddr(nireg, offsetof(NativeIterator, flags)); @@ -3694,7 +3691,8 @@ mjit::Compiler::iterEnd() stubcc.linkExit(notIterator, Uses(1)); /* Get private from iter obj. :FIXME: X64 */ - masm.loadPtr(Address(reg, offsetof(JSObject, privateData)), T1); + Address privSlot(reg, offsetof(JSObject, fslots) + sizeof(Value) * JSSLOT_PRIVATE); + masm.loadPtr(privSlot, T1); RegisterID T2 = frame.allocReg(); @@ -3808,7 +3806,7 @@ mjit::Compiler::jsop_getgname(uint32 index) /* Garbage value. */ uint32 slot = 1 << 24; - masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); + masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); Address address(objReg, slot); /* @@ -3929,7 +3927,7 @@ mjit::Compiler::jsop_setgname(uint32 index) v = fe->getValue(); } - masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); + masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); Address address(objReg, slot); mic.load = masm.label(); diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index 0c45e87d52a4..7f3616390413 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -1200,21 +1200,26 @@ mjit::Compiler::jsop_setelem() stubcc.linkExit(guardDense, Uses(3)); /* guard within capacity */ - Address capacity(objReg, offsetof(JSObject, capacity)); + Address capacity(objReg, offsetof(JSObject, fslots) + + JSObject::JSSLOT_DENSE_ARRAY_CAPACITY * sizeof(Value)); Jump inRange; MaybeRegisterID maybeIdReg; if (id->isConstant()) { - inRange = masm.branch32(Assembler::LessThanOrEqual, capacity, + inRange = masm.branch32(Assembler::LessThanOrEqual, + masm.payloadOf(capacity), Imm32(id->getValue().toInt32())); } else { maybeIdReg = frame.copyDataIntoReg(id); - inRange = masm.branch32(Assembler::AboveOrEqual, maybeIdReg.reg(), capacity); + inRange = masm.branch32(Assembler::AboveOrEqual, maybeIdReg.reg(), + masm.payloadOf(capacity)); } stubcc.linkExit(inRange, Uses(3)); - /* load dslots */ - masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); + /* dslots non-NULL */ + masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); + Jump guardSlots = masm.branchTestPtr(Assembler::Zero, objReg, objReg); + stubcc.linkExit(guardSlots, Uses(3)); /* guard within capacity */ if (id->isConstant()) { @@ -1293,17 +1298,17 @@ mjit::Compiler::jsop_setelem() extendedObject.linkTo(syncTarget, &stubcc.masm); /* Update the array length if needed. Don't worry about overflow. */ - Address arrayLength(baseReg, offsetof(JSObject, privateData)); - stubcc.masm.load32(arrayLength, T1); + Address arrayLength(baseReg, offsetof(JSObject, fslots[JSObject::JSSLOT_ARRAY_LENGTH])); + stubcc.masm.loadPayload(arrayLength, T1); Jump underLength = stubcc.masm.branch32(Assembler::LessThan, idReg, T1); stubcc.masm.move(idReg, T1); stubcc.masm.add32(Imm32(1), T1); - stubcc.masm.store32(T1, arrayLength); + stubcc.masm.storePayload(T1, arrayLength); underLength.linkTo(stubcc.masm.label(), &stubcc.masm); /* Restore the dslots register if we clobbered it with the object. */ if (baseReg == objReg) - stubcc.masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); + stubcc.masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); /* Rejoin OOL path with inline path to do the store itself. */ Jump jmpHoleExit = stubcc.masm.jump(); @@ -1349,17 +1354,22 @@ mjit::Compiler::jsop_getelem_dense(FrameEntry *obj, FrameEntry *id, RegisterID o /* Guard within capacity. */ Jump inRange; - Address capacity(objReg, offsetof(JSObject, capacity)); + Address capacity(objReg, offsetof(JSObject, fslots) + + JSObject::JSSLOT_DENSE_ARRAY_CAPACITY * sizeof(Value)); if (id->isConstant()) { - inRange = masm.branch32(Assembler::LessThanOrEqual, capacity, + inRange = masm.branch32(Assembler::LessThanOrEqual, + masm.payloadOf(capacity), Imm32(id->getValue().toInt32())); } else { - inRange = masm.branch32(Assembler::AboveOrEqual, idReg.reg(), capacity); + inRange = masm.branch32(Assembler::AboveOrEqual, idReg.reg(), + masm.payloadOf(capacity)); } stubcc.linkExit(inRange, Uses(2)); - /* load dslots */ - masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); + /* dslots non-NULL */ + masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); + Jump guardSlots = masm.branchTestPtr(Assembler::Zero, objReg, objReg); + stubcc.linkExit(guardSlots, Uses(2)); /* guard within capacity */ if (id->isConstant()) { diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index 1a5cf762c430..12cf05c4006a 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -237,8 +237,7 @@ stubs::NewObject(VMFrame &f, uint32 argc) THROWV(JS_FALSE); JSObject *proto = vp[1].isObject() ? &vp[1].toObject() : NULL; - JSObject *obj = NewNonFunction(cx, &js_ObjectClass, proto, funobj->getParent(), - FINALIZE_OBJECT4); + JSObject *obj = NewNonFunction(cx, &js_ObjectClass, proto, funobj->getParent()); if (!obj) THROWV(JS_FALSE); diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index 64485130bd27..1936daf03d78 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -109,6 +109,8 @@ ic::GetGlobalName(VMFrame &f, uint32 index) repatch.repatch(mic.shape, obj->shape()); /* Patch loads. */ + JS_ASSERT(slot >= JS_INITIAL_NSLOTS); + slot -= JS_INITIAL_NSLOTS; slot *= sizeof(Value); JSC::RepatchBuffer loads(mic.load.executableAddress(), 32, false); #if defined JS_CPU_X86 @@ -189,6 +191,8 @@ ic::SetGlobalName(VMFrame &f, uint32 index) repatch.repatch(mic.shape, obj->shape()); /* Patch loads. */ + JS_ASSERT(slot >= JS_INITIAL_NSLOTS); + slot -= JS_INITIAL_NSLOTS; slot *= sizeof(Value); JSC::RepatchBuffer stores(mic.load.executableAddress(), 32, false); diff --git a/js/src/methodjit/NunboxAssembler.h b/js/src/methodjit/NunboxAssembler.h index 27e5579cab81..341429c3f460 100644 --- a/js/src/methodjit/NunboxAssembler.h +++ b/js/src/methodjit/NunboxAssembler.h @@ -89,14 +89,13 @@ class Assembler : public BaseAssembler return BaseIndex(address.base, address.index, address.scale, address.offset + TAG_OFFSET); } - void loadSlot(RegisterID obj, RegisterID clobber, uint32 slot, bool inlineAccess, - RegisterID type, RegisterID data) { + void loadSlot(RegisterID obj, RegisterID clobber, uint32 slot, RegisterID type, RegisterID data) { JS_ASSERT(type != data); - Address address(obj, offsetof(JSObject, fixedSlots) + slot * sizeof(Value)); + Address address(obj, offsetof(JSObject, fslots) + slot * sizeof(Value)); RegisterID activeAddressReg = obj; - if (!inlineAccess) { - loadPtr(Address(obj, offsetof(JSObject, slots)), clobber); - address = Address(clobber, slot * sizeof(Value)); + if (slot >= JS_INITIAL_NSLOTS) { + loadPtr(Address(obj, offsetof(JSObject, dslots)), clobber); + address = Address(clobber, (slot - JS_INITIAL_NSLOTS) * sizeof(Value)); activeAddressReg = clobber; } if (activeAddressReg == type) { @@ -205,8 +204,9 @@ class Assembler : public BaseAssembler } void loadFunctionPrivate(RegisterID base, RegisterID to) { - Address priv(base, offsetof(JSObject, privateData)); - loadPtr(priv, to); + Address privSlot(base, offsetof(JSObject, fslots) + + JSSLOT_PRIVATE * sizeof(Value)); + loadPtr(privSlot, to); } Jump testNull(Assembler::Condition cond, RegisterID reg) { diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 2919d3f2aa1d..a383409dad7b 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -253,7 +253,7 @@ class SetPropCompiler : public PICStubCompiler repatcher.relinkCallerToTrampoline(retPtr, target); } - bool patchInline(const Shape *shape, bool inlineSlot) + bool patchInline(const Shape *shape) { JS_ASSERT(!pic.inlinePathPatched); JaegerSpew(JSpew_PICs, "patch setprop inline at %p\n", pic.fastPathStart.executableAddress()); @@ -261,7 +261,7 @@ class SetPropCompiler : public PICStubCompiler PICRepatchBuffer repatcher(pic, pic.fastPathStart); int32 offset; - if (inlineSlot) { + if (shape->slot < JS_INITIAL_NSLOTS) { JSC::CodeLocationInstruction istr; istr = pic.storeBack.instructionAtOffset(dslotsLoadOffset()); repatcher.repatchLoadPtrToLEA(istr); @@ -273,12 +273,12 @@ class SetPropCompiler : public PICStubCompiler // Because the offset is wrong, it's necessary to correct it // below. // - int32 diff = int32(offsetof(JSObject, fixedSlots)) - - int32(offsetof(JSObject, slots)); + int32 diff = int32(offsetof(JSObject, fslots)) - + int32(offsetof(JSObject, dslots)); JS_ASSERT(diff != 0); offset = (int32(shape->slot) * sizeof(Value)) + diff; } else { - offset = shape->slot * sizeof(Value); + offset = (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value); } uint32 shapeOffs = pic.shapeGuard + inlineShapeOffset(); @@ -326,7 +326,7 @@ class SetPropCompiler : public PICStubCompiler } } - bool generateStub(uint32 initialShape, const Shape *shape, bool adding, bool inlineSlot) + bool generateStub(uint32 initialShape, const Shape *shape, bool adding) { /* Exits to the slow path. */ Vector slowExits(f.cx); @@ -406,21 +406,28 @@ class SetPropCompiler : public PICStubCompiler } } - if (inlineSlot) { + if (shape->slot < JS_INITIAL_NSLOTS) { Address address(pic.objReg, - offsetof(JSObject, fixedSlots) + shape->slot * sizeof(Value)); + offsetof(JSObject, fslots) + shape->slot * sizeof(Value)); emitStore(masm, address); } else { + /* Check dslots non-zero. */ + masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.shapeReg); + Jump emptyDslots = masm.branchPtr(Assembler::Equal, pic.shapeReg, ImmPtr(0)); + if (!slowExits.append(emptyDslots)) + return false; + /* Check capacity. */ - Address capacity(pic.objReg, offsetof(JSObject, capacity)); + Address capacity(pic.shapeReg, -ptrdiff_t(sizeof(Value))); masm.load32(masm.payloadOf(capacity), pic.shapeReg); Jump overCapacity = masm.branch32(Assembler::LessThanOrEqual, pic.shapeReg, Imm32(shape->slot)); if (!slowExits.append(overCapacity)) return false; - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.shapeReg); - Address address(pic.shapeReg, shape->slot * sizeof(Value)); + masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.shapeReg); + Address address(pic.shapeReg, + (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value)); emitStore(masm, address); } @@ -441,10 +448,10 @@ class SetPropCompiler : public PICStubCompiler masm.store32(pic.shapeReg, flags); } } else if (shape->hasDefaultSetter()) { - Address address(pic.objReg, offsetof(JSObject, fixedSlots) + shape->slot * sizeof(Value)); - if (!inlineSlot) { - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg); - address = Address(pic.objReg, shape->slot * sizeof(Value)); + Address address(pic.objReg, offsetof(JSObject, fslots) + shape->slot * sizeof(Value)); + if (shape->slot >= JS_INITIAL_NSLOTS) { + masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.objReg); + address = Address(pic.objReg, (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value)); } // If the scope is branded, or has a method barrier. It's now necessary @@ -486,9 +493,9 @@ class SetPropCompiler : public PICStubCompiler { if (shape->setterOp() == SetCallVar) slot += fun->nargs; - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg); + masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.objReg); - Address dslot(pic.objReg, (slot + JSObject::CALL_RESERVED_SLOTS) * sizeof(Value)); + Address dslot(pic.objReg, slot * sizeof(Value)); emitStore(masm, dslot); } @@ -668,7 +675,7 @@ class SetPropCompiler : public PICStubCompiler if (obj->numSlots() != slots) return disable("insufficient slot capacity"); - return generateStub(initialShape, shape, true, !obj->hasSlotsArray()); + return generateStub(initialShape, shape, true); } AutoPropertyDropper dropper(f.cx, holder, prop); @@ -696,10 +703,10 @@ class SetPropCompiler : public PICStubCompiler !obj->brandedOrHasMethodBarrier() && shape->hasDefaultSetter() && !obj->isDenseArray()) { - return patchInline(shape, !obj->hasSlotsArray()); + return patchInline(shape); } - return generateStub(obj->shape(), shape, false, !obj->hasSlotsArray()); + return generateStub(obj->shape(), shape, false); } }; @@ -803,8 +810,9 @@ class GetPropCompiler : public PICStubCompiler Address clasp(pic.objReg, offsetof(JSObject, clasp)); Jump notArgs = masm.branchPtr(Assembler::NotEqual, clasp, ImmPtr(obj->getClass())); - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg); - masm.load32(Address(pic.objReg, JSObject::JSSLOT_ARGS_LENGTH * sizeof(Value)), + masm.load32(Address(pic.objReg, + offsetof(JSObject, fslots) + + JSObject::JSSLOT_ARGS_LENGTH * sizeof(Value)), pic.objReg); masm.move(pic.objReg, pic.shapeReg); Jump overridden = masm.branchTest32(Assembler::NonZero, pic.shapeReg, Imm32(1)); @@ -848,7 +856,10 @@ class GetPropCompiler : public PICStubCompiler ImmPtr(&js_SlowArrayClass)); isDense.linkTo(masm.label(), &masm); - masm.load32(Address(pic.objReg, offsetof(JSObject, privateData)), pic.objReg); + masm.load32(Address(pic.objReg, + offsetof(JSObject, fslots) + + JSObject::JSSLOT_ARRAY_LENGTH * sizeof(Value)), + pic.objReg); Jump oob = masm.branch32(Assembler::Above, pic.objReg, Imm32(JSVAL_INT_MAX)); masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg); Jump done = masm.jump(); @@ -933,8 +944,7 @@ class GetPropCompiler : public PICStubCompiler masm.loadShape(pic.objReg, pic.shapeReg); Jump shapeMismatch = masm.branch32(Assembler::NotEqual, pic.shapeReg, Imm32(obj->shape())); - masm.loadSlot(pic.objReg, pic.objReg, shape->slot, !obj->hasSlotsArray(), - pic.shapeReg, pic.objReg); + masm.loadSlot(pic.objReg, pic.objReg, shape->slot, pic.shapeReg, pic.objReg); Jump done = masm.jump(); @@ -1013,7 +1023,7 @@ class GetPropCompiler : public PICStubCompiler PICRepatchBuffer repatcher(pic, pic.fastPathStart); int32 offset; - if (!holder->hasSlotsArray()) { + if (shape->slot < JS_INITIAL_NSLOTS) { JSC::CodeLocationInstruction istr; istr = pic.storeBack.instructionAtOffset(dslotsLoad()); repatcher.repatchLoadPtrToLEA(istr); @@ -1025,12 +1035,12 @@ class GetPropCompiler : public PICStubCompiler // Because the offset is wrong, it's necessary to correct it // below. // - int32 diff = int32(offsetof(JSObject, fixedSlots)) - - int32(offsetof(JSObject, slots)); + int32 diff = int32(offsetof(JSObject, fslots)) - + int32(offsetof(JSObject, dslots)); JS_ASSERT(diff != 0); offset = (int32(shape->slot) * sizeof(Value)) + diff; } else { - offset = shape->slot * sizeof(Value); + offset = (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value); } uint32 shapeOffs = pic.shapeGuard + inlineShapeOffset(); @@ -1136,8 +1146,7 @@ class GetPropCompiler : public PICStubCompiler } /* Load the value out of the object. */ - masm.loadSlot(pic.objReg, pic.objReg, shape->slot, !holder->hasSlotsArray(), - pic.shapeReg, pic.objReg); + masm.loadSlot(pic.objReg, pic.objReg, shape->slot, pic.shapeReg, pic.objReg); Jump done = masm.jump(); JSC::ExecutablePool *ep = getExecPool(masm.size()); @@ -1349,7 +1358,7 @@ class GetElemCompiler : public PICStubCompiler PICRepatchBuffer repatcher(pic, pic.fastPathStart); int32 offset; - if (!holder->hasSlotsArray()) { + if (shape->slot < JS_INITIAL_NSLOTS) { JSC::CodeLocationInstruction istr = pic.storeBack.instructionAtOffset(dslotsLoad()); repatcher.repatchLoadPtrToLEA(istr); @@ -1360,11 +1369,11 @@ class GetElemCompiler : public PICStubCompiler // Because the offset is wrong, it's necessary to correct it // below. // - int32 diff = int32(offsetof(JSObject, fixedSlots)) - int32(offsetof(JSObject, slots)); + int32 diff = int32(offsetof(JSObject, fslots)) - int32(offsetof(JSObject, dslots)); JS_ASSERT(diff != 0); offset = (int32(shape->slot) * sizeof(Value)) + diff; } else { - offset = shape->slot * sizeof(Value); + offset = (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value); } uint32 shapeOffset = pic.shapeGuard + inlineShapeOffset(); @@ -1493,8 +1502,7 @@ class GetElemCompiler : public PICStubCompiler } /* Load the value out of the object. */ - masm.loadSlot(pic.objReg, pic.objReg, shape->slot, !holder->hasSlotsArray(), - pic.shapeReg, pic.objReg); + masm.loadSlot(pic.objReg, pic.objReg, shape->slot, pic.shapeReg, pic.objReg); Jump done = masm.jump(); JSC::ExecutablePool *ep = getExecPool(masm.size()); @@ -1683,7 +1691,7 @@ class ScopeNameCompiler : public PICStubCompiler masm.loadShape(pic.objReg, pic.shapeReg); Jump finalShape = masm.branch32(Assembler::NotEqual, pic.shapeReg, Imm32(holder->shape())); - masm.loadSlot(pic.objReg, pic.objReg, shape->slot, false, pic.shapeReg, pic.objReg); + masm.loadSlot(pic.objReg, pic.objReg, shape->slot, pic.shapeReg, pic.objReg); Jump done = masm.jump(); @@ -1786,11 +1794,11 @@ class ScopeNameCompiler : public PICStubCompiler escapedFrame.linkTo(masm.label(), &masm); { - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg); + masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.objReg); if (kind == VAR) slot += fun->nargs; - Address dslot(pic.objReg, (slot + JSObject::CALL_RESERVED_SLOTS) * sizeof(Value)); + Address dslot(pic.objReg, slot * sizeof(Value)); /* Safe because type is loaded first. */ masm.loadValueAsComponents(dslot, pic.shapeReg, pic.objReg); diff --git a/js/src/methodjit/PunboxAssembler.h b/js/src/methodjit/PunboxAssembler.h index 49a07e9076d5..d72b8a0be810 100644 --- a/js/src/methodjit/PunboxAssembler.h +++ b/js/src/methodjit/PunboxAssembler.h @@ -93,13 +93,12 @@ class Assembler : public BaseAssembler return address; } - void loadSlot(RegisterID obj, RegisterID clobber, uint32 slot, bool inlineAccess, - RegisterID type, RegisterID data) { + void loadSlot(RegisterID obj, RegisterID clobber, uint32 slot, RegisterID type, RegisterID data) { JS_ASSERT(type != data); - Address address(obj, offsetof(JSObject, fixedSlots) + slot * sizeof(Value)); - if (!inlineAccess) { - loadPtr(Address(obj, offsetof(JSObject, slots)), clobber); - address = Address(clobber, slot * sizeof(Value)); + Address address(obj, offsetof(JSObject, fslots) + slot * sizeof(Value)); + if (slot >= JS_INITIAL_NSLOTS) { + loadPtr(Address(obj, offsetof(JSObject, dslots)), clobber); + address = Address(clobber, (slot - JS_INITIAL_NSLOTS) * sizeof(Value)); } loadValueAsComponents(address, type, data); @@ -247,8 +246,9 @@ class Assembler : public BaseAssembler } void loadFunctionPrivate(RegisterID base, RegisterID to) { - Address priv(base, offsetof(JSObject, privateData)); - loadPtr(priv, to); + Address privSlot(base, offsetof(JSObject, fslots) + + JSSLOT_PRIVATE * sizeof(Value)); + loadPtr(privSlot, to); } Jump testNull(Assembler::Condition cond, RegisterID reg) { diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 898cfac9b422..0734c8cb9984 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -1432,25 +1432,21 @@ stubs::Neg(VMFrame &f) } JSObject * JS_FASTCALL -stubs::NewInitArray(VMFrame &f, uint32 count) +stubs::NewInitArray(VMFrame &f) { - JSContext *cx = f.cx; - JSFinalizeGCThingKind kind = GuessObjectGCKind(count); - - JSObject *obj = NewArrayWithKind(cx, kind); - if (!obj || !obj->ensureSlots(cx, count)) + JSObject *obj = js_NewArrayObject(f.cx, 0, NULL); + if (!obj) THROWV(NULL); return obj; } JSObject * JS_FASTCALL -stubs::NewInitObject(VMFrame &f, uint32 count) +stubs::NewInitObject(VMFrame &f) { JSContext *cx = f.cx; - JSFinalizeGCThingKind kind = GuessObjectGCKind(count); - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); - if (!obj || !obj->ensureSlots(cx, count)) + JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass); + if (!obj) THROWV(NULL); return obj; diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index 4a1c94dd017a..a89b48d94639 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -49,8 +49,8 @@ namespace stubs { void JS_FASTCALL This(VMFrame &f); void JS_FASTCALL ComputeThis(VMFrame &f); -JSObject * JS_FASTCALL NewInitArray(VMFrame &f, uint32 count); -JSObject * JS_FASTCALL NewInitObject(VMFrame &f, uint32 count); +JSObject * JS_FASTCALL NewInitArray(VMFrame &f); +JSObject * JS_FASTCALL NewInitObject(VMFrame &f); JSObject * JS_FASTCALL NewArray(VMFrame &f, uint32 len); void JS_FASTCALL Trap(VMFrame &f, jsbytecode *pc); void JS_FASTCALL Debugger(VMFrame &f, jsbytecode *pc); diff --git a/js/src/trace-test/tests/basic/testHoleInDenseArray.js b/js/src/trace-test/tests/basic/testHoleInDenseArray.js index ba7a2c01e16c..eb041a2055c9 100644 --- a/js/src/trace-test/tests/basic/testHoleInDenseArray.js +++ b/js/src/trace-test/tests/basic/testHoleInDenseArray.js @@ -8,8 +8,7 @@ function f(i) { return 1; } -/* trailing 'true' ensures array has capacity >= 10 */ -var arr = [ false, false, false, false, false, , , , , , true ]; +var arr = [ false, false, false, false, false, , , , ]; for (var i = 0; i < 10; ++i) { (s = arr[i]) + f(i); diff --git a/js/src/trace-test/tests/jaeger/bug563000/eif-trap-typechange.js b/js/src/trace-test/tests/jaeger/bug563000/eif-trap-typechange.js index 11f2ff0a659c..30974f3dafd9 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/eif-trap-typechange.js +++ b/js/src/trace-test/tests/jaeger/bug563000/eif-trap-typechange.js @@ -6,5 +6,5 @@ function caller(obj) { var x = ({ dana : "zuul" }); return x; } -trap(caller, 23, "x = 'success'; nop()"); +trap(caller, 20, "x = 'success'; nop()"); assertEq(caller(this), "success"); diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index 4b400738c597..7ea2ea728f2d 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -1373,10 +1373,10 @@ DebugCheckWrapperClass(JSObject* obj) // Only use these macros if IS_WRAPPER_CLASS(obj->getClass()) is true. #define IS_WN_WRAPPER_OBJECT(obj) \ (DebugCheckWrapperClass(obj) && \ - obj->getSlot(0).isUndefined()) + obj->getSlot(JSSLOT_START(obj->getClass())).isUndefined()) #define IS_SLIM_WRAPPER_OBJECT(obj) \ (DebugCheckWrapperClass(obj) && \ - !obj->getSlot(0).isUndefined()) + !obj->getSlot(JSSLOT_START(obj->getClass())).isUndefined()) // Use these macros if IS_WRAPPER_CLASS(obj->getClass()) might be false. // Avoid calling them if IS_WRAPPER_CLASS(obj->getClass()) can only be @@ -2284,7 +2284,7 @@ extern JSBool MorphSlimWrapper(JSContext *cx, JSObject *obj); static inline XPCWrappedNativeProto* GetSlimWrapperProto(JSObject *obj) { - const js::Value &v = obj->getSlot(0); + const js::Value &v = obj->getSlot(JSSLOT_START(obj->getClass())); return static_cast(v.toPrivate()); } diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 42cb2003d94b..17743713999f 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -52,7 +52,7 @@ namespace xpc { using namespace js; -static const uint32 JSSLOT_WN_OBJ = 0; +static const uint32 JSSLOT_WN_OBJ = JSSLOT_PRIVATE; static JSBool holder_get(JSContext *cx, JSObject *holder, jsid id, jsval *vp);