Return of the property cache (365851, r=shaver).

This commit is contained in:
brendan@mozilla.org 2008-02-07 15:18:45 -08:00
parent ef03cb0f60
commit 88be18b937
28 changed files with 1809 additions and 472 deletions

View File

@ -1571,7 +1571,7 @@ DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
} else {
if (!JS_ValueToId(cx, STRING_TO_JSVAL(str), &id))
return JS_FALSE;
if (js_FindProperty(cx, id, &obj, &obj2, &prop) < 0)
if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
return JS_FALSE;
if (prop) {
OBJ_DROP_PROPERTY(cx, obj2, prop);

View File

@ -2946,8 +2946,10 @@ JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep)
/* Ensure that obj has its own, mutable scope, and seal that scope. */
JS_LOCK_OBJ(cx, obj);
scope = js_GetMutableScope(cx, obj);
if (scope)
if (scope) {
SCOPE_SET_SEALED(scope);
SCOPE_GENERATE_PCTYPE(cx, scope);
}
JS_UNLOCK_OBJ(cx, obj);
if (!scope)
return JS_FALSE;
@ -4010,7 +4012,7 @@ JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp)
*idp = JSVAL_VOID;
} else {
*idp = ida->vector[--i];
OBJ_SET_SLOT(cx, iterobj, JSSLOT_ITER_INDEX, INT_TO_JSVAL(i));
STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_INDEX, INT_TO_JSVAL(i));
}
}
return JS_TRUE;
@ -4567,6 +4569,9 @@ JS_NewScriptObject(JSContext *cx, JSScript *script)
if (obj) {
JS_SetPrivate(cx, obj, script);
script->object = obj;
#ifdef CHECK_SCRIPT_OWNER
script->owner = NULL;
#endif
}
JS_POP_TEMP_ROOT(cx, &tvr);
return obj;

View File

@ -186,6 +186,10 @@ JS_BEGIN_EXTERN_C
note that bit #15 is used internally
to flag interpreted functions */
#define JSFUN_STUB_GSOPS 0x1000 /* use JS_PropertyStub getter/setter
instead of defaulting to class gsops
for property holding function */
#endif
/*
@ -1460,7 +1464,8 @@ struct JSFunctionSpec {
* frame when activated.
*/
#define JS_FN(name,fastcall,minargs,nargs,flags) \
{name, (JSNative)(fastcall), nargs, (flags) | JSFUN_FAST_NATIVE, \
{name, (JSNative)(fastcall), nargs, \
(flags) | JSFUN_FAST_NATIVE | JSFUN_STUB_GSOPS, \
(minargs) << 16}
extern JS_PUBLIC_API(JSObject *)

View File

@ -131,7 +131,7 @@ Boolean(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
*rval = bval;
return JS_TRUE;
}
OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, bval);
STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, bval);
return JS_TRUE;
}
@ -144,7 +144,7 @@ js_InitBooleanClass(JSContext *cx, JSObject *obj)
NULL, boolean_methods, NULL, NULL);
if (!proto)
return NULL;
OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_FALSE);
STOBJ_SET_SLOT(proto, JSSLOT_PRIVATE, JSVAL_FALSE);
return proto;
}

View File

@ -121,9 +121,13 @@ struct JSThread {
* among two or more contexts running script in one thread.
*/
JSGSNCache gsnCache;
/* Property cache for faster call/get/set invocation. */
JSPropertyCache propertyCache;
};
#define JS_GSN_CACHE(cx) ((cx)->thread->gsnCache)
#define JS_GSN_CACHE(cx) ((cx)->thread->gsnCache)
#define JS_PROPERTY_CACHE(cx) ((cx)->thread->propertyCache)
extern void JS_DLL_CALLBACK
js_ThreadDestructorCB(void *ptr);
@ -386,9 +390,27 @@ struct JSRuntime {
*/
JSGSNCache gsnCache;
#define JS_GSN_CACHE(cx) ((cx)->runtime->gsnCache)
/* Property cache for faster call/get/set invocation. */
JSPropertyCache propertyCache;
#define JS_GSN_CACHE(cx) ((cx)->runtime->gsnCache)
#define JS_PROPERTY_CACHE(cx) ((cx)->runtime->propertyCache)
#endif
/*
* Object shape (property cache structural type) identifier generator.
*
* Type 0 stands for the empty scope, and must not be regenerated due to
* uint32 wrap-around. Since we use atomic pre-increment, the initial
* value for the first typed non-empty scope will be 1.
*
* The GC compresses live types, minimizing rt->shapeGen in the process.
* If this counter overflows into SHAPE_OVERFLOW_BIT (in jsinterp.h), the
* GC will disable property caches for all threads, to avoid aliasing two
* different types. Updated by js_GenerateShape (in jsinterp.c).
*/
uint32 shapeGen;
/* Literal table maintained by jsatom.c functions. */
JSAtomState atomState;

View File

@ -617,7 +617,7 @@ BuildSpanDepTable(JSContext *cx, JSCodeGenerator *cg)
op = (JSOp)*pc;
cs = &js_CodeSpec[op];
switch (cs->format & JOF_TYPEMASK) {
switch (JOF_TYPE(cs->format)) {
case JOF_TABLESWITCH:
case JOF_LOOKUPSWITCH:
pc = AddSwitchSpanDeps(cx, cg, pc);
@ -793,7 +793,7 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg)
pivot = sd->offset;
pc = base + top;
op = (JSOp) *pc;
type = (js_CodeSpec[op].format & JOF_TYPEMASK);
type = JOF_OPTYPE(op);
if (JOF_TYPE_IS_EXTENDED_JUMP(type)) {
/*
* We already extended all the jump offset operands for
@ -926,7 +926,7 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg)
if (sd->top != top) {
top = sd->top;
op = (JSOp) base[top];
type = (js_CodeSpec[op].format & JOF_TYPEMASK);
type = JOF_OPTYPE(op);
for (sd2 = sd - 1; sd2 >= sdbase && sd2->top == top; sd2--)
continue;
@ -1109,7 +1109,7 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg)
top = sd->top;
JS_ASSERT(top == sd->before);
op = (JSOp) base[offset];
type = (js_CodeSpec[op].format & JOF_TYPEMASK);
type = JOF_OPTYPE(op);
JS_ASSERT(type == JOF_JUMP ||
type == JOF_JUMPX ||
type == JOF_TABLESWITCH ||
@ -1773,7 +1773,11 @@ EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
{
JSAtomListElement *ale;
JS_ASSERT((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_ATOM);
JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
if (op == JSOP_GETPROP &&
pn->pn_atom == cx->runtime->atomState.lengthAtom) {
return js_Emit1(cx, cg, JSOP_LENGTH) >= 0;
}
ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);
if (!ale)
return JS_FALSE;
@ -1787,7 +1791,7 @@ static JSBool
EmitObjectOp(JSContext *cx, JSParsedObjectBox *pob, JSOp op,
JSCodeGenerator *cg)
{
JS_ASSERT((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_OBJECT);
JS_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT);
return EmitIndexOp(cx, op, IndexParsedObject(pob, &cg->objectList), cg);
}
@ -1809,8 +1813,8 @@ EmitSlotIndexOp(JSContext *cx, JSOp op, uintN slot, uintN index,
ptrdiff_t off;
jsbytecode *pc;
JS_ASSERT((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_SLOTATOM ||
(js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_SLOTOBJECT);
JS_ASSERT(JOF_OPTYPE(op) == JOF_SLOTATOM ||
JOF_OPTYPE(op) == JOF_SLOTOBJECT);
bigSuffix = EmitBigIndexPrefix(cx, cg, index);
if (bigSuffix == JSOP_FALSE)
return JS_FALSE;
@ -1953,9 +1957,9 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
}
/*
* We are optimizing global variables, and there is no pre-existing
* We are optimizing global variables and there may be no pre-existing
* global property named atom. If atom was declared via const or var,
* optimize pn to access fp->vars using the appropriate JOF_QVAR op.
* optimize pn to access fp->vars using the appropriate JSOP_*GVAR op.
*/
ATOM_LIST_SEARCH(ale, &tc->decls, atom);
if (!ale) {
@ -3250,7 +3254,7 @@ MaybeEmitVarDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
atomIndex = ALE_INDEX(ale);
}
if ((js_CodeSpec[pn->pn_op].format & JOF_TYPEMASK) == JOF_ATOM &&
if (JOF_OPTYPE(pn->pn_op) == JOF_ATOM &&
(!(cg->treeContext.flags & TCF_IN_FUNCTION) ||
(cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT))) {
/* Emit a prolog bytecode to predefine the variable. */
@ -3279,7 +3283,7 @@ EmitDestructuringDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
JSOp decltype;
JS_ASSERT(pn->pn_type == TOK_NAME);
decltype = (prologOp == JSOP_NOP) ? (JSOp) LET_DECL : (JSOp) VAR_DECL;
decltype = (JSOp) ((prologOp == JSOP_NOP) ? LET_DECL : VAR_DECL);
if (!BindNameToSlot(cx, cg, pn, decltype))
return JS_FALSE;
@ -5407,14 +5411,19 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
atomIndex);
break;
}
/* FALL THROUGH */
if (js_Emit1(cx, cg, JSOP_DUP) < 0)
return JS_FALSE;
EMIT_INDEX_OP(JSOP_GETXPROP, atomIndex);
break;
case TOK_DOT:
if (js_Emit1(cx, cg, JSOP_DUP) < 0)
return JS_FALSE;
EMIT_INDEX_OP((pn2->pn_type == TOK_NAME)
? JSOP_GETXPROP
: JSOP_GETPROP,
atomIndex);
if (pn2->pn_atom == cx->runtime->atomState.lengthAtom) {
if (js_Emit1(cx, cg, JSOP_LENGTH) < 0)
return JS_FALSE;
} else {
EMIT_INDEX_OP(JSOP_GETPROP, atomIndex);
}
break;
case TOK_LB:
#if JS_HAS_LVALUE_RETURN
@ -5714,7 +5723,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (pn2->pn_slot >= 0) {
if (pn2->pn_const) {
/* Incrementing a declared const: just get its value. */
op = ((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_ATOM)
op = (JOF_OPTYPE(op) == JOF_ATOM)
? JSOP_GETGVAR
: JSOP_GETVAR;
}

View File

@ -338,7 +338,7 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
JS_ASSERT(priv->stackElems + stackDepth == elem);
JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values);
OBJ_SET_SLOT(cx, exnObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(priv));
STOBJ_SET_SLOT(exnObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(priv));
if (report) {
/*
@ -759,7 +759,7 @@ Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
* data so that the finalizer doesn't attempt to free it.
*/
if (OBJ_GET_CLASS(cx, obj) == &js_ErrorClass)
OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, JSVAL_VOID);
STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, JSVAL_VOID);
/* Set the 'message' property. */
if (argc != 0) {
@ -1048,7 +1048,7 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj)
break;
/* So exn_finalize knows whether to destroy private data. */
OBJ_SET_SLOT(cx, protos[i], JSSLOT_PRIVATE, JSVAL_VOID);
STOBJ_SET_SLOT(protos[i], JSSLOT_PRIVATE, JSVAL_VOID);
/* Make a constructor function for the current name. */
atom = cx->runtime->atomState.classAtoms[exceptions[i].key];

View File

@ -1293,6 +1293,9 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
if (xdr->mode == JSXDR_DECODE) {
*objp = fun->object;
#ifdef CHECK_SCRIPT_OWNER
fun->u.i.script->owner = NULL;
#endif
js_CallNewScriptHook(cx, fun->u.i.script, fun);
}
@ -1918,6 +1921,9 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj)
if (!fun->u.i.script)
goto bad;
fun->u.i.script->code[0] = JSOP_STOP;
#ifdef CHECK_SCRIPT_OWNER
fun->u.i.script->owner = NULL;
#endif
return proto;
bad:
@ -2067,13 +2073,15 @@ js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
uintN nargs, uintN attrs)
{
JSFunction *fun;
JSPropertyOp gsop;
fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom);
if (!fun)
return NULL;
gsop = (attrs & JSFUN_STUB_GSOPS) ? JS_PropertyStub : NULL;
if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom),
OBJECT_TO_JSVAL(fun->object),
NULL, NULL,
gsop, gsop,
attrs & ~JSFUN_FLAGS_MASK, NULL)) {
return NULL;
}

