зеркало из https://github.com/mozilla/pjs.git
Return of the property cache (365851, r=shaver).
This commit is contained in:
Родитель
7c20b80135
Коммит
611c3c2000
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 *)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
1257
js/src/jsinterp.c
1257
js/src/jsinterp.c
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
321
js/src/jsobj.c
321
js/src/jsobj.c
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче