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