View File

@ -2387,9 +2387,8 @@ ProcessSetSlotRequest(JSContext *cx, JSSetSlotRequest *ssr)
}
}
#if 0
/*
* Regenerate property cache type ids for all of the scopes along the
* Regenerate property cache shape ids for all of the scopes along the
* old prototype chain, in case any property cache entries were filled
* by looking up starting from obj.
*/
@ -2398,7 +2397,6 @@ ProcessSetSlotRequest(JSContext *cx, JSSetSlotRequest *ssr)
SCOPE_GENERATE_PCTYPE(cx, scope);
oldproto = STOBJ_GET_PROTO(scope->object);
}
#endif
}
/* Finally, do the deed. */
@ -2613,7 +2611,12 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
/* Reset malloc counter. */
rt->gcMallocBytes = 0;
#if 0
#ifdef JS_DUMP_SCOPE_METERS
{ extern void js_DumpScopeMeters(JSRuntime *rt);
js_DumpScopeMeters(rt);
}
#endif
/*
* Clear property cache weak references and disable the cache so nothing
* can fill it during GC (this is paranoia, since scripts should not run
@ -2621,13 +2624,6 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
*/
js_DisablePropertyCache(cx);
js_FlushPropertyCache(cx);
#endif
#ifdef JS_DUMP_SCOPE_METERS
{ extern void js_DumpScopeMeters(JSRuntime *rt);
js_DumpScopeMeters(rt);
}
#endif
#ifdef JS_THREADSAFE
/*
@ -2648,9 +2644,8 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
continue;
memset(acx->thread->gcFreeLists, 0, sizeof acx->thread->gcFreeLists);
GSN_CACHE_CLEAR(&acx->thread->gsnCache);
#if 0
js_DisablePropertyCache(acx);
js_FlushPropertyCache(acx);
#endif
}
#else
/* The thread-unsafe case just has to clear the runtime's GSN cache. */
@ -2662,10 +2657,8 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
JS_ASSERT(!rt->gcUntracedArenaStackTop);
JS_ASSERT(rt->gcTraceLaterCount == 0);
#if 0
/* Reset the property cache's type id generator so we can compress ids. */
rt->pcTypeGen = 0;
#endif
rt->shapeGen = 0;
/*
* Mark phase.
@ -2898,10 +2891,17 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
goto restart;
}
#if 0
if (!(rt->pcTypeGen & PCTYPE_OVERFLOW_BIT))
if (!(rt->shapeGen & SHAPE_OVERFLOW_BIT)) {
js_EnablePropertyCache(cx);
#ifdef JS_THREADSAFE
iter = NULL;
while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
if (!acx->thread || acx->thread == cx->thread)
continue;
js_EnablePropertyCache(acx);
}
#endif
}
rt->gcLastBytes = rt->gcBytes;
done_running:

View File

@ -122,6 +122,26 @@ js_GetGCStringRuntime(JSString *str);
#define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JSVAL_IS_GCTHING(oldval))
#endif
/*
* Write barrier macro monitoring property update from oldval to newval in
* scope->object.
*
* Since oldval is used only for the branded scope case, and the oldval actual
* argument expression is typically not used otherwise by callers, performance
* benefits if oldval is *not* evaluated into a callsite temporary variable,
* and instead passed to GC_WRITE_BARRIER for conditional evaluation (we rely
* on modern compilers to do a good CSE job). Yay, C macros.
*/
#define GC_WRITE_BARRIER(cx,scope,oldval,newval) \
JS_BEGIN_MACRO \
if (SCOPE_IS_BRANDED(scope) && \
(oldval) != (newval) && \
(VALUE_IS_FUNCTION(cx,oldval) || VALUE_IS_FUNCTION(cx,newval))) { \
SCOPE_GENERATE_PCTYPE(cx, scope); \
} \
GC_POKE(cx, oldval); \
JS_END_MACRO
extern JSBool
js_InitGC(JSRuntime *rt, uint32 maxbytes);

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,5 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=78:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
@ -44,6 +45,7 @@
*/
#include "jsprvtd.h"
#include "jspubtd.h"
#include "jsopcode.h"
JS_BEGIN_EXTERN_C
@ -82,6 +84,9 @@ struct JSStackFrame {
JSStackFrame *dormantNext; /* next dormant frame chain */
JSObject *xmlNamespace; /* null or default xml namespace in E4X */
JSObject *blockChain; /* active compile-time block scopes */
#ifdef DEBUG
jsrefcount pcDisabledSave; /* for balanced property cache control */
#endif
};
typedef struct JSInlineFrame {
@ -112,6 +117,208 @@ typedef struct JSInlineFrame {
#define JSFRAME_SPECIAL (JSFRAME_DEBUGGER | JSFRAME_EVAL)
/*
* Property cache with structurally typed capabilities for invalidation, for
* polymorphic callsite method/get/set speedups.
*
* See bug https://bugzilla.mozilla.org/show_bug.cgi?id=365851.
*/
#define PROPERTY_CACHE_LOG2 12
#define PROPERTY_CACHE_SIZE JS_BIT(PROPERTY_CACHE_LOG2)
#define PROPERTY_CACHE_MASK JS_BITMASK(PROPERTY_CACHE_LOG2)
#define PROPERTY_CACHE_HASH(pc,kshape) \
((((jsuword)(pc) >> PROPERTY_CACHE_LOG2) ^ (jsuword)(pc) ^ (kshape)) & \
PROPERTY_CACHE_MASK)
#define PROPERTY_CACHE_HASH_PC(pc,kshape) \
PROPERTY_CACHE_HASH(pc, kshape)
#define PROPERTY_CACHE_HASH_ATOM(atom,obj,pobj) \
PROPERTY_CACHE_HASH((jsuword)(atom) >> 2, OBJ_SCOPE(obj)->shape)
/*
* Property cache value capability macros.
*/
#define PCVCAP_PROTOBITS 4
#define PCVCAP_PROTOSIZE JS_BIT(PCVCAP_PROTOBITS)
#define PCVCAP_PROTOMASK JS_BITMASK(PCVCAP_PROTOBITS)
#define PCVCAP_SCOPEBITS 4
#define PCVCAP_SCOPESIZE JS_BIT(PCVCAP_SCOPEBITS)
#define PCVCAP_SCOPEMASK JS_BITMASK(PCVCAP_SCOPEBITS)
#define PCVCAP_TAGBITS (PCVCAP_PROTOBITS + PCVCAP_SCOPEBITS)
#define PCVCAP_TAGMASK JS_BITMASK(PCVCAP_TAGBITS)
#define PCVCAP_TAG(t) ((t) & PCVCAP_TAGMASK)
#define PCVCAP_MAKE(t,s,p) (((t) << PCVCAP_TAGBITS) | \
((s) << PCVCAP_PROTOBITS) | \
(p))
#define PCVCAP_PCTYPE(t) ((t) >> PCVCAP_TAGBITS)
#define SHAPE_OVERFLOW_BIT JS_BIT(32 - PCVCAP_TAGBITS)
extern uint32
js_GenerateShape(JSContext *cx);
struct JSPropCacheEntry {
jsbytecode *kpc; /* pc if vcap tag is <= 1, else atom */
jsuword kshape; /* key shape if pc, else obj for atom */
jsuword vcap; /* value capability, see above */
jsuword vword; /* value word, see PCVAL_* below */
};
#if defined DEBUG_brendan || defined DEBUG_brendaneich
#define JS_PROPERTY_CACHE_METERING 1
#endif
typedef struct JSPropertyCache {
JSPropCacheEntry table[PROPERTY_CACHE_SIZE];
JSBool empty;
jsrefcount disabled; /* signed for anti-underflow asserts */
#ifdef JS_PROPERTY_CACHE_METERING
uint32 fills; /* number of cache entry fills */
uint32 nofills; /* couldn't fill (e.g. default get) */
uint32 rofills; /* set on read-only prop can't fill */
uint32 disfills; /* fill attempts on disabled cache */
uint32 oddfills; /* fill attempt after setter deleted */
uint32 modfills; /* fill that rehashed to a new entry */
uint32 brandfills; /* scope brandings to type structural
method fills */
uint32 longchains; /* overlong scope and/or proto chain */
uint32 recycles; /* cache entries recycled by fills */
uint32 pcrecycles; /* pc-keyed entries recycled by atom-
keyed fills */
uint32 tests; /* cache probes */
uint32 pchits; /* fast-path polymorphic op hits */
uint32 protopchits; /* pchits hitting immediate prototype */
uint32 settests; /* cache probes from JOF_SET opcodes */
uint32 addpchits; /* adding next property pchit case */
uint32 setpchits; /* setting existing property pchit */
uint32 setpcmisses; /* setting/adding property pc misses */
uint32 setmisses; /* JSOP_SET{NAME,PROP} total misses */
uint32 idmisses; /* slow-path key id == atom misses */
uint32 komisses; /* slow-path key object misses */
uint32 vcmisses; /* value capability misses */
uint32 misses; /* cache misses */
uint32 flushes; /* cache flushes */
# define PCMETER(x) x
#else
# define PCMETER(x) /* nothing */
#endif
} JSPropertyCache;
/*
* Property cache value tagging/untagging macros.
*/
#define PCVAL_OBJECT 0
#define PCVAL_SLOT 1
#define PCVAL_SPROP 2
#define PCVAL_TAGBITS 2
#define PCVAL_TAGMASK JS_BITMASK(PCVAL_TAGBITS)
#define PCVAL_TAG(v) ((v) & PCVAL_TAGMASK)
#define PCVAL_CLRTAG(v) ((v) & ~(jsuword)PCVAL_TAGMASK)
#define PCVAL_SETTAG(v,t) ((jsuword)(v) | (t))
#define PCVAL_NULL 0
#define PCVAL_IS_NULL(v) ((v) == PCVAL_NULL)
#define PCVAL_IS_OBJECT(v) (PCVAL_TAG(v) == PCVAL_OBJECT)
#define PCVAL_TO_OBJECT(v) ((JSObject *) (v))
#define OBJECT_TO_PCVAL(obj) ((jsuword) (obj))
#define PCVAL_OBJECT_TO_JSVAL(v) OBJECT_TO_JSVAL(PCVAL_TO_OBJECT(v))
#define JSVAL_OBJECT_TO_PCVAL(v) OBJECT_TO_PCVAL(JSVAL_TO_OBJECT(v))
#define PCVAL_IS_SLOT(v) ((v) & PCVAL_SLOT)
#define PCVAL_TO_SLOT(v) ((jsuint)(v) >> 1)
#define SLOT_TO_PCVAL(i) (((jsuword)(i) << 1) | PCVAL_SLOT)
#define PCVAL_IS_SPROP(v) (PCVAL_TAG(v) == PCVAL_SPROP)
#define PCVAL_TO_SPROP(v) ((JSScopeProperty *) PCVAL_CLRTAG(v))
#define SPROP_TO_PCVAL(sprop) PCVAL_SETTAG(sprop, PCVAL_SPROP)
/*
* Fill property cache entry for key cx->fp->pc, optimized value word computed
* from obj and sprop, and entry capability forged from OBJ_SCOPE(obj)->shape,
* scopeIndex, and protoIndex.
*/
extern void
js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
uintN scopeIndex, uintN protoIndex,
JSObject *pobj, JSScopeProperty *sprop,
JSPropCacheEntry **entryp);
/*
* Property cache lookup macros. PROPERTY_CACHE_TEST is designed to inline the
* fast path in js_Interpret, so it makes "just-so" restrictions on parameters,
* e.g. pobj and obj should not be the same variable, since for JOF_PROP-mode
* opcodes, obj must not be changed because of a cache miss.
*
* On return from PROPERTY_CACHE_TEST, if atom is null then obj points to the
* scope chain element in which the property was found, pobj is locked, and
* entry is valid. If atom is non-null then no object is locked but entry is
* still set correctly for use, e.g., by js_FillPropertyCache and atom should
* be used as the id to find.
*
* We must lock pobj on a hit in order to close races with threads that might
* be deleting a property from its scope, or otherwise invalidating property
* caches (on all threads) by re-generating scope->shape.
*/
#define PROPERTY_CACHE_TEST(cx, pc, obj, pobj, entry, atom) \
do { \
JSPropertyCache *cache_ = &JS_PROPERTY_CACHE(cx); \
uint32 kshape_ = OBJ_SCOPE(obj)->shape; \
entry = &cache_->table[PROPERTY_CACHE_HASH_PC(pc, kshape_)]; \
PCMETER(cache_->tests++); \
JS_ASSERT(&obj != &pobj); \
if (entry->kpc == pc && entry->kshape == kshape_) { \
JSObject *tmp_; \
pobj = obj; \
JS_LOCK_OBJ(cx, pobj); \
JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1); \
if (PCVCAP_TAG(entry->vcap) == 1 && \
(tmp_ = LOCKED_OBJ_GET_PROTO(pobj)) != NULL) { \
JS_UNLOCK_OBJ(cx, pobj); \
pobj = tmp_; \
JS_LOCK_OBJ(cx, pobj); \
} \
if (PCVCAP_PCTYPE(entry->vcap) == OBJ_SCOPE(pobj)->shape) { \
PCMETER(cache_->pchits++); \
PCMETER(!PCVCAP_TAG(entry->vcap) || cache_->protopchits++); \
pobj = OBJ_SCOPE(pobj)->object; \
atom = NULL; \
break; \
} \
JS_UNLOCK_OBJ(cx, pobj); \
} \
atom = js_FullTestPropertyCache(cx, pc, &obj, &pobj, &entry); \
if (atom) \
PCMETER(cache_->misses++); \
} while (0)
extern JSAtom *
js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
JSObject **objp, JSObject **pobjp,
JSPropCacheEntry **entryp);
extern void
js_FlushPropertyCache(JSContext *cx);
extern void
js_FlushPropertyCacheForScript(JSContext *cx, JSScript *script);
extern void
js_DisablePropertyCache(JSContext *cx);
extern void
js_EnablePropertyCache(JSContext *cx);
/*
* Interpreter stack arena-pool alloc and free functions.
*/
extern JS_FRIEND_API(jsval *)
js_AllocStack(JSContext *cx, uintN nslots, void **markp);

