mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-01 05:48:26 +00:00
bug 487204 - avoiding extra locks for js_Native(Get|Set). r=brendan
This commit is contained in:
parent
3e11fd6eb0
commit
496bcfe77d
@ -793,8 +793,9 @@ array_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
|||||||
sprop = (JSScopeProperty *) prop;
|
sprop = (JSScopeProperty *) prop;
|
||||||
if (!js_NativeGet(cx, obj, obj2, sprop, vp))
|
if (!js_NativeGet(cx, obj, obj2, sprop, vp))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
|
} else {
|
||||||
|
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
||||||
}
|
}
|
||||||
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
|
||||||
}
|
}
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
@ -1291,7 +1292,7 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj)
|
|||||||
|
|
||||||
sprop = js_AddScopeProperty(cx, (JSScope *)map, id, NULL, NULL,
|
sprop = js_AddScopeProperty(cx, (JSScope *)map, id, NULL, NULL,
|
||||||
i + JS_INITIAL_NSLOTS, JSPROP_ENUMERATE,
|
i + JS_INITIAL_NSLOTS, JSPROP_ENUMERATE,
|
||||||
0, 0);
|
0, 0, NULL);
|
||||||
if (!sprop)
|
if (!sprop)
|
||||||
goto out_bad;
|
goto out_bad;
|
||||||
}
|
}
|
||||||
|
@ -274,7 +274,8 @@ js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
|
|||||||
|
|
||||||
sprop2 = js_AddScopeProperty(cx, scope, sprop->id,
|
sprop2 = js_AddScopeProperty(cx, scope, sprop->id,
|
||||||
sprop->getter, sprop->setter, SPROP_INVALID_SLOT,
|
sprop->getter, sprop->setter, SPROP_INVALID_SLOT,
|
||||||
sprop->attrs, sprop->flags, sprop->shortid);
|
sprop->attrs, sprop->flags, sprop->shortid,
|
||||||
|
NULL);
|
||||||
if (sprop2 == sprop) {
|
if (sprop2 == sprop) {
|
||||||
JS_UNLOCK_SCOPE(cx, scope);
|
JS_UNLOCK_SCOPE(cx, scope);
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
|
@ -111,23 +111,26 @@ js_GenerateShape(JSContext *cx, JSBool gcLocked, JSScopeProperty *sprop)
|
|||||||
}
|
}
|
||||||
|
|
||||||
JS_REQUIRES_STACK void
|
JS_REQUIRES_STACK void
|
||||||
js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
|
js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
||||||
uintN scopeIndex, uintN protoIndex,
|
uintN scopeIndex, uintN protoIndex,
|
||||||
JSObject *pobj, JSScopeProperty *sprop,
|
JSObject *pobj, JSScopeProperty *sprop,
|
||||||
JSPropCacheEntry **entryp)
|
JSBool cacheByPrevShape, JSPropCacheEntry **entryp)
|
||||||
{
|
{
|
||||||
JSPropertyCache *cache;
|
JSPropertyCache *cache;
|
||||||
jsbytecode *pc;
|
jsbytecode *pc;
|
||||||
JSScope *scope;
|
JSScope *scope;
|
||||||
|
jsuword kshape, khash;
|
||||||
JSOp op;
|
JSOp op;
|
||||||
const JSCodeSpec *cs;
|
const JSCodeSpec *cs;
|
||||||
jsuword vword;
|
jsuword vword;
|
||||||
ptrdiff_t pcoff;
|
ptrdiff_t pcoff;
|
||||||
jsuword khash;
|
|
||||||
JSAtom *atom;
|
JSAtom *atom;
|
||||||
JSPropCacheEntry *entry;
|
JSPropCacheEntry *entry;
|
||||||
|
|
||||||
|
JS_ASSERT(OBJ_SCOPE(pobj)->object == pobj);
|
||||||
|
JS_ASSERT(SCOPE_HAS_PROPERTY(OBJ_SCOPE(pobj), sprop));
|
||||||
JS_ASSERT(!cx->runtime->gcRunning);
|
JS_ASSERT(!cx->runtime->gcRunning);
|
||||||
|
|
||||||
cache = &JS_PROPERTY_CACHE(cx);
|
cache = &JS_PROPERTY_CACHE(cx);
|
||||||
pc = cx->fp->regs->pc;
|
pc = cx->fp->regs->pc;
|
||||||
if (cache->disabled || (cx->fp->flags & JSFRAME_EVAL)) {
|
if (cache->disabled || (cx->fp->flags & JSFRAME_EVAL)) {
|
||||||
@ -137,24 +140,11 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for fill from js_SetPropertyHelper where the setter removed sprop
|
* Check for overdeep scope and prototype chain. Because resolve hooks
|
||||||
* from pobj's scope (via unwatch or delete, e.g.).
|
* that js_LookupPropertyWithFlags runs can change the prototype chain
|
||||||
*/
|
* using JS_SetPrototype, we have to validate protoIndex if it is
|
||||||
scope = OBJ_SCOPE(pobj);
|
* non-zero. If it is zero, then we know from the fact that obj == pobj
|
||||||
JS_ASSERT(scope->object == pobj);
|
* that protoIndex is invariant.
|
||||||
if (!SCOPE_HAS_PROPERTY(scope, sprop)) {
|
|
||||||
PCMETER(cache->oddfills++);
|
|
||||||
*entryp = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check for overdeep scope and prototype chain. Because resolve, getter,
|
|
||||||
* and setter hooks can change the prototype chain using JS_SetPrototype
|
|
||||||
* after js_LookupPropertyWithFlags has returned the nominal protoIndex,
|
|
||||||
* we have to validate protoIndex if it is non-zero. If it is zero, then
|
|
||||||
* we know thanks to the SCOPE_HAS_PROPERTY test above, and from the fact
|
|
||||||
* that obj == pobj, that protoIndex is invariant.
|
|
||||||
*
|
*
|
||||||
* The scopeIndex can't be wrong. We require JS_SetParent calls to happen
|
* The scopeIndex can't be wrong. We require JS_SetParent calls to happen
|
||||||
* before any running script might consult a parent-linked scope chain. If
|
* before any running script might consult a parent-linked scope chain. If
|
||||||
@ -198,7 +188,8 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
|
|||||||
*/
|
*/
|
||||||
op = js_GetOpcode(cx, cx->fp->script, pc);
|
op = js_GetOpcode(cx, cx->fp->script, pc);
|
||||||
cs = &js_CodeSpec[op];
|
cs = &js_CodeSpec[op];
|
||||||
|
scope = OBJ_SCOPE(pobj);
|
||||||
|
kshape = 0;
|
||||||
do {
|
do {
|
||||||
/*
|
/*
|
||||||
* Check for a prototype "plain old method" callee computation. What
|
* Check for a prototype "plain old method" callee computation. What
|
||||||
@ -234,12 +225,18 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
|
|||||||
JSVAL_TO_OBJECT(v),
|
JSVAL_TO_OBJECT(v),
|
||||||
JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx,
|
JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx,
|
||||||
JSVAL_TO_OBJECT(v))),
|
JSVAL_TO_OBJECT(v))),
|
||||||
kshape);
|
OBJ_SHAPE(obj));
|
||||||
#endif
|
#endif
|
||||||
SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);
|
SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);
|
||||||
|
if (cache->disabled) {
|
||||||
|
/*
|
||||||
|
* js_GenerateShape could not recover from shape's
|
||||||
|
* overflow.
|
||||||
|
*/
|
||||||
|
*entryp = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
SCOPE_SET_BRANDED(scope);
|
SCOPE_SET_BRANDED(scope);
|
||||||
if (OBJ_SCOPE(obj) == scope)
|
|
||||||
kshape = scope->shape;
|
|
||||||
}
|
}
|
||||||
vword = JSVAL_OBJECT_TO_PCVAL(v);
|
vword = JSVAL_OBJECT_TO_PCVAL(v);
|
||||||
break;
|
break;
|
||||||
@ -256,26 +253,48 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
|
|||||||
} else {
|
} else {
|
||||||
/* Best we can do is to cache sprop (still a nice speedup). */
|
/* Best we can do is to cache sprop (still a nice speedup). */
|
||||||
vword = SPROP_TO_PCVAL(sprop);
|
vword = SPROP_TO_PCVAL(sprop);
|
||||||
|
if (cacheByPrevShape) {
|
||||||
|
/*
|
||||||
|
* Our caller just called js_AddScopeProperty and added a new
|
||||||
|
* property. We want to cache under the shape that was prior
|
||||||
|
* the call to bias for the case when the mutator always adds
|
||||||
|
* the same property. It allows to optimize periodic execution
|
||||||
|
* of object initializers or explicit initialization sequences
|
||||||
|
* like
|
||||||
|
*
|
||||||
|
* obj = {}; obj.x = 1; obj.y = 2;
|
||||||
|
*
|
||||||
|
* We assume that on average the win from this optimization is
|
||||||
|
* bigger that the cost of an extra mismatch per loop due to
|
||||||
|
* the bias for the following case:
|
||||||
|
*
|
||||||
|
* obj = {}; ... for (...) { ... obj.x = ... }
|
||||||
|
*
|
||||||
|
* Here on the first iteration JSOP_SETPROP fills the cache
|
||||||
|
* with the shape of newly created object, not the shape after
|
||||||
|
* obj.x is assigned. That mismatches obj's shape on the
|
||||||
|
* second iteration. Note that on third and the following
|
||||||
|
* iterations the cache will be hit since the shape no longer
|
||||||
|
* mutates.
|
||||||
|
*/
|
||||||
|
JS_ASSERT(scope->object == obj);
|
||||||
|
JS_ASSERT(sprop == scope->lastProp);
|
||||||
|
if (sprop->parent) {
|
||||||
|
kshape = sprop->parent->shape;
|
||||||
|
} else {
|
||||||
|
JSObject *proto = STOBJ_GET_PROTO(obj);
|
||||||
|
if (proto && OBJ_IS_NATIVE(proto))
|
||||||
|
kshape = OBJ_SHAPE(proto);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} while (0);
|
} while (0);
|
||||||
|
|
||||||
/*
|
if (kshape == 0)
|
||||||
* Our caller preserved the scope shape prior to the js_GetPropertyHelper
|
kshape = OBJ_SHAPE(obj);
|
||||||
* or similar call out of the interpreter. We want to cache under that
|
|
||||||
* shape if op is overtly mutating, to bias for the case where the mutator
|
|
||||||
* udpates shape predictably.
|
|
||||||
*
|
|
||||||
* Note that an apparently non-mutating op such as JSOP_NAME may still
|
|
||||||
* mutate the base object via, e.g., lazy standard class initialization,
|
|
||||||
* but that is a one-time event and we'll have to miss the old shape and
|
|
||||||
* re-fill under the new one.
|
|
||||||
*/
|
|
||||||
if (!(cs->format & (JOF_SET | JOF_INCDEC)) && obj == pobj)
|
|
||||||
kshape = scope->shape;
|
|
||||||
|
|
||||||
khash = PROPERTY_CACHE_HASH_PC(pc, kshape);
|
khash = PROPERTY_CACHE_HASH_PC(pc, kshape);
|
||||||
if (obj == pobj) {
|
if (obj == pobj) {
|
||||||
JS_ASSERT(kshape != 0 || scope->shape != 0);
|
JS_ASSERT(kshape != 0);
|
||||||
JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
|
JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
|
||||||
JS_ASSERT(OBJ_SCOPE(obj)->object == obj);
|
JS_ASSERT(OBJ_SCOPE(obj)->object == obj);
|
||||||
} else {
|
} else {
|
||||||
@ -470,7 +489,6 @@ js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache)
|
|||||||
P(nofills);
|
P(nofills);
|
||||||
P(rofills);
|
P(rofills);
|
||||||
P(disfills);
|
P(disfills);
|
||||||
P(oddfills);
|
|
||||||
P(modfills);
|
P(modfills);
|
||||||
P(brandfills);
|
P(brandfills);
|
||||||
P(noprotos);
|
P(noprotos);
|
||||||
@ -2620,6 +2638,16 @@ JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_DECNAME_LENGTH);
|
|||||||
JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH);
|
JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH);
|
||||||
JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH);
|
JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH);
|
||||||
|
|
||||||
|
#ifdef JS_TRACER
|
||||||
|
# define ABORT_RECORDING(cx, reason) \
|
||||||
|
JS_BEGIN_MACRO \
|
||||||
|
if (TRACE_RECORDER(cx)) \
|
||||||
|
js_AbortRecording(cx, reason); \
|
||||||
|
JS_END_MACRO
|
||||||
|
#else
|
||||||
|
# define ABORT_RECORDING(cx, reason) ((void 0))
|
||||||
|
#endif
|
||||||
|
|
||||||
JS_REQUIRES_STACK JSBool
|
JS_REQUIRES_STACK JSBool
|
||||||
js_Interpret(JSContext *cx)
|
js_Interpret(JSContext *cx)
|
||||||
{
|
{
|
||||||
@ -3015,10 +3043,7 @@ js_Interpret(JSContext *cx)
|
|||||||
bool moreInterrupts = false;
|
bool moreInterrupts = false;
|
||||||
JSTrapHandler handler = cx->debugHooks->interruptHandler;
|
JSTrapHandler handler = cx->debugHooks->interruptHandler;
|
||||||
if (handler) {
|
if (handler) {
|
||||||
#ifdef JS_TRACER
|
ABORT_RECORDING(cx, "interrupt handler");
|
||||||
if (TRACE_RECORDER(cx))
|
|
||||||
js_AbortRecording(cx, "interrupt handler");
|
|
||||||
#endif
|
|
||||||
switch (handler(cx, script, regs.pc, &rval,
|
switch (handler(cx, script, regs.pc, &rval,
|
||||||
cx->debugHooks->interruptHandlerData)) {
|
cx->debugHooks->interruptHandlerData)) {
|
||||||
case JSTRAP_ERROR:
|
case JSTRAP_ERROR:
|
||||||
@ -3558,21 +3583,25 @@ js_Interpret(JSContext *cx)
|
|||||||
*vp = ((sprop)->slot != SPROP_INVALID_SLOT) \
|
*vp = ((sprop)->slot != SPROP_INVALID_SLOT) \
|
||||||
? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \
|
? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \
|
||||||
: JSVAL_VOID; \
|
: JSVAL_VOID; \
|
||||||
|
JS_UNLOCK_OBJ(cx, pobj); \
|
||||||
} else { \
|
} else { \
|
||||||
if (!js_NativeGet(cx, obj, pobj, sprop, vp)) \
|
if (!js_NativeGet(cx, obj, pobj, sprop, vp)) \
|
||||||
goto error; \
|
goto error; \
|
||||||
} \
|
} \
|
||||||
JS_END_MACRO
|
JS_END_MACRO
|
||||||
|
|
||||||
#define NATIVE_SET(cx,obj,sprop,vp) \
|
#define NATIVE_SET(cx,obj,entry,sprop,vp) \
|
||||||
JS_BEGIN_MACRO \
|
JS_BEGIN_MACRO \
|
||||||
if (SPROP_HAS_STUB_SETTER(sprop) && \
|
if (SPROP_HAS_STUB_SETTER(sprop) && \
|
||||||
(sprop)->slot != SPROP_INVALID_SLOT) { \
|
(sprop)->slot != SPROP_INVALID_SLOT) { \
|
||||||
/* Fast path for, e.g., Object instance properties. */ \
|
/* Fast path for, e.g., Object instance properties. */ \
|
||||||
LOCKED_OBJ_WRITE_BARRIER(cx, obj, (sprop)->slot, *vp); \
|
LOCKED_OBJ_WRITE_BARRIER(cx, obj, (sprop)->slot, *vp); \
|
||||||
|
JS_UNLOCK_OBJ(cx, obj); \
|
||||||
|
TRACE_2(SetPropHit, entry, sprop); \
|
||||||
} else { \
|
} else { \
|
||||||
if (!js_NativeSet(cx, obj, sprop, vp)) \
|
if (!js_NativeSet(cx, obj, sprop, vp)) \
|
||||||
goto error; \
|
goto error; \
|
||||||
|
ABORT_RECORDING(cx, "non-stub setter"); \
|
||||||
} \
|
} \
|
||||||
JS_END_MACRO
|
JS_END_MACRO
|
||||||
|
|
||||||
@ -4432,6 +4461,7 @@ js_Interpret(JSContext *cx)
|
|||||||
JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
|
JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
|
||||||
sprop = PCVAL_TO_SPROP(entry->vword);
|
sprop = PCVAL_TO_SPROP(entry->vword);
|
||||||
NATIVE_GET(cx, obj, obj2, sprop, &rval);
|
NATIVE_GET(cx, obj, obj2, sprop, &rval);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
JS_UNLOCK_OBJ(cx, obj2);
|
JS_UNLOCK_OBJ(cx, obj2);
|
||||||
break;
|
break;
|
||||||
@ -4515,16 +4545,17 @@ js_Interpret(JSContext *cx)
|
|||||||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
|
ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
|
||||||
if (PCVAL_IS_OBJECT(entry->vword)) {
|
if (PCVAL_IS_OBJECT(entry->vword)) {
|
||||||
rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
|
rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
|
||||||
|
JS_UNLOCK_OBJ(cx, obj2);
|
||||||
} else if (PCVAL_IS_SLOT(entry->vword)) {
|
} else if (PCVAL_IS_SLOT(entry->vword)) {
|
||||||
slot = PCVAL_TO_SLOT(entry->vword);
|
slot = PCVAL_TO_SLOT(entry->vword);
|
||||||
JS_ASSERT(slot < obj2->map->freeslot);
|
JS_ASSERT(slot < obj2->map->freeslot);
|
||||||
rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
|
rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
|
||||||
|
JS_UNLOCK_OBJ(cx, obj2);
|
||||||
} else {
|
} else {
|
||||||
JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
|
JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
|
||||||
sprop = PCVAL_TO_SPROP(entry->vword);
|
sprop = PCVAL_TO_SPROP(entry->vword);
|
||||||
NATIVE_GET(cx, obj, obj2, sprop, &rval);
|
NATIVE_GET(cx, obj, obj2, sprop, &rval);
|
||||||
}
|
}
|
||||||
JS_UNLOCK_OBJ(cx, obj2);
|
|
||||||
STORE_OPND(-1, rval);
|
STORE_OPND(-1, rval);
|
||||||
PUSH_OPND(lval);
|
PUSH_OPND(lval);
|
||||||
goto end_callprop;
|
goto end_callprop;
|
||||||
@ -4635,9 +4666,7 @@ js_Interpret(JSContext *cx)
|
|||||||
SCOPE_HAS_PROPERTY(scope, sprop)) {
|
SCOPE_HAS_PROPERTY(scope, sprop)) {
|
||||||
PCMETER(cache->pchits++);
|
PCMETER(cache->pchits++);
|
||||||
PCMETER(cache->setpchits++);
|
PCMETER(cache->setpchits++);
|
||||||
NATIVE_SET(cx, obj, sprop, &rval);
|
NATIVE_SET(cx, obj, entry, sprop, &rval);
|
||||||
JS_UNLOCK_SCOPE(cx, scope);
|
|
||||||
TRACE_2(SetPropHit, entry, sprop);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -4704,7 +4733,8 @@ js_Interpret(JSContext *cx)
|
|||||||
slot,
|
slot,
|
||||||
sprop->attrs,
|
sprop->attrs,
|
||||||
sprop->flags,
|
sprop->flags,
|
||||||
sprop->shortid);
|
sprop->shortid,
|
||||||
|
NULL);
|
||||||
if (!sprop2) {
|
if (!sprop2) {
|
||||||
js_FreeSlot(cx, obj, slot);
|
js_FreeSlot(cx, obj, slot);
|
||||||
JS_UNLOCK_SCOPE(cx, scope);
|
JS_UNLOCK_SCOPE(cx, scope);
|
||||||
@ -4762,13 +4792,10 @@ js_Interpret(JSContext *cx)
|
|||||||
sprop = PCVAL_TO_SPROP(entry->vword);
|
sprop = PCVAL_TO_SPROP(entry->vword);
|
||||||
JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
|
JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
|
||||||
JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj2)));
|
JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj2)));
|
||||||
NATIVE_SET(cx, obj, sprop, &rval);
|
NATIVE_SET(cx, obj, entry, sprop, &rval);
|
||||||
}
|
|
||||||
JS_UNLOCK_OBJ(cx, obj2);
|
|
||||||
if (sprop) {
|
|
||||||
TRACE_2(SetPropHit, entry, sprop);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
JS_UNLOCK_OBJ(cx, obj2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4786,10 +4813,7 @@ js_Interpret(JSContext *cx)
|
|||||||
if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
|
if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
#ifdef JS_TRACER
|
ABORT_RECORDING(cx, "SetPropUncached");
|
||||||
if (!entry && TRACE_RECORDER(cx))
|
|
||||||
js_AbortRecording(cx, "SetPropUncached");
|
|
||||||
#endif
|
|
||||||
} while (0);
|
} while (0);
|
||||||
END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2);
|
END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2);
|
||||||
|
|
||||||
@ -5309,7 +5333,6 @@ js_Interpret(JSContext *cx)
|
|||||||
sprop = (JSScopeProperty *)prop;
|
sprop = (JSScopeProperty *)prop;
|
||||||
do_native_get:
|
do_native_get:
|
||||||
NATIVE_GET(cx, obj, obj2, sprop, &rval);
|
NATIVE_GET(cx, obj, obj2, sprop, &rval);
|
||||||
OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *) sprop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
do_push_rval:
|
do_push_rval:
|
||||||
@ -5789,10 +5812,7 @@ js_Interpret(JSContext *cx)
|
|||||||
* JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval]
|
* JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval]
|
||||||
* as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME.
|
* as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME.
|
||||||
*/
|
*/
|
||||||
#ifdef JS_TRACER
|
ABORT_RECORDING(cx, "SETGVAR with NULL slot");
|
||||||
if (TRACE_RECORDER(cx))
|
|
||||||
js_AbortRecording(cx, "SETGVAR with NULL slot");
|
|
||||||
#endif
|
|
||||||
LOAD_ATOM(0);
|
LOAD_ATOM(0);
|
||||||
id = ATOM_TO_JSID(atom);
|
id = ATOM_TO_JSID(atom);
|
||||||
if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
|
if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
|
||||||
@ -6097,10 +6117,7 @@ js_Interpret(JSContext *cx)
|
|||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (OBJ_GET_PARENT(cx, obj) != parent) {
|
if (OBJ_GET_PARENT(cx, obj) != parent) {
|
||||||
#ifdef JS_TRACER
|
ABORT_RECORDING(cx, "DEFLOCALFUN for closure");
|
||||||
if (TRACE_RECORDER(cx))
|
|
||||||
js_AbortRecording(cx, "DEFLOCALFUN for closure");
|
|
||||||
#endif
|
|
||||||
obj = js_CloneFunctionObject(cx, fun, parent);
|
obj = js_CloneFunctionObject(cx, fun, parent);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
goto error;
|
goto error;
|
||||||
@ -6394,7 +6411,8 @@ js_Interpret(JSContext *cx)
|
|||||||
js_AddScopeProperty(cx, scope, sprop->id,
|
js_AddScopeProperty(cx, scope, sprop->id,
|
||||||
sprop->getter, sprop->setter,
|
sprop->getter, sprop->setter,
|
||||||
slot, sprop->attrs,
|
slot, sprop->attrs,
|
||||||
sprop->flags, sprop->shortid);
|
sprop->flags, sprop->shortid,
|
||||||
|
NULL);
|
||||||
if (!sprop2) {
|
if (!sprop2) {
|
||||||
js_FreeSlot(cx, obj, slot);
|
js_FreeSlot(cx, obj, slot);
|
||||||
JS_UNLOCK_SCOPE(cx, scope);
|
JS_UNLOCK_SCOPE(cx, scope);
|
||||||
@ -7141,15 +7159,12 @@ js_Interpret(JSContext *cx)
|
|||||||
|
|
||||||
JS_ASSERT((size_t)((fp->imacpc ? fp->imacpc : regs.pc) - script->code) < script->length);
|
JS_ASSERT((size_t)((fp->imacpc ? fp->imacpc : regs.pc) - script->code) < script->length);
|
||||||
|
|
||||||
#ifdef JS_TRACER
|
|
||||||
/*
|
/*
|
||||||
* This abort could be weakened to permit tracing through exceptions that
|
* This abort could be weakened to permit tracing through exceptions that
|
||||||
* are thrown and caught within a loop, with the co-operation of the tracer.
|
* are thrown and caught within a loop, with the co-operation of the tracer.
|
||||||
* For now just bail on any sign of trouble.
|
* For now just bail on any sign of trouble.
|
||||||
*/
|
*/
|
||||||
if (TRACE_RECORDER(cx))
|
ABORT_RECORDING(cx, "error or exception while recording");
|
||||||
js_AbortRecording(cx, "error or exception while recording");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!cx->throwing) {
|
if (!cx->throwing) {
|
||||||
/* This is an error, not a catchable exception, quit the frame ASAP. */
|
/* This is an error, not a catchable exception, quit the frame ASAP. */
|
||||||
|
@ -273,7 +273,6 @@ typedef struct JSPropertyCache {
|
|||||||
uint32 nofills; /* couldn't fill (e.g. default get) */
|
uint32 nofills; /* couldn't fill (e.g. default get) */
|
||||||
uint32 rofills; /* set on read-only prop can't fill */
|
uint32 rofills; /* set on read-only prop can't fill */
|
||||||
uint32 disfills; /* fill attempts on disabled cache */
|
uint32 disfills; /* fill attempts on disabled cache */
|
||||||
uint32 oddfills; /* fill attempt after setter deleted */
|
|
||||||
uint32 modfills; /* fill that rehashed to a new entry */
|
uint32 modfills; /* fill that rehashed to a new entry */
|
||||||
uint32 brandfills; /* scope brandings to type structural
|
uint32 brandfills; /* scope brandings to type structural
|
||||||
method fills */
|
method fills */
|
||||||
@ -344,10 +343,10 @@ typedef struct JSPropertyCache {
|
|||||||
* 4-bit scopeIndex, and 4-bit protoIndex.
|
* 4-bit scopeIndex, and 4-bit protoIndex.
|
||||||
*/
|
*/
|
||||||
extern JS_REQUIRES_STACK void
|
extern JS_REQUIRES_STACK void
|
||||||
js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
|
js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
||||||
uintN scopeIndex, uintN protoIndex,
|
uintN scopeIndex, uintN protoIndex,
|
||||||
JSObject *pobj, JSScopeProperty *sprop,
|
JSObject *pobj, JSScopeProperty *sprop,
|
||||||
JSPropCacheEntry **entryp);
|
JSBool cacheByPrevShape, JSPropCacheEntry **entryp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Property cache lookup macros. PROPERTY_CACHE_TEST is designed to inline the
|
* Property cache lookup macros. PROPERTY_CACHE_TEST is designed to inline the
|
||||||
|
201
js/src/jsobj.cpp
201
js/src/jsobj.cpp
@ -3646,7 +3646,7 @@ js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
|
|||||||
/* Convert string indices to integers if appropriate. */
|
/* Convert string indices to integers if appropriate. */
|
||||||
CHECK_FOR_STRING_INDEX(id);
|
CHECK_FOR_STRING_INDEX(id);
|
||||||
sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs,
|
sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs,
|
||||||
flags, shortid);
|
flags, shortid, NULL);
|
||||||
}
|
}
|
||||||
JS_UNLOCK_OBJ(cx, obj);
|
JS_UNLOCK_OBJ(cx, obj);
|
||||||
return sprop;
|
return sprop;
|
||||||
@ -3712,12 +3712,11 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
|||||||
JSClass *clasp;
|
JSClass *clasp;
|
||||||
JSScope *scope;
|
JSScope *scope;
|
||||||
JSScopeProperty *sprop;
|
JSScopeProperty *sprop;
|
||||||
|
JSBool cacheByPrevShape;
|
||||||
|
|
||||||
/* Convert string indices to integers if appropriate. */
|
/* Convert string indices to integers if appropriate. */
|
||||||
CHECK_FOR_STRING_INDEX(id);
|
CHECK_FOR_STRING_INDEX(id);
|
||||||
|
|
||||||
uint32 shape = OBJ_SHAPE(obj);
|
|
||||||
|
|
||||||
#if JS_HAS_GETTER_SETTER
|
#if JS_HAS_GETTER_SETTER
|
||||||
/*
|
/*
|
||||||
* If defining a getter or setter, we must check for its counterpart and
|
* If defining a getter or setter, we must check for its counterpart and
|
||||||
@ -3785,13 +3784,14 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
|||||||
if (!scope)
|
if (!scope)
|
||||||
goto bad;
|
goto bad;
|
||||||
|
|
||||||
|
cacheByPrevShape = false;
|
||||||
if (!sprop) {
|
if (!sprop) {
|
||||||
/* Add a new property, or replace an existing one of the same id. */
|
/* Add a new property, or replace an existing one of the same id. */
|
||||||
if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
|
if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
|
||||||
attrs |= JSPROP_SHARED;
|
attrs |= JSPROP_SHARED;
|
||||||
sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
|
sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
|
||||||
SPROP_INVALID_SLOT, attrs, flags,
|
SPROP_INVALID_SLOT, attrs, flags,
|
||||||
shortid);
|
shortid, &cacheByPrevShape);
|
||||||
if (!sprop)
|
if (!sprop)
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
@ -3807,10 +3807,12 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
|||||||
|
|
||||||
if (entryp) {
|
if (entryp) {
|
||||||
JS_ASSERT_NOT_ON_TRACE(cx);
|
JS_ASSERT_NOT_ON_TRACE(cx);
|
||||||
if (!(attrs & JSPROP_SHARED))
|
if (!(attrs & JSPROP_SHARED)) {
|
||||||
js_FillPropertyCache(cx, obj, shape, 0, 0, obj, sprop, entryp);
|
js_FillPropertyCache(cx, obj, 0, 0, obj, sprop,
|
||||||
else
|
cacheByPrevShape, entryp);
|
||||||
|
} else {
|
||||||
PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
|
PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (propp)
|
if (propp)
|
||||||
*propp = (JSProperty *) sprop;
|
*propp = (JSProperty *) sprop;
|
||||||
@ -4009,15 +4011,14 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSObject **objp,
|
|||||||
JSObject **pobjp, JSProperty **propp,
|
JSObject **pobjp, JSProperty **propp,
|
||||||
JSPropCacheEntry **entryp)
|
JSPropCacheEntry **entryp)
|
||||||
{
|
{
|
||||||
JSObject *obj, *pobj, *lastobj;
|
JSObject *scopeChain, *obj, *pobj, *lastobj;
|
||||||
uint32 shape;
|
|
||||||
int scopeIndex, protoIndex;
|
int scopeIndex, protoIndex;
|
||||||
JSProperty *prop;
|
JSProperty *prop;
|
||||||
JSScopeProperty *sprop;
|
JSScopeProperty *sprop;
|
||||||
|
|
||||||
JS_ASSERT_IF(entryp, !JS_ON_TRACE(cx));
|
JS_ASSERT_IF(entryp, !JS_ON_TRACE(cx));
|
||||||
obj = js_GetTopStackFrame(cx)->scopeChain;
|
scopeChain = js_GetTopStackFrame(cx)->scopeChain;
|
||||||
shape = OBJ_SHAPE(obj);
|
obj = scopeChain;
|
||||||
for (scopeIndex = 0; ; scopeIndex++) {
|
for (scopeIndex = 0; ; scopeIndex++) {
|
||||||
if (obj->map->ops->lookupProperty == js_LookupProperty) {
|
if (obj->map->ops->lookupProperty == js_LookupProperty) {
|
||||||
protoIndex =
|
protoIndex =
|
||||||
@ -4035,8 +4036,8 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSObject **objp,
|
|||||||
if (entryp) {
|
if (entryp) {
|
||||||
if (protoIndex >= 0 && OBJ_IS_NATIVE(pobj)) {
|
if (protoIndex >= 0 && OBJ_IS_NATIVE(pobj)) {
|
||||||
sprop = (JSScopeProperty *) prop;
|
sprop = (JSScopeProperty *) prop;
|
||||||
js_FillPropertyCache(cx, cx->fp->scopeChain, shape,
|
js_FillPropertyCache(cx, scopeChain, scopeIndex,
|
||||||
scopeIndex, protoIndex, pobj, sprop,
|
protoIndex, pobj, sprop, false,
|
||||||
entryp);
|
entryp);
|
||||||
} else {
|
} else {
|
||||||
PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
|
PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
|
||||||
@ -4118,9 +4119,8 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id,
|
|||||||
if (prop) {
|
if (prop) {
|
||||||
JS_ASSERT(OBJ_IS_NATIVE(pobj));
|
JS_ASSERT(OBJ_IS_NATIVE(pobj));
|
||||||
JS_ASSERT(OBJ_GET_CLASS(cx, pobj) == OBJ_GET_CLASS(cx, obj));
|
JS_ASSERT(OBJ_GET_CLASS(cx, pobj) == OBJ_GET_CLASS(cx, obj));
|
||||||
js_FillPropertyCache(cx, scopeChain, OBJ_SHAPE(scopeChain),
|
js_FillPropertyCache(cx, scopeChain, scopeIndex, protoIndex, pobj,
|
||||||
scopeIndex, protoIndex, pobj,
|
(JSScopeProperty *) prop, false, &entry);
|
||||||
(JSScopeProperty *) prop, &entry);
|
|
||||||
JS_UNLOCK_OBJ(cx, pobj);
|
JS_UNLOCK_OBJ(cx, pobj);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@ -4159,69 +4159,67 @@ JSBool
|
|||||||
js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj,
|
js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj,
|
||||||
JSScopeProperty *sprop, jsval *vp)
|
JSScopeProperty *sprop, jsval *vp)
|
||||||
{
|
{
|
||||||
js_LeaveTraceIfGlobalObject(cx, pobj);
|
|
||||||
|
|
||||||
JSScope *scope;
|
|
||||||
uint32 slot;
|
|
||||||
int32 sample;
|
|
||||||
JSTempValueRooter tvr;
|
|
||||||
JSBool ok;
|
|
||||||
|
|
||||||
JS_ASSERT(OBJ_IS_NATIVE(pobj));
|
JS_ASSERT(OBJ_IS_NATIVE(pobj));
|
||||||
JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj));
|
JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj));
|
||||||
scope = OBJ_SCOPE(pobj);
|
JS_ASSERT(OBJ_SCOPE(pobj)->object == pobj);
|
||||||
JS_ASSERT(scope->object == pobj);
|
|
||||||
|
|
||||||
slot = sprop->slot;
|
js_LeaveTraceIfGlobalObject(cx, pobj);
|
||||||
*vp = (slot != SPROP_INVALID_SLOT)
|
|
||||||
? LOCKED_OBJ_GET_SLOT(pobj, slot)
|
|
||||||
: JSVAL_VOID;
|
|
||||||
if (SPROP_HAS_STUB_GETTER(sprop))
|
|
||||||
return JS_TRUE;
|
|
||||||
|
|
||||||
sample = cx->runtime->propertyRemovals;
|
JSScope *scope = OBJ_SCOPE(pobj);
|
||||||
JS_UNLOCK_SCOPE(cx, scope);
|
uint32 slot = sprop->slot;
|
||||||
JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
|
if (slot == SPROP_INVALID_SLOT) {
|
||||||
ok = js_GetSprop(cx, sprop, obj, vp);
|
JS_UNLOCK_SCOPE(cx, scope);
|
||||||
JS_POP_TEMP_ROOT(cx, &tvr);
|
*vp = JSVAL_VOID;
|
||||||
if (!ok)
|
return SPROP_HAS_STUB_GETTER(sprop) || js_GetSprop(cx, sprop, obj, vp);
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
JS_LOCK_SCOPE(cx, scope);
|
|
||||||
JS_ASSERT(scope->object == pobj);
|
|
||||||
if (SLOT_IN_SCOPE(slot, scope) &&
|
|
||||||
(JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
|
|
||||||
SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) {
|
|
||||||
LOCKED_OBJ_SET_SLOT(pobj, slot, *vp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*vp = LOCKED_OBJ_GET_SLOT(pobj, slot);
|
||||||
|
if (!SPROP_HAS_STUB_GETTER(sprop)) {
|
||||||
|
/*
|
||||||
|
* For API compatibility, we must set the slot with the getter's
|
||||||
|
* result, so we have to make sure that it is still valid after we run
|
||||||
|
* the getter. FIXME: we should reconsider this, bug 488458.
|
||||||
|
*/
|
||||||
|
int32 sample = cx->runtime->propertyRemovals;
|
||||||
|
JSTempValueRooter tvr, tvr2;
|
||||||
|
JSBool ok;
|
||||||
|
|
||||||
|
JS_UNLOCK_SCOPE(cx, scope);
|
||||||
|
JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
|
||||||
|
JS_PUSH_TEMP_ROOT_OBJECT(cx, pobj, &tvr2);
|
||||||
|
ok = js_GetSprop(cx, sprop, obj, vp);
|
||||||
|
JS_POP_TEMP_ROOT(cx, &tvr2);
|
||||||
|
JS_POP_TEMP_ROOT(cx, &tvr);
|
||||||
|
if (!ok)
|
||||||
|
return JS_FALSE;
|
||||||
|
|
||||||
|
JS_LOCK_SCOPE(cx, scope);
|
||||||
|
JS_ASSERT(scope->object == pobj);
|
||||||
|
if (SLOT_IN_SCOPE(slot, scope) &&
|
||||||
|
(JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
|
||||||
|
SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) {
|
||||||
|
LOCKED_OBJ_SET_SLOT(pobj, slot, *vp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JS_UNLOCK_SCOPE(cx, scope);
|
||||||
|
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSBool
|
JSBool
|
||||||
js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp)
|
js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp)
|
||||||
{
|
{
|
||||||
js_LeaveTraceIfGlobalObject(cx, obj);
|
|
||||||
|
|
||||||
JSScope *scope;
|
|
||||||
uint32 slot;
|
|
||||||
int32 sample;
|
|
||||||
JSTempValueRooter tvr;
|
|
||||||
JSBool ok;
|
|
||||||
|
|
||||||
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
||||||
JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj));
|
JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj));
|
||||||
scope = OBJ_SCOPE(obj);
|
JS_ASSERT(OBJ_SCOPE(obj)->object == obj);
|
||||||
JS_ASSERT(scope->object == obj);
|
|
||||||
|
|
||||||
slot = sprop->slot;
|
js_LeaveTraceIfGlobalObject(cx, obj);
|
||||||
if (slot != SPROP_INVALID_SLOT) {
|
|
||||||
OBJ_CHECK_SLOT(obj, slot);
|
JSScope *scope = OBJ_SCOPE(obj);
|
||||||
|
uint32 slot = sprop->slot;
|
||||||
|
if (slot == SPROP_INVALID_SLOT) {
|
||||||
|
JS_UNLOCK_SCOPE(cx, scope);
|
||||||
|
|
||||||
/* If sprop has a stub setter, keep scope locked and just store *vp. */
|
|
||||||
if (SPROP_HAS_STUB_SETTER(sprop))
|
|
||||||
goto set_slot;
|
|
||||||
} else {
|
|
||||||
/*
|
/*
|
||||||
* Allow API consumers to create shared properties with stub setters.
|
* Allow API consumers to create shared properties with stub setters.
|
||||||
* Such properties lack value storage, so setting them is like writing
|
* Such properties lack value storage, so setting them is like writing
|
||||||
@ -4235,24 +4233,39 @@ js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp)
|
|||||||
JS_ASSERT(!(sprop->attrs & JSPROP_SETTER));
|
JS_ASSERT(!(sprop->attrs & JSPROP_SETTER));
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
return js_SetSprop(cx, sprop, obj, vp);
|
||||||
}
|
}
|
||||||
|
|
||||||
sample = cx->runtime->propertyRemovals;
|
OBJ_CHECK_SLOT(obj, slot);
|
||||||
JS_UNLOCK_SCOPE(cx, scope);
|
|
||||||
JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
|
|
||||||
ok = js_SetSprop(cx, sprop, obj, vp);
|
|
||||||
JS_POP_TEMP_ROOT(cx, &tvr);
|
|
||||||
if (!ok)
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
JS_LOCK_SCOPE(cx, scope);
|
if (SPROP_HAS_STUB_SETTER(sprop)) {
|
||||||
JS_ASSERT(scope->object == obj);
|
|
||||||
if (SLOT_IN_SCOPE(slot, scope) &&
|
|
||||||
(JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
|
|
||||||
SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) {
|
|
||||||
set_slot:
|
|
||||||
LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, *vp);
|
LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, *vp);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* For API compatibility we must set the slot with setter's result so
|
||||||
|
* we must make sure that it is still valid after we run the setter.
|
||||||
|
* FIXME: we should reconsider this, bug 488458.
|
||||||
|
*/
|
||||||
|
int32 sample = cx->runtime->propertyRemovals;
|
||||||
|
JSTempValueRooter tvr;
|
||||||
|
JSBool ok;
|
||||||
|
|
||||||
|
JS_UNLOCK_SCOPE(cx, scope);
|
||||||
|
JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
|
||||||
|
ok = js_SetSprop(cx, sprop, obj, vp);
|
||||||
|
JS_POP_TEMP_ROOT(cx, &tvr);
|
||||||
|
if (!ok)
|
||||||
|
return JS_FALSE;
|
||||||
|
|
||||||
|
JS_LOCK_SCOPE(cx, scope);
|
||||||
|
JS_ASSERT(scope->object == obj);
|
||||||
|
if (SLOT_IN_SCOPE(slot, scope) &&
|
||||||
|
(JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
|
||||||
|
SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) {
|
||||||
|
LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, *vp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
JS_UNLOCK_SCOPE(cx, scope);
|
||||||
|
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
@ -4262,7 +4275,6 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
|
|||||||
JSPropCacheEntry **entryp)
|
JSPropCacheEntry **entryp)
|
||||||
{
|
{
|
||||||
JSObject *aobj, *obj2;
|
JSObject *aobj, *obj2;
|
||||||
uint32 shape;
|
|
||||||
int protoIndex;
|
int protoIndex;
|
||||||
JSProperty *prop;
|
JSProperty *prop;
|
||||||
JSScopeProperty *sprop;
|
JSScopeProperty *sprop;
|
||||||
@ -4272,7 +4284,6 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
|
|||||||
CHECK_FOR_STRING_INDEX(id);
|
CHECK_FOR_STRING_INDEX(id);
|
||||||
|
|
||||||
aobj = js_GetProtoIfDenseArray(cx, obj);
|
aobj = js_GetProtoIfDenseArray(cx, obj);
|
||||||
shape = OBJ_SHAPE(aobj);
|
|
||||||
protoIndex = js_LookupPropertyWithFlags(cx, aobj, id, cx->resolveFlags,
|
protoIndex = js_LookupPropertyWithFlags(cx, aobj, id, cx->resolveFlags,
|
||||||
&obj2, &prop);
|
&obj2, &prop);
|
||||||
if (protoIndex < 0)
|
if (protoIndex < 0)
|
||||||
@ -4346,15 +4357,14 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
sprop = (JSScopeProperty *) prop;
|
sprop = (JSScopeProperty *) prop;
|
||||||
if (!js_NativeGet(cx, obj, obj2, sprop, vp))
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
if (entryp) {
|
if (entryp) {
|
||||||
JS_ASSERT_NOT_ON_TRACE(cx);
|
JS_ASSERT_NOT_ON_TRACE(cx);
|
||||||
js_FillPropertyCache(cx, aobj, shape, 0, protoIndex, obj2, sprop, entryp);
|
js_FillPropertyCache(cx, aobj, 0, protoIndex, obj2, sprop, false,
|
||||||
|
entryp);
|
||||||
}
|
}
|
||||||
JS_UNLOCK_OBJ(cx, obj2);
|
|
||||||
return JS_TRUE;
|
/* The following unlocks the object. */
|
||||||
|
return js_NativeGet(cx, obj, obj2, sprop, vp);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSBool
|
JSBool
|
||||||
@ -4404,7 +4414,6 @@ JSBool
|
|||||||
js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
|
js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
|
||||||
JSPropCacheEntry **entryp)
|
JSPropCacheEntry **entryp)
|
||||||
{
|
{
|
||||||
uint32 shape;
|
|
||||||
int protoIndex;
|
int protoIndex;
|
||||||
JSObject *pobj;
|
JSObject *pobj;
|
||||||
JSProperty *prop;
|
JSProperty *prop;
|
||||||
@ -4414,6 +4423,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
|
|||||||
intN shortid;
|
intN shortid;
|
||||||
JSClass *clasp;
|
JSClass *clasp;
|
||||||
JSPropertyOp getter, setter;
|
JSPropertyOp getter, setter;
|
||||||
|
JSBool cacheByPrevShape;
|
||||||
|
|
||||||
/* Convert string indices to integers if appropriate. */
|
/* Convert string indices to integers if appropriate. */
|
||||||
CHECK_FOR_STRING_INDEX(id);
|
CHECK_FOR_STRING_INDEX(id);
|
||||||
@ -4427,7 +4437,6 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
|
|||||||
goto read_only_error;
|
goto read_only_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
shape = OBJ_SHAPE(obj);
|
|
||||||
protoIndex = js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
|
protoIndex = js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
|
||||||
&pobj, &prop);
|
&pobj, &prop);
|
||||||
if (protoIndex < 0)
|
if (protoIndex < 0)
|
||||||
@ -4557,6 +4566,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cacheByPrevShape = false;
|
||||||
if (!sprop) {
|
if (!sprop) {
|
||||||
/*
|
/*
|
||||||
* Purge the property cache of now-shadowed id in obj's scope chain.
|
* Purge the property cache of now-shadowed id in obj's scope chain.
|
||||||
@ -4574,7 +4584,8 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
|
|||||||
if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
|
if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
|
||||||
attrs |= JSPROP_SHARED;
|
attrs |= JSPROP_SHARED;
|
||||||
sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
|
sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
|
||||||
SPROP_INVALID_SLOT, attrs, flags, shortid);
|
SPROP_INVALID_SLOT, attrs, flags, shortid,
|
||||||
|
&cacheByPrevShape);
|
||||||
if (!sprop) {
|
if (!sprop) {
|
||||||
JS_UNLOCK_SCOPE(cx, scope);
|
JS_UNLOCK_SCOPE(cx, scope);
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
@ -4595,18 +4606,18 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
|
|||||||
return JS_FALSE);
|
return JS_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!js_NativeSet(cx, obj, sprop, vp))
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
if (entryp) {
|
if (entryp) {
|
||||||
JS_ASSERT_NOT_ON_TRACE(cx);
|
JS_ASSERT_NOT_ON_TRACE(cx);
|
||||||
if (!(attrs & JSPROP_SHARED))
|
if (!(attrs & JSPROP_SHARED)) {
|
||||||
js_FillPropertyCache(cx, obj, shape, 0, 0, obj, sprop, entryp);
|
js_FillPropertyCache(cx, obj, 0, 0, obj, sprop, cacheByPrevShape,
|
||||||
else
|
entryp);
|
||||||
|
} else {
|
||||||
PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
|
PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
JS_UNLOCK_SCOPE(cx, scope);
|
|
||||||
return JS_TRUE;
|
/* The following unlocks the object. */
|
||||||
|
return js_NativeSet(cx, obj, sprop, vp);
|
||||||
|
|
||||||
read_only_error:
|
read_only_error:
|
||||||
return js_ReportValueErrorFlags(cx, flags, JSMSG_READ_ONLY,
|
return js_ReportValueErrorFlags(cx, flags, JSMSG_READ_ONLY,
|
||||||
|
@ -688,15 +688,19 @@ extern JSObject *
|
|||||||
js_FindVariableScope(JSContext *cx, JSFunction **funp);
|
js_FindVariableScope(JSContext *cx, JSFunction **funp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NB: js_NativeGet and js_NativeSet are called with the scope containing sprop
|
* This function must be called with pobj's scope locked. On return that scope
|
||||||
* (pobj's scope for Get, obj's for Set) locked, and on successful return, that
|
* is always unlocked. After this function returns, the caller must not access
|
||||||
* scope is again locked. But on failure, both functions return false with the
|
* pobj or sprop unless it knows that sprop's getter is a stub.
|
||||||
* scope containing sprop unlocked.
|
|
||||||
*/
|
*/
|
||||||
extern JSBool
|
extern JSBool
|
||||||
js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj,
|
js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj,
|
||||||
JSScopeProperty *sprop, jsval *vp);
|
JSScopeProperty *sprop, jsval *vp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function must be called with obj's scope locked. On return that scope
|
||||||
|
* is always unlocked. After this function returns, the caller must not access
|
||||||
|
* sprop unless it knows that sprop's setter is a stub.
|
||||||
|
*/
|
||||||
extern JSBool
|
extern JSBool
|
||||||
js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp);
|
js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp);
|
||||||
|
|
||||||
|
@ -991,7 +991,8 @@ ReportReadOnlyScope(JSContext *cx, JSScope *scope)
|
|||||||
JSScopeProperty *
|
JSScopeProperty *
|
||||||
js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id,
|
js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id,
|
||||||
JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
|
JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
|
||||||
uintN attrs, uintN flags, intN shortid)
|
uintN attrs, uintN flags, intN shortid,
|
||||||
|
JSBool *cacheByPrevShape)
|
||||||
{
|
{
|
||||||
JSScopeProperty **spp, *sprop, *overwriting, **spvec, **spp2, child;
|
JSScopeProperty **spp, *sprop, *overwriting, **spvec, **spp2, child;
|
||||||
uint32 size, splen, i;
|
uint32 size, splen, i;
|
||||||
@ -999,6 +1000,7 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id,
|
|||||||
JSTempValueRooter tvr;
|
JSTempValueRooter tvr;
|
||||||
|
|
||||||
JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));
|
JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));
|
||||||
|
JS_ASSERT_IF(cacheByPrevShape, !*cacheByPrevShape);
|
||||||
CHECK_ANCESTOR_LINE(scope, JS_TRUE);
|
CHECK_ANCESTOR_LINE(scope, JS_TRUE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1272,8 +1274,17 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id,
|
|||||||
* The scope's shape defaults to its last property's shape, but may
|
* The scope's shape defaults to its last property's shape, but may
|
||||||
* be regenerated later as the scope diverges (from the property cache
|
* be regenerated later as the scope diverges (from the property cache
|
||||||
* point of view) from the structural type associated with sprop.
|
* point of view) from the structural type associated with sprop.
|
||||||
|
*
|
||||||
|
* The following is an open-coded version of SCOPE_EXTEND_SHAPE to
|
||||||
|
* set *cacheByPrevShape.
|
||||||
*/
|
*/
|
||||||
SCOPE_EXTEND_SHAPE(cx, scope, sprop);
|
if (!scope->lastProp || scope->shape == scope->lastProp->shape) {
|
||||||
|
scope->shape = sprop->shape;
|
||||||
|
if (cacheByPrevShape)
|
||||||
|
*cacheByPrevShape = true;
|
||||||
|
} else {
|
||||||
|
scope->shape = js_GenerateShape(cx, false, sprop);
|
||||||
|
}
|
||||||
|
|
||||||
/* Store the tree node pointer in the table entry for id. */
|
/* Store the tree node pointer in the table entry for id. */
|
||||||
if (scope->table)
|
if (scope->table)
|
||||||
@ -1409,7 +1420,8 @@ js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope,
|
|||||||
*/
|
*/
|
||||||
newsprop = js_AddScopeProperty(cx, scope, child.id,
|
newsprop = js_AddScopeProperty(cx, scope, child.id,
|
||||||
child.getter, child.setter, child.slot,
|
child.getter, child.setter, child.slot,
|
||||||
child.attrs, child.flags, child.shortid);
|
child.attrs, child.flags, child.shortid,
|
||||||
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newsprop) {
|
if (newsprop) {
|
||||||
|
@ -406,10 +406,16 @@ js_SearchScope(JSScope *scope, jsid id, JSBool adding);
|
|||||||
#define SCOPE_HAS_PROPERTY(scope, sprop) \
|
#define SCOPE_HAS_PROPERTY(scope, sprop) \
|
||||||
(SCOPE_GET_PROPERTY(scope, (sprop)->id) == (sprop))
|
(SCOPE_GET_PROPERTY(scope, (sprop)->id) == (sprop))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If cacheByPrevShape is not null, *cacheByPrevShape must be false on
|
||||||
|
* entrance. On exit it will point to true if this call added the property
|
||||||
|
* predictably and js_FillPropertyCache can optimize for that.
|
||||||
|
*/
|
||||||
extern JSScopeProperty *
|
extern JSScopeProperty *
|
||||||
js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id,
|
js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id,
|
||||||
JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
|
JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
|
||||||
uintN attrs, uintN flags, intN shortid);
|
uintN attrs, uintN flags, intN shortid,
|
||||||
|
JSBool *cacheByPrevShape);
|
||||||
|
|
||||||
extern JSScopeProperty *
|
extern JSScopeProperty *
|
||||||
js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope,
|
js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope,
|
||||||
|
@ -5966,8 +5966,8 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
|
|||||||
ABORT_TRACE("property found on non-native object");
|
ABORT_TRACE("property found on non-native object");
|
||||||
}
|
}
|
||||||
|
|
||||||
js_FillPropertyCache(cx, aobj, OBJ_SHAPE(aobj), 0, protoIndex, obj2,
|
js_FillPropertyCache(cx, aobj, 0, protoIndex, obj2,
|
||||||
(JSScopeProperty*) prop, &entry);
|
(JSScopeProperty*) prop, false, &entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user