View File

@ -244,7 +244,7 @@ IteratorNextImpl(JSContext *cx, JSObject *obj, jsval *rval)
if (!ok)
return JS_FALSE;
OBJ_SET_SLOT(cx, obj, JSSLOT_ITER_STATE, state);
STOBJ_SET_SLOT(obj, JSSLOT_ITER_STATE, state);
if (JSVAL_IS_NULL(state))
goto stop;

View File

@ -51,6 +51,7 @@
#include "jscntxt.h"
#include "jsdtoa.h"
#include "jsgc.h"
#include "jsfun.h" /* for VALUE_IS_FUNCTION used by *_WRITE_BARRIER */
#include "jslock.h"
#include "jsscope.h"
#include "jsstr.h"
@ -676,7 +677,7 @@ js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
if (CX_THREAD_IS_RUNNING_GC(cx) ||
(SCOPE_IS_SEALED(scope) && scope->object == obj) ||
(scope->ownercx && ClaimScope(scope, cx))) {
STOBJ_SET_SLOT(obj, slot, v);
LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, v);
return;
}
@ -686,7 +687,7 @@ js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
JS_ASSERT(CURRENT_THREAD_IS_ME(me));
if (js_CompareAndSwap(&tl->owner, 0, me)) {
if (scope == OBJ_SCOPE(obj)) {
STOBJ_SET_SLOT(obj, slot, v);
LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, v);
if (!js_CompareAndSwap(&tl->owner, me, 0)) {
/* Assert that scope locks never revert to flyweight. */
JS_ASSERT(scope->ownercx != cx);
@ -700,20 +701,16 @@ js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
js_Dequeue(tl);
}
else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) {
STOBJ_SET_SLOT(obj, slot, v);
LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, v);
return;
}
#endif
js_LockObj(cx, obj);
STOBJ_SET_SLOT(obj, slot, v);
LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, v);
/*
* Same drill as above, in js_GetSlotThreadSafe. Note that we cannot
* assume obj has its own mutable scope (where scope->object == obj) yet,
* because OBJ_SET_SLOT is called for the "universal", common slots such
* as JSSLOT_PROTO and JSSLOT_PARENT, without a prior js_GetMutableScope.
* See also the JSPROP_SHARED attribute and its usage.
* Same drill as above, in js_GetSlotThreadSafe.
*/
scope = OBJ_SCOPE(obj);
if (scope->ownercx != cx)

View File

@ -111,11 +111,31 @@ typedef struct JSFatLockTable {
* an #include cycle between jslock.h and jsscope.h: moderate-sized XXX here,
* to be fixed by moving JS_LOCK_SCOPE to jsscope.h, JS_LOCK_OBJ to jsobj.h,
* and so on.
*/
*
* We also need jsscope.h #ifdef JS_DEBUG_SCOPE_LOCKS for SET_OBJ_INFO and
* SET_SCOPE_INFO, but we do not want any nested includes that depend on DEBUG.
* Those lead to build bustage when someone makes a change that depends in a
* subtle way on jsscope.h being included directly or indirectly, but does not
* test by building optimized as well as DEBUG.
*/
JS_END_EXTERN_C
#include "jsscope.h"
JS_BEGIN_EXTERN_C
#ifdef JS_DEBUG_SCOPE_LOCKS
#define SET_OBJ_INFO(obj_,file_,line_) \
SET_SCOPE_INFO(OBJ_SCOPE(obj_),file_,line_)
#define SET_SCOPE_INFO(scope_,file_,line_) \
((scope_)->ownercx ? (void)0 : \
(JS_ASSERT((0 < (scope_)->u.count && (scope_)->u.count <= 4) || \
SCOPE_IS_SEALED(scope_)), \
(void)((scope_)->file[(scope_)->u.count-1] = (file_), \
(scope_)->line[(scope_)->u.count-1] = (line_))))
#endif
#define JS_LOCK_RUNTIME(rt) js_LockRuntime(rt)
#define JS_UNLOCK_RUNTIME(rt) js_UnlockRuntime(rt)
@ -126,18 +146,20 @@ JS_BEGIN_EXTERN_C
* are for optimizations above the JSObjectOps layer, under which object locks
* normally hide.
*/
#define JS_LOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->ownercx == (cx)) \
? (void)0 \
: (js_LockObj(cx, obj)))
#define JS_UNLOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->ownercx == (cx)) \
? (void)0 : js_UnlockObj(cx, obj))
#define JS_LOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->ownercx == (cx)) \
? (void)0 \
: (js_LockObj(cx, obj), \
SET_OBJ_INFO(obj,__FILE__,__LINE__)))
#define JS_UNLOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->ownercx == (cx)) \
? (void)0 : js_UnlockObj(cx, obj))
#define JS_LOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 \
: js_LockScope(cx, scope))
#define JS_UNLOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 \
: js_UnlockScope(cx, scope))
#define JS_LOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 \
: (js_LockScope(cx, scope), \
SET_SCOPE_INFO(scope,__FILE__,__LINE__)))
#define JS_UNLOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 \
: js_UnlockScope(cx, scope))
#define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope) \
js_TransferScopeLock(cx, scope, newscope)
js_TransferScopeLock(cx, scope, newscope)
extern void js_LockRuntime(JSRuntime *rt);
extern void js_UnlockRuntime(JSRuntime *rt);
@ -265,6 +287,13 @@ JS_BEGIN_EXTERN_C
#define JS_LOCK(P,CX) JS_LOCK0(P, CX_THINLOCK_ID(CX))
#define JS_UNLOCK(P,CX) JS_UNLOCK0(P, CX_THINLOCK_ID(CX))
#ifndef SET_OBJ_INFO
#define SET_OBJ_INFO(obj,f,l) ((void)0)
#endif
#ifndef SET_SCOPE_INFO
#define SET_SCOPE_INFO(scope,f,l) ((void)0)
#endif
JS_END_EXTERN_C

View File

@ -181,7 +181,7 @@ Number(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
*rval = v;
return JS_TRUE;
}
OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v);
STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, v);
return JS_TRUE;
}
@ -616,7 +616,7 @@ js_InitNumberClass(JSContext *cx, JSObject *obj)
NULL, number_methods, NULL, NULL);
if (!proto || !(ctor = JS_GetConstructor(cx, proto)))
return NULL;
OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_ZERO);
STOBJ_SET_SLOT(proto, JSSLOT_PRIVATE, JSVAL_ZERO);
if (!JS_DefineConstDoubles(cx, ctor, number_constants))
return NULL;

View File

@ -1234,9 +1234,11 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
setCallerVarObj = JS_TRUE;
}
}
/* From here on, control must exit through label out with ok set. */
#endif
/* From here on, control must exit through label out with ok set. */
js_DisablePropertyCache(cx);
/*
* Compile using caller's current scope object.
*
@ -1324,6 +1326,7 @@ out:
caller->varobj = callerVarObj;
#endif
js_EnablePropertyCache(cx);
return ok;
}
@ -2862,6 +2865,41 @@ CheckForStringIndex(jsid id, const jschar *cp, const jschar *end,
return id;
}
static JSBool
PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id)
{
JSScope *scope;
JSScopeProperty *sprop;
while (obj) {
if (!OBJ_IS_NATIVE(obj)) {
obj = OBJ_GET_PROTO(cx, obj);
continue;
}
JS_LOCK_OBJ(cx, obj);
scope = OBJ_SCOPE(obj);
sprop = SCOPE_GET_PROPERTY(scope, id);
if (sprop) {
SCOPE_GENERATE_PCTYPE(cx, scope);
JS_UNLOCK_SCOPE(cx, scope);
return JS_TRUE;
}
obj = LOCKED_OBJ_GET_PROTO(scope->object);
JS_UNLOCK_SCOPE(cx, scope);
}
return JS_FALSE;
}
static void
PurgeScopeChain(JSContext *cx, JSObject *obj, jsid id)
{
PurgeProtoChain(cx, OBJ_GET_PROTO(cx, obj), id);
while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL) {
if (PurgeProtoChain(cx, obj, id))
return;
}
}
JSScopeProperty *
js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
@ -2870,15 +2908,19 @@ js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
JSScope *scope;
JSScopeProperty *sprop;
/*
* Purge the property cache of now-shadowed id in obj's scope chain. Do
* this optimistically (assuming no failure below) before locking obj, so
* we can lock the shadowed scope.
*/
PurgeScopeChain(cx, obj, id);
JS_LOCK_OBJ(cx, obj);
scope = js_GetMutableScope(cx, obj);
if (!scope) {
sprop = NULL;
} else {
/*
* Handle old bug that took empty string as zero index. Also convert
* string indices to integers if appropriate.
*/
/* Convert string indices to integers if appropriate. */
CHECK_FOR_STRING_INDEX(id);
sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs,
flags, shortid);
@ -2944,10 +2986,7 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSScope *scope;
JSScopeProperty *sprop;
/*
* Handle old bug that took empty string as zero index. Also convert
* string indices to integers if appropriate.
*/
/* Convert string indices to integers if appropriate. */
CHECK_FOR_STRING_INDEX(id);
#if JS_HAS_GETTER_SETTER
@ -2997,6 +3036,12 @@ 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.
*/
PurgeScopeChain(cx, obj, id);
/* Lock if object locking is required by this implementation. */
JS_LOCK_OBJ(cx, obj);
@ -3140,13 +3185,10 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
uint32 format;
JSBool ok;
/*
* Handle old bug that took empty string as zero index. Also convert
* string indices to integers if appropriate.
*/
/* Convert string indices to integers if appropriate. */
CHECK_FOR_STRING_INDEX(id);
JS_COUNT_OPERATION(cx, JSOW_LOOKUP_PROPERTY);
/* Search scopes starting with obj and following the prototype link. */
start = obj;
for (protoIndex = 0; ; protoIndex++) {
@ -3227,6 +3269,11 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
JS_UNLOCK_OBJ(cx, obj);
JS_LOCK_OBJ(cx, obj2);
}
protoIndex = 0;
for (proto = start; proto && proto != obj2;
proto = OBJ_GET_PROTO(cx, proto)) {
protoIndex++;
}
scope = OBJ_SCOPE(obj2);
if (!MAP_IS_NATIVE(&scope->map)) {
/* Whoops, newresolve handed back a foreign obj2. */
@ -3308,31 +3355,53 @@ out:
return protoIndex;
}
JS_FRIEND_API(int)
js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
JSProperty **propp)
int
js_FindPropertyHelper(JSContext *cx, jsid id, JSObject **objp,
JSObject **pobjp, JSProperty **propp,
JSPropCacheEntry **entryp)
{
JSRuntime *rt;
JSObject *obj, *pobj, *lastobj;
int scopeIndex;
uint32 type;
int scopeIndex, protoIndex;
JSProperty *prop;
JSScopeProperty *sprop;
rt = cx->runtime;
obj = cx->fp->scopeChain;
scopeIndex = 0;
do {
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
return -1;
type = OBJ_SCOPE(obj)->shape;
for (scopeIndex = 0; ; scopeIndex++) {
if (obj->map->ops->lookupProperty == js_LookupProperty) {
protoIndex =
js_LookupPropertyWithFlags(cx, obj, id, 0, &pobj, &prop);
} else {
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
return -1;
protoIndex = -1;
}
if (prop) {
if (entryp) {
if (protoIndex >= 0 && OBJ_IS_NATIVE(pobj)) {
sprop = (JSScopeProperty *) prop;
js_FillPropertyCache(cx, cx->fp->scopeChain, type,
scopeIndex, protoIndex, pobj, sprop,
entryp);
} else {
PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
*entryp = NULL;
}
}
SCOPE_DEPTH_ACCUM(&rt->scopeSearchDepthStats, scopeIndex);
*objp = obj;
*pobjp = pobj;
*propp = prop;
return scopeIndex;
}
lastobj = obj;
scopeIndex++;
} while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
obj = OBJ_GET_PARENT(cx, obj);
if (!obj)
break;
}
*objp = lastobj;
*pobjp = NULL;
@ -3340,8 +3409,15 @@ js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
return scopeIndex;
}
JS_FRIEND_API(JSBool)
js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
JSProperty **propp)
{
return js_FindPropertyHelper(cx, id, objp, pobjp, propp, NULL) >= 0;
}
JSObject *
js_FindIdentifierBase(JSContext *cx, jsid id)
js_FindIdentifierBase(JSContext *cx, jsid id, JSPropCacheEntry *entry)
{
JSObject *obj, *pobj;
JSProperty *prop;
@ -3350,10 +3426,15 @@ js_FindIdentifierBase(JSContext *cx, jsid id)
* Look for id's property along the "with" statement chain and the
* statically-linked scope chain.
*/
if (js_FindProperty(cx, id, &obj, &pobj, &prop) < 0)
if (js_FindPropertyHelper(cx, id, &obj, &pobj, &prop, &entry) < 0)
return NULL;
if (prop) {
OBJ_DROP_PROPERTY(cx, pobj, prop);
JS_ASSERT(!entry ||
entry->kpc == (PCVCAP_TAG(entry->vcap)
? (jsbytecode *) JSID_TO_ATOM(id)
: cx->fp->pc));
return obj;
}
@ -3379,6 +3460,7 @@ js_FindIdentifierBase(JSContext *cx, jsid id)
return NULL;
}
}
return obj;
}
@ -3470,28 +3552,29 @@ js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp)
(JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) {
set_slot:
GC_POKE(cx, pval);
LOCKED_OBJ_SET_SLOT(obj, slot, *vp);
LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, *vp);
}
return JS_TRUE;
}
JSBool
js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
JSPropCacheEntry **entryp)
{
uint32 type;
int protoIndex;
JSObject *obj2;
JSProperty *prop;
JSScopeProperty *sprop;
/*
* Handle old bug that took empty string as zero index. Also convert
* string indices to integers if appropriate.
*/
/* Convert string indices to integers if appropriate. */
CHECK_FOR_STRING_INDEX(id);
JS_COUNT_OPERATION(cx, JSOW_GET_PROPERTY);
if (!js_LookupProperty(cx, obj, id, &obj2, &prop))
type = OBJ_SCOPE(obj)->shape;
protoIndex = js_LookupPropertyWithFlags(cx, obj, id, 0, &obj2, &prop);
if (protoIndex < 0)
return JS_FALSE;
if (!prop) {
jsbytecode *pc;
@ -3501,6 +3584,11 @@ js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp))
return JS_FALSE;
if (entryp) {
PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
*entryp = NULL;
}
/*
* Give a strict warning if foo.bar is evaluated by a script for an
* object foo with no property named 'bar'.
@ -3553,13 +3641,26 @@ js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
if (!js_NativeGet(cx, obj, obj2, sprop, vp))
return JS_FALSE;
if (entryp) {
js_FillPropertyCache(cx, obj, type, 0, protoIndex, obj2, sprop,
entryp);
}
JS_UNLOCK_OBJ(cx, obj2);
return JS_TRUE;
}
JSBool
js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
return js_GetPropertyHelper(cx, obj, id, vp, NULL);
}
JSBool
js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
JSPropCacheEntry **entryp)
{
uint32 type;
int protoIndex;
JSObject *pobj;
JSProperty *prop;
JSScopeProperty *sprop;
@ -3569,14 +3670,13 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
JSClass *clasp;
JSPropertyOp getter, setter;
/*
* Handle old bug that took empty string as zero index. Also convert
* string indices to integers if appropriate.
*/
/* Convert string indices to integers if appropriate. */
CHECK_FOR_STRING_INDEX(id);
JS_COUNT_OPERATION(cx, JSOW_SET_PROPERTY);
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
type = OBJ_SCOPE(obj)->shape;
protoIndex = js_LookupPropertyWithFlags(cx, obj, id, 0, &pobj, &prop);
if (protoIndex < 0)
return JS_FALSE;
if (prop && !OBJ_IS_NATIVE(pobj)) {
@ -3625,6 +3725,7 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
if (attrs & JSPROP_READONLY) {
if (!JS_HAS_STRICT_OPTION(cx)) {
/* Just return true per ECMA if not in strict mode. */
PCMETER(!entryp || JS_PROPERTY_CACHE(cx).rofills++);
return JS_TRUE;
}
@ -3637,18 +3738,37 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
if (pobj != obj) {
/*
* We found id in a prototype object: prepare to share or shadow.
*
* NB: Thanks to the immutable, garbage-collected property tree
* maintained by jsscope.c in cx->runtime, we needn't worry about
* sprop going away behind our back after we've unlocked scope.
*
* But if we are shadowing (not sharing) the proto-property, then
* we need to regenerate the property cache shape id for scope, in
* case the cache contains the old type in an entry value that was
* filled by a get on obj that delegated up the prototype chain to
* pobj. Once we've shadowed the proto-property, that cache entry
* must not be hit.
*/
if (!(attrs & JSPROP_SHARED))
SCOPE_GENERATE_PCTYPE(cx, scope);
JS_UNLOCK_SCOPE(cx, scope);
/* Don't clone a shared prototype property. */
if (attrs & JSPROP_SHARED) {
if (SPROP_HAS_STUB_SETTER(sprop) &&
!(sprop->attrs & JSPROP_GETTER)) {
if (entryp) {
PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
*entryp = NULL;
}
return JS_TRUE;
}
if (entryp) {
js_FillPropertyCache(cx, obj, type, 0, protoIndex,
pobj, sprop, entryp);
}
return SPROP_SET(cx, sprop, obj, pobj, vp);
}
@ -3687,6 +3807,12 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
goto read_only_error;
}
/*
* Purge the property cache of now-shadowed id in obj's scope chain.
* Do this early, before locking obj to avoid nesting locks.
*/
PurgeScopeChain(cx, obj, id);
/* Find or make a property descriptor with the right heritage. */
JS_LOCK_OBJ(cx, obj);
scope = js_GetMutableScope(cx, obj);
@ -3720,6 +3846,9 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
if (!js_NativeSet(cx, obj, sprop, vp))
return JS_FALSE;
if (entryp)
js_FillPropertyCache(cx, obj, type, 0, 0, obj, sprop, entryp);
JS_UNLOCK_SCOPE(cx, scope);
return JS_TRUE;
@ -3729,6 +3858,12 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
NULL, NULL);
}
JSBool
js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
return js_SetPropertyHelper(cx, obj, id, vp, NULL);
}
JSBool
js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
uintN *attrsp)
@ -3795,13 +3930,10 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
*rval = JSVAL_TRUE;
/*
* Handle old bug that took empty string as zero index. Also convert
* string indices to integers if appropriate.
*/
/* Convert string indices to integers if appropriate. */
CHECK_FOR_STRING_INDEX(id);
JS_COUNT_OPERATION(cx, JSOW_DELETE_PROPERTY);
if (!js_LookupProperty(cx, obj, id, &proto, &prop))
return JS_FALSE;
if (!prop || proto != obj) {
@ -4475,7 +4607,7 @@ js_PrimitiveToObject(JSContext *cx, jsval *vp)
obj = js_NewObject(cx, clasp, NULL, NULL);
if (!obj)
return JS_FALSE;
OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, *vp);
STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, *vp);
*vp = OBJECT_TO_JSVAL(obj);
return JS_TRUE;
}
@ -4747,28 +4879,75 @@ PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
void
js_TraceObject(JSTracer *trc, JSObject *obj)
{
JSScope *scope;
JSContext *cx;
JSScope *scope;
JSBool traceScope;
JSScopeProperty *sprop;
JSClass *clasp;
size_t nslots, i;
jsval v;
JS_ASSERT(OBJ_IS_NATIVE(obj));
cx = trc->context;
scope = OBJ_SCOPE(obj);
traceScope = (scope->object == obj);
if (!traceScope) {
JSObject *pobj = obj;
/*
* Because obj does not own scope, we should be able to assert that an
* object on obj's prototype chain does -- or scope's properties might
* go untraced. It indeed turns out that you can disconnect an object
* from the prototype object whose scope it shares, so we may have to
* mark scope even though scope->object != obj.
*/
while ((pobj = LOCKED_OBJ_GET_PROTO(pobj)) != NULL) {
if (pobj == scope->object)
break;
}
JS_ASSERT_IF(pobj, OBJ_SCOPE(pobj) == scope);
traceScope = !pobj;
}
if (traceScope) {
#ifdef JS_DUMP_SCOPE_METERS
if (scope->object == obj)
MeterEntryCount(scope->entryCount);
#endif
JS_ASSERT(!SCOPE_LAST_PROP(scope) ||
SCOPE_HAS_PROPERTY(scope, SCOPE_LAST_PROP(scope)));
sprop = SCOPE_LAST_PROP(scope);
if (sprop) {
JS_ASSERT(SCOPE_HAS_PROPERTY(scope, sprop));
cx = trc->context;
for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
continue;
TRACE_SCOPE_PROPERTY(trc, sprop);
/* Regenerate property cache shape ids if GC'ing. */
if (IS_GC_MARKING_TRACER(trc)) {
uint32 shape, oldshape;
shape = ++cx->runtime->shapeGen;
JS_ASSERT(shape != 0);
if (!(sprop->flags & SPROP_MARK)) {
oldshape = sprop->shape;
sprop->shape = shape;
sprop->flags |= SPROP_FLAG_SHAPE_REGEN;
if (scope->shape != oldshape) {
shape = ++cx->runtime->shapeGen;
JS_ASSERT(shape != 0);
}
}
scope->shape = shape;
}
/* Trace scope's property tree ancestor line. */
do {
if (SCOPE_HAD_MIDDLE_DELETE(scope) &&
!SCOPE_HAS_PROPERTY(scope, sprop)) {
continue;
}
TRACE_SCOPE_PROPERTY(trc, sprop);
} while ((sprop = sprop->parent) != NULL);
}
}
if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList))
@ -4778,21 +4957,23 @@ js_TraceObject(JSTracer *trc, JSObject *obj)
clasp = LOCKED_OBJ_GET_CLASS(obj);
if (clasp->mark) {
if (clasp->flags & JSCLASS_MARK_IS_TRACE)
((JSTraceOp)(clasp->mark))(trc, obj);
((JSTraceOp) clasp->mark)(trc, obj);
else if (IS_GC_MARKING_TRACER(trc))
(void) clasp->mark(cx, obj, trc);
}
if (scope->object != obj) {
/*
* An unmutated object that shares a prototype's scope. We can't tell
* how many slots are in use in obj by looking at scope, so we use
* STOBJ_NSLOTS(obj).
*/
nslots = STOBJ_NSLOTS(obj);
} else {
nslots = LOCKED_OBJ_NSLOTS(obj);
}
/*
* An unmutated object that shares a prototype object's scope. We can't
* tell how many slots are in use in obj by looking at its scope, so we
* use STOBJ_NSLOTS(obj).
*
* NB: In case clasp->mark mutates something, leave this code here --
* don't move it up and unify it with the |if (!traceScope)| section
* above.
*/
nslots = (scope->object != obj)
? STOBJ_NSLOTS(obj)
: LOCKED_OBJ_NSLOTS(obj);
for (i = 0; i != nslots; ++i) {
v = STOBJ_GET_SLOT(obj, i);

View File

@ -156,12 +156,12 @@ struct JSObject {
#define STOBJ_GET_PROTO(obj) \
JSVAL_TO_OBJECT((obj)->fslots[JSSLOT_PROTO])
#define STOBJ_SET_PROTO(obj,proto) \
#define STOBJ_SET_PROTO(obj,proto) \
((obj)->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto))
#define STOBJ_GET_PARENT(obj) \
JSVAL_TO_OBJECT((obj)->fslots[JSSLOT_PARENT])
#define STOBJ_SET_PARENT(obj,parent) \
#define STOBJ_SET_PARENT(obj,parent) \
((obj)->fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent))
/*
@ -183,16 +183,32 @@ struct JSObject {
/*
* Macros for accessing slots in obj while obj is locked (if thread-safe) and
* when slot must be bound by the map->freeslot.
* when slot must be bounded by the map->freeslot.
*/
#define LOCKED_OBJ_NSLOTS(obj) \
JS_MIN((obj)->map->freeslot, STOBJ_NSLOTS(obj))
#define LOCKED_OBJ_GET_SLOT(obj,slot) \
#define LOCKED_OBJ_GET_SLOT(obj,slot) \
(OBJ_CHECK_SLOT(obj, slot), STOBJ_GET_SLOT(obj, slot))
#define LOCKED_OBJ_SET_SLOT(obj,slot,value) \
#define LOCKED_OBJ_SET_SLOT(obj,slot,value) \
(OBJ_CHECK_SLOT(obj, slot), STOBJ_SET_SLOT(obj, slot, value))
/*
* NB: Don't call LOCKED_OBJ_SET_SLOT or STOBJ_SET_SLOT for a write to a slot
* that may contain a function reference already, or where the new value is a
* function ref, and the object's scope may be branded with a property cache
* structural type capability that distinguishes versions of the object with
* and without the function property. Instead use LOCKED_OBJ_WRITE_BARRIER or
* a fast inline equivalent (JSOP_SETNAME/JSOP_SETPROP cases in jsinterp.c).
*/
#define LOCKED_OBJ_WRITE_BARRIER(cx,obj,slot,newval) \
JS_BEGIN_MACRO \
JSScope *scope_ = OBJ_SCOPE(obj); \
JS_ASSERT(scope_->object == (obj)); \
GC_WRITE_BARRIER(cx, scope_, LOCKED_OBJ_GET_SLOT(obj, slot), newval); \
LOCKED_OBJ_SET_SLOT(obj, slot, newval); \
JS_END_MACRO
#define LOCKED_OBJ_GET_PROTO(obj) \
(OBJ_CHECK_SLOT(obj, JSSLOT_PROTO), STOBJ_GET_PROTO(obj))
#define LOCKED_OBJ_SET_PROTO(obj,proto) \
@ -219,10 +235,13 @@ struct JSObject {
: js_GetSlotThreadSafe(cx, obj, slot))
#define OBJ_SET_SLOT(cx,obj,slot,value) \
(OBJ_CHECK_SLOT(obj, slot), \
(OBJ_IS_NATIVE(obj) && OBJ_SCOPE(obj)->ownercx == cx) \
? (void) LOCKED_OBJ_SET_SLOT(obj, slot, value) \
: js_SetSlotThreadSafe(cx, obj, slot, value))
JS_BEGIN_MACRO \
OBJ_CHECK_SLOT(obj, slot); \
if (OBJ_IS_NATIVE(obj) && OBJ_SCOPE(obj)->ownercx == cx) \
LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, value); \
else \
js_SetSlotThreadSafe(cx, obj, slot, value); \
JS_END_MACRO
/*
* If thread-safe, define an OBJ_GET_SLOT wrapper that bypasses, for a native
@ -245,7 +264,8 @@ struct JSObject {
#else /* !JS_THREADSAFE */
#define OBJ_GET_SLOT(cx,obj,slot) LOCKED_OBJ_GET_SLOT(obj,slot)
#define OBJ_SET_SLOT(cx,obj,slot,value) LOCKED_OBJ_SET_SLOT(obj,slot,value)
#define OBJ_SET_SLOT(cx,obj,slot,value) LOCKED_OBJ_WRITE_BARRIER(cx,obj,slot, \
value)
#endif /* !JS_THREADSAFE */
@ -253,12 +273,12 @@ struct JSObject {
#define OBJ_GET_PROTO(cx,obj) \
STOBJ_GET_PROTO(obj)
#define OBJ_SET_PROTO(cx,obj,proto) \
OBJ_SET_SLOT(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto))
STOBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto))
#define OBJ_GET_PARENT(cx,obj) \
STOBJ_GET_PARENT(obj)
#define OBJ_SET_PARENT(cx,obj,parent) \
OBJ_SET_SLOT(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent))
STOBJ_SET_SLOT(obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent))
/*
* Class is invariant and comes from the fixed JSSLOT_CLASS. Thus no locking
@ -269,8 +289,8 @@ struct JSObject {
/* Test whether a map or object is native. */
#define MAP_IS_NATIVE(map) \
((map)->ops == &js_ObjectOps || \
((map)->ops && (map)->ops->newObjectMap == js_ObjectOps.newObjectMap))
JS_LIKELY((map)->ops == &js_ObjectOps || \
(map)->ops->newObjectMap == js_ObjectOps.newObjectMap)
#define OBJ_IS_NATIVE(obj) MAP_IS_NATIVE((obj)->map)
@ -298,12 +318,12 @@ extern JSClass js_BlockClass;
*/
#define JSSLOT_BLOCK_DEPTH (JSSLOT_PRIVATE + 1)
#define OBJ_BLOCK_COUNT(cx,obj) \
#define OBJ_BLOCK_COUNT(cx,obj) \
((obj)->map->freeslot - (JSSLOT_BLOCK_DEPTH + 1))
#define OBJ_BLOCK_DEPTH(cx,obj) \
JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_BLOCK_DEPTH))
#define OBJ_SET_BLOCK_DEPTH(cx,obj,depth) \
OBJ_SET_SLOT(cx, obj, JSSLOT_BLOCK_DEPTH, INT_TO_JSVAL(depth))
#define OBJ_BLOCK_DEPTH(cx,obj) \
JSVAL_TO_INT(STOBJ_GET_SLOT(obj, JSSLOT_BLOCK_DEPTH))
#define OBJ_SET_BLOCK_DEPTH(cx,obj,depth) \
STOBJ_SET_SLOT(obj, JSSLOT_BLOCK_DEPTH, INT_TO_JSVAL(depth))
/*
* To make sure this slot is well-defined, always call js_NewWithObject to
@ -486,16 +506,21 @@ extern int
js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
JSObject **objp, JSProperty **propp);
extern int
js_FindPropertyHelper(JSContext *cx, jsid id, JSObject **objp,
JSObject **pobjp, JSProperty **propp,
JSPropCacheEntry **entryp);
/*
* Return the index along the scope chain in which id was found, or the last
* index if not found, or -1 on error.
*/
extern JS_FRIEND_API(int)
extern JS_FRIEND_API(JSBool)
js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
JSProperty **propp);
extern JSObject *
js_FindIdentifierBase(JSContext *cx, jsid id);
js_FindIdentifierBase(JSContext *cx, jsid id, JSPropCacheEntry *entry);
extern JSObject *
js_FindVariableScope(JSContext *cx, JSFunction **funp);
@ -513,9 +538,17 @@ js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj,
extern JSBool
js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp);
extern JSBool
js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
JSPropCacheEntry **entryp);
extern JSBool
js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
extern JSBool
js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
JSPropCacheEntry **entryp);
extern JSBool
js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp);

View File

@ -116,7 +116,7 @@ GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
{
uint32 type;
type = (js_CodeSpec[*pc].format & JOF_TYPEMASK);
type = JOF_OPTYPE(*pc);
if (JOF_TYPE_IS_EXTENDED_JUMP(type))
return GET_JUMPX_OFFSET(pc2);
return GET_JUMP_OFFSET(pc2);
@ -236,7 +236,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc,
if (lines)
fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc));
fprintf(fp, " %s", CodeName[op]);
type = cs->format & JOF_TYPEMASK;
type = JOF_TYPE(cs->format);
switch (type) {
case JOF_BYTE:
if (op == JSOP_TRAP) {
@ -1392,10 +1392,15 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc)
}
break;
case JSOP_LENGTH:
atom = cx->runtime->atomState.lengthAtom;
goto do_destructure_atom;
case JSOP_CALLPROP:
case JSOP_GETPROP:
*OFF2STR(&ss->sprinter, head) = '{';
GET_ATOM_FROM_BYTECODE(jp->script, pc, 0, atom);
do_destructure_atom:
*OFF2STR(&ss->sprinter, head) = '{';
str = ATOM_TO_STRING(atom);
#if JS_HAS_DESTRUCTURING_SHORTHAND
nameoff = ss->sprinter.offset;
@ -1761,7 +1766,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* JSOP_GETARG or JSOP_GETVAR appropriately, instead of to
* JSOP_NAME.
*/
type = format & JOF_TYPEMASK;
type = JOF_TYPE(format);
op = (type == JOF_QARG)
? JSOP_GETARG
: (type == JOF_QVAR)
@ -1835,7 +1840,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
}
}
LOCAL_ASSERT(js_CodeSpec[saveop].length == oplen ||
(format & JOF_TYPEMASK) == JOF_SLOTATOM);
JOF_TYPE(format) == JOF_SLOTATOM);
jp->dvgfence = NULL;
}
@ -3509,6 +3514,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
}
break;
case JSOP_LENGTH:
fmt = dot_format;
rval = js_length_str;
goto do_getprop_lval;
case JSOP_GETPROP2:
op = JSOP_GETPROP;
(void) PopOff(ss, lastop);
@ -3521,6 +3531,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
do_getprop:
GET_QUOTE_AND_FMT(index_format, dot_format, rval);
do_getprop_lval:
lval = POP_STR();
todo = Sprint(&ss->sprinter, fmt, lval, rval);
break;
@ -5011,7 +5022,7 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
}
}
type = cs->format & JOF_TYPEMASK;
type = JOF_TYPE(cs->format);
switch (type) {
case JOF_TABLESWITCH:
case JOF_TABLESWITCHX:

View File

@ -124,6 +124,10 @@ typedef enum JSOpLength {
to root intermediate objects */
#define JOF_TMPSLOT_SHIFT 23
/* Shorthands for type from format and type from opcode. */
#define JOF_TYPE(fmt) ((fmt) & JOF_TYPEMASK)
#define JOF_OPTYPE(op) JOF_TYPE(js_CodeSpec[op].format)
/* Shorthands for mode from format and mode from opcode. */
#define JOF_MODE(fmt) ((fmt) & JOF_MODEMASK)
#define JOF_OPMODE(op) JOF_MODE(js_CodeSpec[op].format)

View File

@ -117,7 +117,7 @@ OPDEF(JSOP_FORVAR, 11, "forvar", NULL, 3, 0, 1, 19, JOF_QVAR|J
/* More longstanding bytecodes. */
OPDEF(JSOP_DUP, 12, "dup", NULL, 1, 1, 2, 0, JOF_BYTE)
OPDEF(JSOP_DUP2, 13, "dup2", NULL, 1, 2, 4, 0, JOF_BYTE)
OPDEF(JSOP_SETCONST, 14, "setconst", NULL, 3, 1, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET|JOF_ASSIGNING)
OPDEF(JSOP_SETCONST, 14, "setconst", NULL, 3, 1, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET)
OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC)
@ -157,9 +157,9 @@ OPDEF(JSOP_NAMEDEC, 50, "namedec", NULL, 3, 0, 1, 15, JOF_ATOM|J
OPDEF(JSOP_PROPDEC, 51, "propdec", NULL, 3, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_DEC|JOF_POST|JOF_TMPSLOT)
OPDEF(JSOP_ELEMDEC, 52, "elemdec", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_POST|JOF_TMPSLOT)
OPDEF(JSOP_GETPROP, 53, "getprop", NULL, 3, 1, 1, 18, JOF_ATOM|JOF_PROP)
OPDEF(JSOP_SETPROP, 54, "setprop", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_ASSIGNING|JOF_DETECTING)
OPDEF(JSOP_SETPROP, 54, "setprop", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_GETELEM, 55, "getelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC)
OPDEF(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_ASSIGNING|JOF_DETECTING)
OPDEF(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_CALLNAME, 57, "callname", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE)
OPDEF(JSOP_NAME, 59, "name", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME)
@ -206,9 +206,9 @@ OPDEF(JSOP_TRAP, 83, "trap", NULL, 1, 0, 0, 0, JOF_BYTE)
/* Fast get/set ops for function arguments and local variables. */
OPDEF(JSOP_GETARG, 84, "getarg", NULL, 3, 0, 1, 19, JOF_QARG |JOF_NAME)
OPDEF(JSOP_SETARG, 85, "setarg", NULL, 3, 1, 1, 3, JOF_QARG |JOF_NAME|JOF_SET|JOF_ASSIGNING)
OPDEF(JSOP_SETARG, 85, "setarg", NULL, 3, 1, 1, 3, JOF_QARG |JOF_NAME|JOF_SET)
OPDEF(JSOP_GETVAR, 86, "getvar", NULL, 3, 0, 1, 19, JOF_QVAR |JOF_NAME)
OPDEF(JSOP_SETVAR, 87, "setvar", NULL, 3, 1, 1, 3, JOF_QVAR |JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING)
OPDEF(JSOP_SETVAR, 87, "setvar", NULL, 3, 1, 1, 3, JOF_QVAR |JOF_NAME|JOF_SET|JOF_DETECTING)
/* Push unsigned 16-bit int constant. */
OPDEF(JSOP_UINT16, 88, "uint16", NULL, 3, 0, 1, 16, JOF_UINT16)
@ -234,7 +234,7 @@ OPDEF(JSOP_VARDEC, 102,"vardec", NULL, 3, 0, 1, 15, JOF_QVAR |
/*
* Initialize for-in iterator. See also JSOP_FOREACH and JSOP_FOREACHKEYVAL.
*/
OPDEF(JSOP_FORIN, 103,"forin", NULL, 1, 1, 1, 0, JOF_BYTE)
OPDEF(JSOP_FORIN, 103,"forin", NULL, 1, 1, 1, 0, JOF_BYTE)
/* ECMA-compliant for/in ops. */
OPDEF(JSOP_FORNAME, 104,"forname", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME|JOF_FOR)
@ -243,8 +243,8 @@ OPDEF(JSOP_FORELEM, 106,"forelem", NULL, 1, 1, 3, 18, JOF_BYTE |
OPDEF(JSOP_POPN, 107,"popn", NULL, 3, -1, 0, 0, JOF_UINT16)
/* ECMA-compliant assignment ops. */
OPDEF(JSOP_BINDNAME, 108,"bindname", NULL, 3, 0, 1, 0, JOF_ATOM|JOF_NAME|JOF_SET|JOF_ASSIGNING)
OPDEF(JSOP_SETNAME, 109,"setname", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING)
OPDEF(JSOP_BINDNAME, 108,"bindname", NULL, 3, 0, 1, 0, JOF_ATOM|JOF_NAME|JOF_SET)
OPDEF(JSOP_SETNAME, 109,"setname", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING)
/* Exception handling ops. */
OPDEF(JSOP_THROW, 110,js_throw_str, NULL, 1, 1, 0, 0, JOF_BYTE)
@ -283,7 +283,7 @@ OPDEF(JSOP_EVAL, 121,"eval", NULL, 3, -1, 1, 18, JOF_UINT16
/*
* ECMA-compliant helper for 'for (x[i] in o)' loops.
*/
OPDEF(JSOP_ENUMELEM, 122,"enumelem", NULL, 1, 3, 0, 3, JOF_BYTE |JOF_SET|JOF_ASSIGNING)
OPDEF(JSOP_ENUMELEM, 122,"enumelem", NULL, 1, 3, 0, 3, JOF_BYTE |JOF_SET)
/*
* Getter and setter prefix bytecodes. These modify the next bytecode, either
@ -319,7 +319,7 @@ OPDEF(JSOP_GROUP, 131, "group", NULL, 1, 0, 0, 19, JOF_BYTE)
* Host object extension: given 'o.item(i) = j', the left-hand side compiles
* JSOP_SETCALL, rather than JSOP_CALL.
*/
OPDEF(JSOP_SETCALL, 132, "setcall", NULL, 3, -1, 2, 18, JOF_UINT16|JOF_SET|JOF_ASSIGNING)
OPDEF(JSOP_SETCALL, 132, "setcall", NULL, 3, -1, 2, 18, JOF_UINT16|JOF_SET)
/*
* Exception handling no-ops, for more economical byte-coding than SRC_TRYFIN
@ -373,7 +373,7 @@ OPDEF(JSOP_RETRVAL, 153,"retrval", NULL, 1, 0, 0, 0, JOF_BYTE)
/* Optimized global variable ops (we don't bother doing a JSOP_FORGVAR op). */
OPDEF(JSOP_GETGVAR, 154,"getgvar", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME)
OPDEF(JSOP_SETGVAR, 155,"setgvar", NULL, 3, 1, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING)
OPDEF(JSOP_SETGVAR, 155,"setgvar", NULL, 3, 1, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_INCGVAR, 156,"incgvar", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC)
OPDEF(JSOP_DECGVAR, 157,"decgvar", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC)
OPDEF(JSOP_GVARINC, 158,"gvarinc", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST)
@ -392,8 +392,8 @@ OPDEF(JSOP_TOATTRNAME, 166,"toattrname", NULL, 1, 1, 1, 19, JOF_BYTE|J
OPDEF(JSOP_TOATTRVAL, 167,"toattrval", NULL, 1, 1, 1, 19, JOF_BYTE)
OPDEF(JSOP_ADDATTRNAME, 168,"addattrname",NULL, 1, 2, 1, 13, JOF_BYTE)
OPDEF(JSOP_ADDATTRVAL, 169,"addattrval", NULL, 1, 2, 1, 13, JOF_BYTE)
OPDEF(JSOP_BINDXMLNAME, 170,"bindxmlname",NULL, 1, 1, 2, 3, JOF_BYTE|JOF_SET|JOF_ASSIGNING)
OPDEF(JSOP_SETXMLNAME, 171,"setxmlname", NULL, 1, 3, 1, 3, JOF_BYTE|JOF_SET|JOF_ASSIGNING|JOF_DETECTING)
OPDEF(JSOP_BINDXMLNAME, 170,"bindxmlname",NULL, 1, 1, 2, 3, JOF_BYTE|JOF_SET)
OPDEF(JSOP_SETXMLNAME, 171,"setxmlname", NULL, 1, 3, 1, 3, JOF_BYTE|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_XMLNAME, 172,"xmlname", NULL, 1, 1, 1, 19, JOF_BYTE)
OPDEF(JSOP_DESCENDANTS, 173,"descendants",NULL, 1, 2, 1, 18, JOF_BYTE)
OPDEF(JSOP_FILTER, 174,"filter", NULL, 3, 1, 1, 0, JOF_JUMP)
@ -432,7 +432,7 @@ OPDEF(JSOP_RESETBASE0, 191,"resetbase0", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_STARTXML, 192,"startxml", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_STARTXMLEXPR, 193,"startxmlexpr",NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_CALLELEM, 194, "callelem", NULL, 1, 2, 2, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC|JOF_CALLOP)
OPDEF(JSOP_CALLELEM, 194, "callelem", NULL, 1, 2, 2, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC|JOF_CALLOP)
/*
* Stop interpretation, emitted at end of script to save the threaded bytecode
@ -480,7 +480,7 @@ OPDEF(JSOP_FOREACHKEYVAL, 213,"foreachkeyval",NULL, 1, 1, 1, 0, JOF_BYTE)
/*
* Variant of JSOP_ENUMELEM for destructuring const (const [a, b] = ...).
*/
OPDEF(JSOP_ENUMCONSTELEM, 214,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|JOF_SET|JOF_ASSIGNING)
OPDEF(JSOP_ENUMCONSTELEM, 214,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|JOF_SET)
/*
* Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals,
@ -514,3 +514,8 @@ OPDEF(JSOP_CALLLOCAL, 226, "calllocal", NULL, 3, 0, 2, 19, JOF_LOCAL|
*/
OPDEF(JSOP_INT8, 227, "int8", NULL, 2, 0, 1, 16, JOF_INT8)
OPDEF(JSOP_INT32, 228, "int32", NULL, 5, 0, 1, 16, JOF_INT32)
/*
* Get the value of the 'length' property from a stacked object.
*/
OPDEF(JSOP_LENGTH, 229, "length", NULL, 1, 1, 1, 18, JOF_BYTE|JOF_PROP)

View File

@ -93,6 +93,7 @@ typedef struct JSGenerator JSGenerator;
typedef struct JSParseContext JSParseContext;
typedef struct JSParsedObjectBox JSParsedObjectBox;
typedef struct JSParseNode JSParseNode;
typedef struct JSPropCacheEntry JSPropCacheEntry;
typedef struct JSSharpObjectMap JSSharpObjectMap;
typedef struct JSTempValueRooter JSTempValueRooter;
typedef struct JSThread JSThread;

View File

@ -541,9 +541,9 @@ typedef void
JSProperty *prop);
/*
* Function type for JSObjectOps.setProto and JSObjectOps.setParent. These
* hooks must check for cycles without deadlocking, and otherwise take special
* steps. See jsobj.c, js_SetProtoOrParent, for an example.
* Function pointer type for JSObjectOps.setProto and JSObjectOps.setParent.
* These hooks must check for cycles without deadlocking, and otherwise take
* special steps. See jsobj.c and jsgc.c for details.
*/
typedef JSBool
(* JS_DLL_CALLBACK JSSetObjectSlotOp)(JSContext *cx, JSObject *obj,

View File

@ -92,6 +92,7 @@ js_GetMutableScope(JSContext *cx, JSObject *obj)
static void
InitMinimalScope(JSScope *scope)
{
scope->shape = 0;
scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2;
scope->entryCount = scope->removedCount = 0;
scope->table = NULL;
@ -161,7 +162,7 @@ js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp,
*/
scope->u.link = NULL;
#ifdef DEBUG
#ifdef JS_DEBUG_SCOPE_LOCKS
scope->file[0] = scope->file[1] = scope->file[2] = scope->file[3] = NULL;
scope->line[0] = scope->line[1] = scope->line[2] = scope->line[3] = 0;
#endif
@ -390,7 +391,7 @@ ChangeScope(JSContext *cx, JSScope *scope, int change)
/*
* Take care to exclude the mark bits in case we're called from the GC.
*/
#define SPROP_FLAGS_NOT_MATCHED SPROP_MARK
#define SPROP_FLAGS_NOT_MATCHED (SPROP_MARK | SPROP_FLAG_SHAPE_REGEN)
JS_STATIC_DLL_CALLBACK(JSDHashNumber)
js_HashScopeProperty(JSDHashTable *table, const void *key)
@ -903,6 +904,8 @@ locked_not_found:
sprop->flags = child->flags;
sprop->shortid = child->shortid;
sprop->parent = sprop->kids = NULL;
sprop->shape = js_GenerateShape(cx);
if (!parent) {
entry->child = sprop;
} else {
@ -1099,6 +1102,7 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id,
}
SCOPE_SET_MIDDLE_DELETE(scope);
}
SCOPE_GENERATE_PCTYPE(cx, scope);
/*
* If we fail later on trying to find or create a new sprop, we will
@ -1269,6 +1273,16 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id,
if (!sprop)
goto fail_overwrite;
/*
* The scope's shape defaults to its last property's shape, but may
* be regenerated later as the scope diverges (from the property cache
* point of view) from the structural type associated with sprop.
*/
if (!scope->lastProp || scope->shape == scope->lastProp->shape)
scope->shape = sprop->shape;
else
SCOPE_GENERATE_PCTYPE(cx, scope);
/* Store the tree node pointer in the table entry for id. */
if (scope->table)
SPROP_STORE_PRESERVING_COLLISION(spp, sprop);
@ -1402,8 +1416,14 @@ js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope,
child.attrs, child.flags, child.shortid);
}
if (newsprop) {
if (scope->shape == sprop->shape)
scope->shape = newsprop->shape;
else
SCOPE_GENERATE_PCTYPE(cx, scope);
}
#ifdef JS_DUMP_PROPTREE_STATS
if (!newsprop)
else
METER(changeFailures);
#endif
return newsprop;
@ -1470,6 +1490,7 @@ js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id)
} else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) {
SCOPE_SET_MIDDLE_DELETE(scope);
}
SCOPE_GENERATE_PCTYPE(cx, scope);
CHECK_ANCESTOR_LINE(scope, JS_TRUE);
/* Last, consider shrinking scope's table if its load factor is <= .25. */
@ -1707,9 +1728,22 @@ js_SweepScopeProperties(JSContext *cx)
if (sprop->id == JSVAL_NULL)
continue;
/* If the mark bit is set, sprop is alive, so we skip it. */
/*
* If the mark bit is set, sprop is alive, so clear the mark bit
* and continue the while loop.
*
* Regenerate sprop->shape if it hasn't already been refreshed
* during the mark phase, when live scopes' lastProp members are
* followed to update both scope->shape and lastProp->shape.
*/
if (sprop->flags & SPROP_MARK) {
sprop->flags &= ~SPROP_MARK;
if (sprop->flags & SPROP_FLAG_SHAPE_REGEN) {
sprop->flags &= ~SPROP_FLAG_SHAPE_REGEN;
} else {
sprop->shape = ++cx->runtime->shapeGen;
JS_ASSERT(sprop->shape != 0);
}
liveCount++;
continue;
}

View File

@ -44,14 +44,11 @@
* JS symbol tables.
*/
#include "jstypes.h"
#include "jslock.h"
#include "jsobj.h"
#include "jsprvtd.h"
#include "jspubtd.h"
#ifdef JS_THREADSAFE
# include "jslock.h"
#endif
JS_BEGIN_EXTERN_C
/*
@ -201,6 +198,7 @@ JS_BEGIN_EXTERN_C
struct JSScope {
JSObjectMap map; /* base class state */
JSObject *object; /* object that owns this scope */
uint32 shape; /* property cache shape identifier */
uint8 flags; /* flags, see below */
int8 hashShift; /* multiplicative hash shift */
uint16 spare; /* reserved */
@ -215,7 +213,7 @@ struct JSScope {
jsrefcount count; /* lock entry count for reentrancy */
JSScope *link; /* next link in rt->scopeSharingTodo */
} u;
#ifdef DEBUG
#ifdef JS_DEBUG_SCOPE_LOCKS
const char *file[4]; /* file where lock was (re-)taken */
unsigned int line[4]; /* line where lock was (re-)taken */
#endif
@ -223,6 +221,7 @@ struct JSScope {
};
#define OBJ_SCOPE(obj) ((JSScope *)(obj)->map)
#define SCOPE_GENERATE_PCTYPE(cx,scope) ((scope)->shape = js_GenerateShape(cx))
/* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */
#define SCOPE_CAPACITY(scope) JS_BIT(JS_DHASH_BITS-(scope)->hashShift)
@ -230,6 +229,7 @@ struct JSScope {
/* Scope flags and some macros to hide them from other files than jsscope.c. */
#define SCOPE_MIDDLE_DELETE 0x0001
#define SCOPE_SEALED 0x0002
#define SCOPE_BRANDED 0x0004
#define SCOPE_HAD_MIDDLE_DELETE(scope) ((scope)->flags & SCOPE_MIDDLE_DELETE)
#define SCOPE_SET_MIDDLE_DELETE(scope) ((scope)->flags |= SCOPE_MIDDLE_DELETE)
@ -242,9 +242,18 @@ struct JSScope {
* Don't define this, it can't be done safely because JS_LOCK_OBJ will avoid
* taking the lock if the object owns its scope and the scope is sealed.
*/
#define SCOPE_CLR_SEALED(scope) ((scope)->flags &= ~SCOPE_SEALED)
#undef SCOPE_CLR_SEALED(scope) ((scope)->flags &= ~SCOPE_SEALED)
#endif
/*
* A branded scope's object contains plain old methods (function-valued
* properties without magic getters and setters), and its scope->shape
* evolves whenever a function value changes.
*/
#define SCOPE_IS_BRANDED(scope) ((scope)->flags & SCOPE_BRANDED)
#define SCOPE_SET_BRANDED(scope) ((scope)->flags |= SCOPE_BRANDED)
#define SCOPE_CLR_BRANDED(scope) ((scope)->flags &= ~SCOPE_BRANDED)
/*
* A little information hiding for scope->lastProp, in case it ever becomes
* a tagged pointer again.
@ -264,6 +273,7 @@ struct JSScopeProperty {
JSScopeProperty *parent; /* parent node, reverse for..in order */
JSScopeProperty *kids; /* null, single child, or a tagged ptr
to many-kids data structure */
uint32 shape; /* property cache shape identifier */
};
/* JSScopeProperty pointer tag bit indicating a collision. */
@ -290,6 +300,7 @@ struct JSScopeProperty {
#define SPROP_MARK 0x01
#define SPROP_IS_ALIAS 0x02
#define SPROP_HAS_SHORTID 0x04
#define SPROP_FLAG_SHAPE_REGEN 0x08
/*
* If SPROP_HAS_SHORTID is set in sprop->flags, we use sprop->shortid rather

View File

@ -1390,6 +1390,9 @@ js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
nsrcnotes * sizeof(jssrcnote) ==
(uint8 *)script + size);
#ifdef CHECK_SCRIPT_OWNER
script->owner = cx->thread;
#endif
return script;
}
@ -1454,6 +1457,9 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
js_FreezeLocalNames(cx, fun);
fun->u.i.script = script;
#ifdef CHECK_SCRIPT_OWNER
script->owner = NULL;
#endif
if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)
fun->flags |= JSFUN_HEAVYWEIGHT;
if (fun->flags & JSFUN_HEAVYWEIGHT)
@ -1502,12 +1508,40 @@ void
js_DestroyScript(JSContext *cx, JSScript *script)
{
js_CallDestroyScriptHook(cx, script);
JS_ClearScriptTraps(cx, script);
if (script->principals)
JSPRINCIPALS_DROP(cx, script->principals);
if (JS_GSN_CACHE(cx).script == script)
JS_CLEAR_GSN_CACHE(cx);
/*
* The GC flushes all property caches, so no need to purge just the
* entries for this script.
*
* JS_THREADSAFE note: js_FlushPropertyCacheForScript flushes only the
* current thread's property cache, so a script not owned by a function
* or object, which hands off lifetime management for that script to the
* GC, must be used by only one thread over its lifetime.
*
* This should be an API-compatible change, since a script is never safe
* against premature GC if shared among threads without a rooted object
* wrapping it to protect the script's mapped atoms against GC. We use
* script->owner to enforce this requirement via assertions.
*/
#ifdef CHECK_SCRIPT_OWNER
JS_ASSERT_IF(cx->runtime->gcRunning, !script->owner);
#endif
if (!cx->runtime->gcRunning &&
!(cx->fp && (cx->fp->flags & JSFRAME_EVAL))) {
#ifdef CHECK_SCRIPT_OWNER
JS_ASSERT(script->owner == cx->thread);
#endif
js_FlushPropertyCacheForScript(cx, script);
}
JS_free(cx, script);
}

View File

@ -1,4 +1,3 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=78:
*
@ -84,6 +83,10 @@ typedef struct JSObjectArray {
#define JS_OBJECT_ARRAY_SIZE(length) \
(offsetof(JSObjectArray, vector) + sizeof(JSObject *) * (length))
#if defined DEBUG && defined JS_THREADSAFE
# define CHECK_SCRIPT_OWNER 1
#endif
struct JSScript {
jsbytecode *code; /* bytecodes and their immediate operands */
uint32 length; /* length of code vector */
@ -103,6 +106,9 @@ struct JSScript {
uintN depth; /* maximum stack depth in slots */
JSPrincipals *principals;/* principals for this script */
JSObject *object; /* optional Script-class object wrapper */
#ifdef CHECK_SCRIPT_OWNER
JSThread *owner; /* for thread-safe life-cycle assertions */
#endif
};
/* No need to store script->notes now that it is allocated right after code. */

View File

@ -2294,7 +2294,7 @@ String(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
*rval = STRING_TO_JSVAL(str);
return JS_TRUE;
}
OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str));
STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str));
return JS_TRUE;
}
@ -2467,8 +2467,8 @@ js_InitStringClass(JSContext *cx, JSObject *obj)
NULL, string_static_methods);
if (!proto)
return NULL;
OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE,
STRING_TO_JSVAL(cx->runtime->emptyString));
STOBJ_SET_SLOT(proto, JSSLOT_PRIVATE,
STRING_TO_JSVAL(cx->runtime->emptyString));
return proto;
}