зеркало из https://github.com/mozilla/gecko-dev.git
bug 545529 - caching Class.prototype lookups. r=brendan
This commit is contained in:
Родитель
8a7d5bb4db
Коммит
06f21330ba
|
@ -1142,8 +1142,7 @@ js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj)
|
|||
}
|
||||
|
||||
/* Initialize the function class first so constructors can be made. */
|
||||
if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Function),
|
||||
&fun_proto)) {
|
||||
if (!js_GetClassPrototype(cx, obj, JSProto_Function, &fun_proto)) {
|
||||
fun_proto = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1164,8 +1163,7 @@ js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj)
|
|||
}
|
||||
|
||||
/* Initialize the object class next so Object.prototype works. */
|
||||
if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object),
|
||||
&obj_proto)) {
|
||||
if (!js_GetClassPrototype(cx, obj, JSProto_Object, &obj_proto)) {
|
||||
fun_proto = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -4132,6 +4130,14 @@ JS_PUBLIC_API(JSObject *)
|
|||
JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
if (!parent) {
|
||||
if (cx->fp)
|
||||
parent = js_GetScopeChain(cx, cx->fp);
|
||||
if (!parent)
|
||||
parent = cx->globalObject;
|
||||
JS_ASSERT(parent);
|
||||
}
|
||||
|
||||
if (OBJ_GET_CLASS(cx, funobj) != &js_FunctionClass) {
|
||||
/*
|
||||
* We cannot clone this object, so fail (we used to return funobj, bad
|
||||
|
@ -4143,7 +4149,7 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
|
|||
}
|
||||
|
||||
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
|
||||
JSObject *clone = js_CloneFunctionObject(cx, fun, parent);
|
||||
JSObject *clone = CloneFunctionObject(cx, fun, parent);
|
||||
if (!clone)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -683,6 +683,24 @@ DumpFunctionMeter(JSContext *cx)
|
|||
# define DUMP_FUNCTION_METER(cx) ((void) 0)
|
||||
#endif
|
||||
|
||||
#ifdef JS_PROTO_CACHE_METERING
|
||||
static void
|
||||
DumpProtoCacheMeter(JSContext *cx)
|
||||
{
|
||||
JSClassProtoCache::Stats *stats = &cx->runtime->classProtoCacheStats;
|
||||
FILE *fp = fopen("/tmp/protocache.stats", "a");
|
||||
fprintf(fp,
|
||||
"hit ratio %g%%\n",
|
||||
double(stats->hit) * 100.0 / double(stats->probe));
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
# define DUMP_PROTO_CACHE_METER(cx) DumpProtoCacheMeter(cx)
|
||||
#else
|
||||
# define DUMP_PROTO_CACHE_METER(cx) ((void) 0)
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
||||
{
|
||||
|
@ -789,6 +807,7 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
|||
js_GC(cx, GC_LAST_CONTEXT);
|
||||
DUMP_EVAL_CACHE_METER(cx);
|
||||
DUMP_FUNCTION_METER(cx);
|
||||
DUMP_PROTO_CACHE_METER(cx);
|
||||
|
||||
/* Take the runtime down, now that it has no contexts or atoms. */
|
||||
JS_LOCK_GC(rt);
|
||||
|
@ -1949,3 +1968,26 @@ JSContext::isConstructing()
|
|||
JSStackFrame *fp = js_GetTopStackFrame(this);
|
||||
return fp && (fp->flags & JSFRAME_CONSTRUCTING);
|
||||
}
|
||||
|
||||
/*
|
||||
* Release pool's arenas if the stackPool has existed for longer than the
|
||||
* limit specified by gcEmptyArenaPoolLifespan.
|
||||
*/
|
||||
inline void
|
||||
FreeOldArenas(JSRuntime *rt, JSArenaPool *pool)
|
||||
{
|
||||
JSArena *a = pool->current;
|
||||
if (a == pool->first.next && a->avail == a->base + sizeof(int64)) {
|
||||
int64 age = JS_Now() - *(int64 *) a->base;
|
||||
if (age > int64(rt->gcEmptyArenaPoolLifespan) * 1000)
|
||||
JS_FreeArenaPool(pool);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
JSContext::purge()
|
||||
{
|
||||
FreeOldArenas(runtime, &stackPool);
|
||||
FreeOldArenas(runtime, ®expPool);
|
||||
classProtoCache.purge();
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@
|
|||
/*
|
||||
* JS execution context.
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
#include "jsarena.h" /* Added by JSIFY */
|
||||
#include "jsclist.h"
|
||||
#include "jslong.h"
|
||||
|
@ -678,6 +680,35 @@ struct JSSetSlotRequest {
|
|||
JSSetSlotRequest *next; /* next request in GC worklist */
|
||||
};
|
||||
|
||||
#define JS_PROTO_CACHE_METERING
|
||||
|
||||
/* Caching Class.prototype lookups for the standard classes. */
|
||||
struct JSClassProtoCache {
|
||||
void purge() { memset(entries, 0, sizeof(entries)); }
|
||||
|
||||
#ifdef JS_PROTO_CACHE_METERING
|
||||
struct Stats {
|
||||
int32 probe, hit;
|
||||
};
|
||||
# define PROTO_CACHE_METER(cx, x) \
|
||||
((void) (PR_ATOMIC_INCREMENT(&(cx)->runtime->classProtoCacheStats.x)))
|
||||
#else
|
||||
# define PROTO_CACHE_METER(cx, x) ((void) 0)
|
||||
#endif
|
||||
|
||||
private:
|
||||
struct GlobalAndProto {
|
||||
JSObject *global;
|
||||
JSObject *proto;
|
||||
};
|
||||
|
||||
GlobalAndProto entries[JSProto_LIMIT - JSProto_Object];
|
||||
|
||||
friend JSBool js_GetClassPrototype(JSContext *cx, JSObject *scope,
|
||||
JSProtoKey protoKey, JSObject **protop,
|
||||
JSClass *clasp);
|
||||
};
|
||||
|
||||
struct JSRuntime {
|
||||
/* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */
|
||||
JSRuntimeState state;
|
||||
|
@ -1028,6 +1059,10 @@ struct JSRuntime {
|
|||
char lastScriptFilename[1024];
|
||||
#endif
|
||||
|
||||
#ifdef JS_PROTO_CACHE_METERING
|
||||
JSClassProtoCache::Stats classProtoCacheStats;
|
||||
#endif
|
||||
|
||||
JSRuntime();
|
||||
~JSRuntime();
|
||||
|
||||
|
@ -1220,8 +1255,7 @@ extern const JSDebugHooks js_NullDebugHooks; /* defined in jsdbgapi.cpp */
|
|||
* Wraps a stack frame which has been temporarily popped from its call stack
|
||||
* and needs to be GC-reachable. See JSContext::{push,pop}GCReachableFrame.
|
||||
*/
|
||||
struct JSGCReachableFrame
|
||||
{
|
||||
struct JSGCReachableFrame {
|
||||
JSGCReachableFrame *next;
|
||||
JSStackFrame *frame;
|
||||
};
|
||||
|
@ -1464,6 +1498,8 @@ struct JSContext
|
|||
bool jitEnabled;
|
||||
#endif
|
||||
|
||||
JSClassProtoCache classProtoCache;
|
||||
|
||||
/* Caller must be holding runtime->gcLock. */
|
||||
void updateJITEnabled() {
|
||||
#ifdef JS_TRACER
|
||||
|
@ -1622,6 +1658,8 @@ struct JSContext
|
|||
|
||||
bool isConstructing();
|
||||
|
||||
void purge();
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
|
|
|
@ -995,10 +995,8 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj)
|
|||
* See the equivalent code to ensure that parent_proto is non-null when
|
||||
* JS_InitClass calls js_NewObject, in jsapi.c.
|
||||
*/
|
||||
if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object),
|
||||
&obj_proto)) {
|
||||
if (!js_GetClassPrototype(cx, obj, JSProto_Object, &obj_proto))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(roots, 0, sizeof(roots));
|
||||
JSAutoTempValueRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots);
|
||||
|
@ -1167,8 +1165,7 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp,
|
|||
* exception constructor name in the scope chain of the current context's
|
||||
* top stack frame, or in the global object if no frame is active.
|
||||
*/
|
||||
ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(GetExceptionProtoKey(exn)),
|
||||
&errProto);
|
||||
ok = js_GetClassPrototype(cx, NULL, GetExceptionProtoKey(exn), &errProto);
|
||||
if (!ok)
|
||||
goto out;
|
||||
tv[0] = OBJECT_TO_JSVAL(errProto);
|
||||
|
|
|
@ -2448,20 +2448,28 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
|
|||
}
|
||||
|
||||
JSObject * JS_FASTCALL
|
||||
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, JSObject *proto)
|
||||
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
|
||||
JSObject *proto)
|
||||
{
|
||||
JS_ASSERT(parent);
|
||||
JS_ASSERT(proto);
|
||||
|
||||
/*
|
||||
* The cloned function object does not need the extra JSFunction members
|
||||
* beyond JSObject as it points to fun via the private slot.
|
||||
*/
|
||||
JSObject *clone = js_NewObject(cx, &js_FunctionClass, proto, parent, sizeof(JSObject));
|
||||
JSObject *clone = js_NewObjectWithGivenProto(cx, &js_FunctionClass, proto,
|
||||
parent, sizeof(JSObject));
|
||||
if (!clone)
|
||||
return NULL;
|
||||
clone->setPrivate(fun);
|
||||
return clone;
|
||||
}
|
||||
|
||||
JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CloneFunctionObject, CONTEXT, FUNCTION, OBJECT, OBJECT, 0, 0)
|
||||
#ifdef JS_TRACER
|
||||
JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CloneFunctionObject,
|
||||
CONTEXT, FUNCTION, OBJECT, OBJECT, 0, 0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Create a new flat closure, but don't initialize the imported upvar
|
||||
|
@ -2476,7 +2484,7 @@ js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
|
|||
? fun->u.i.script->upvars()->length
|
||||
: 0) == fun->u.i.nupvars);
|
||||
|
||||
JSObject *closure = js_CloneFunctionObject(cx, fun, scopeChain);
|
||||
JSObject *closure = CloneFunctionObject(cx, fun, scopeChain);
|
||||
if (!closure)
|
||||
return closure;
|
||||
|
||||
|
|
|
@ -284,7 +284,19 @@ extern void
|
|||
js_FinalizeFunction(JSContext *cx, JSFunction *fun);
|
||||
|
||||
extern JSObject * JS_FASTCALL
|
||||
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, JSObject *proto = NULL);
|
||||
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
|
||||
JSObject *proto);
|
||||
|
||||
inline JSObject *
|
||||
CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent)
|
||||
{
|
||||
JS_ASSERT(parent);
|
||||
JSObject *proto;
|
||||
if (!js_GetClassPrototype(cx, parent, JSProto_Function, &proto))
|
||||
return NULL;
|
||||
JS_ASSERT(proto);
|
||||
return js_CloneFunctionObject(cx, fun, parent, proto);
|
||||
}
|
||||
|
||||
extern JS_REQUIRES_STACK JSObject *
|
||||
js_NewFlatClosure(JSContext *cx, JSFunction *fun);
|
||||
|
|
|
@ -2379,34 +2379,6 @@ js_TraceContext(JSTracer *trc, JSContext *acx)
|
|||
JSStackHeader *sh;
|
||||
JSTempValueRooter *tvr;
|
||||
|
||||
if (IS_GC_MARKING_TRACER(trc)) {
|
||||
|
||||
#define FREE_OLD_ARENAS(pool) \
|
||||
JS_BEGIN_MACRO \
|
||||
int64 _age; \
|
||||
JSArena * _a = (pool).current; \
|
||||
if (_a == (pool).first.next && \
|
||||
_a->avail == _a->base + sizeof(int64)) { \
|
||||
_age = JS_Now() - *(int64 *) _a->base; \
|
||||
if (_age > (int64) acx->runtime->gcEmptyArenaPoolLifespan * \
|
||||
1000) \
|
||||
JS_FreeArenaPool(&(pool)); \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
/*
|
||||
* Release the stackPool's arenas if the stackPool has existed for
|
||||
* longer than the limit specified by gcEmptyArenaPoolLifespan.
|
||||
*/
|
||||
FREE_OLD_ARENAS(acx->stackPool);
|
||||
|
||||
/*
|
||||
* Release the regexpPool's arenas based on the same criterion as for
|
||||
* the stackPool.
|
||||
*/
|
||||
FREE_OLD_ARENAS(acx->regexpPool);
|
||||
}
|
||||
|
||||
/*
|
||||
* Trace active and suspended callstacks.
|
||||
*
|
||||
|
@ -3140,6 +3112,12 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
|||
}
|
||||
|
||||
js_PurgeThreads(cx);
|
||||
{
|
||||
JSContext *iter = NULL, *acx;
|
||||
while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL)
|
||||
acx->purge();
|
||||
}
|
||||
|
||||
|
||||
#ifdef JS_TRACER
|
||||
if (gckind == GC_LAST_CONTEXT) {
|
||||
|
|
|
@ -291,7 +291,7 @@ js_ThrowStopIteration(JSContext *cx)
|
|||
jsval v;
|
||||
|
||||
JS_ASSERT(!JS_IsExceptionPending(cx));
|
||||
if (js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_StopIteration), &v))
|
||||
if (js_FindClassObject(cx, NULL, JSProto_StopIteration, &v))
|
||||
JS_SetPendingException(cx, v);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
|
128
js/src/jsobj.cpp
128
js/src/jsobj.cpp
|
@ -2061,7 +2061,7 @@ obj_keys(JSContext *cx, uintN argc, jsval *vp)
|
|||
return JS_FALSE;
|
||||
|
||||
JSObject *proto;
|
||||
if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_Array), &proto))
|
||||
if (!js_GetClassPrototype(cx, NULL, JSProto_Array, &proto))
|
||||
return JS_FALSE;
|
||||
vp[1] = OBJECT_TO_JSVAL(proto);
|
||||
|
||||
|
@ -2960,21 +2960,28 @@ out:
|
|||
return obj;
|
||||
}
|
||||
|
||||
inline JSProtoKey
|
||||
GetClassProtoKey(JSClass *clasp)
|
||||
{
|
||||
JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
|
||||
if (key != JSProto_Null)
|
||||
return key;
|
||||
if (clasp->flags & JSCLASS_IS_ANONYMOUS)
|
||||
return JSProto_Object;
|
||||
return JSProto_Null;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto,
|
||||
JSObject *parent, size_t objectSize)
|
||||
{
|
||||
jsid id;
|
||||
|
||||
/* Bootstrap the ur-object, and make it the default prototype object. */
|
||||
if (!proto) {
|
||||
if (!js_GetClassId(cx, clasp, &id))
|
||||
return NULL;
|
||||
if (!js_GetClassPrototype(cx, parent, id, &proto))
|
||||
JSProtoKey protoKey = GetClassProtoKey(clasp);
|
||||
if (!js_GetClassPrototype(cx, parent, protoKey, &proto, clasp))
|
||||
return NULL;
|
||||
if (!proto &&
|
||||
!js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object),
|
||||
&proto)) {
|
||||
!js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -3104,7 +3111,7 @@ js_NewInstance(JSContext *cx, JSClass *clasp, JSObject *ctor)
|
|||
} else {
|
||||
/* Primitive value in .prototype means we use Object.prototype for proto. */
|
||||
if (!js_GetClassPrototype(cx, JSVAL_TO_OBJECT(ctor->fslots[JSSLOT_PARENT]),
|
||||
INT_TO_JSID(JSProto_Object), &proto)) {
|
||||
JSProto_Object, &proto)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -3721,8 +3728,7 @@ js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
|
|||
key = JSCLASS_CACHED_PROTO_KEY(clasp);
|
||||
if (key != JSProto_Null &&
|
||||
!parent_proto &&
|
||||
!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object),
|
||||
&parent_proto)) {
|
||||
!js_GetClassPrototype(cx, obj, JSProto_Object, &parent_proto)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -3961,26 +3967,6 @@ js_EnsureReservedSlots(JSContext *cx, JSObject *obj, size_t nreserved)
|
|||
return true;
|
||||
}
|
||||
|
||||
extern JSBool
|
||||
js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp)
|
||||
{
|
||||
JSProtoKey key;
|
||||
JSAtom *atom;
|
||||
|
||||
key = JSCLASS_CACHED_PROTO_KEY(clasp);
|
||||
if (key != JSProto_Null) {
|
||||
*idp = INT_TO_JSID(key);
|
||||
} else if (clasp->flags & JSCLASS_IS_ANONYMOUS) {
|
||||
*idp = INT_TO_JSID(JSProto_Object);
|
||||
} else {
|
||||
atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
|
||||
if (!atom)
|
||||
return JS_FALSE;
|
||||
*idp = ATOM_TO_JSID(atom);
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JS_BEGIN_EXTERN_C
|
||||
|
||||
static JSObject *
|
||||
|
@ -4068,11 +4054,12 @@ js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj)
|
|||
}
|
||||
|
||||
JSBool
|
||||
js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp)
|
||||
js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey,
|
||||
jsval *vp, JSClass *clasp)
|
||||
{
|
||||
JSStackFrame *fp;
|
||||
JSObject *obj, *cobj, *pobj;
|
||||
JSProtoKey key;
|
||||
jsid id;
|
||||
JSProperty *prop;
|
||||
jsval v;
|
||||
JSScopeProperty *sprop;
|
||||
|
@ -4104,16 +4091,21 @@ js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp)
|
|||
if (!obj)
|
||||
return JS_FALSE;
|
||||
|
||||
if (JSID_IS_INT(id)) {
|
||||
key = (JSProtoKey) JSID_TO_INT(id);
|
||||
JS_ASSERT(key != JSProto_Null);
|
||||
if (!js_GetClassObject(cx, obj, key, &cobj))
|
||||
if (protoKey != JSProto_Null) {
|
||||
JS_ASSERT(JSProto_Null < protoKey);
|
||||
JS_ASSERT(protoKey < JSProto_LIMIT);
|
||||
if (!js_GetClassObject(cx, obj, protoKey, &cobj))
|
||||
return JS_FALSE;
|
||||
if (cobj) {
|
||||
*vp = OBJECT_TO_JSVAL(cobj);
|
||||
return JS_TRUE;
|
||||
}
|
||||
id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
|
||||
id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[protoKey]);
|
||||
} else {
|
||||
JSAtom *atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
|
||||
if (!atom)
|
||||
return false;
|
||||
id = ATOM_TO_JSID(atom);
|
||||
}
|
||||
|
||||
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
||||
|
@ -4141,16 +4133,14 @@ JSObject *
|
|||
js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
|
||||
JSObject *parent, uintN argc, jsval *argv)
|
||||
{
|
||||
jsid id;
|
||||
jsval cval, rval;
|
||||
JSObject *obj, *ctor;
|
||||
|
||||
JSAutoTempValueRooter argtvr(cx, argc, argv);
|
||||
|
||||
if (!js_GetClassId(cx, clasp, &id) ||
|
||||
!js_FindClassObject(cx, parent, id, &cval)) {
|
||||
JSProtoKey protoKey = GetClassProtoKey(clasp);
|
||||
if (!js_FindClassObject(cx, parent, protoKey, &cval, clasp))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (JSVAL_IS_PRIMITIVE(cval)) {
|
||||
js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
|
||||
|
@ -6191,16 +6181,48 @@ js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
|
|||
}
|
||||
|
||||
JSBool
|
||||
js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id,
|
||||
JSObject **protop)
|
||||
js_GetClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey,
|
||||
JSObject **protop, JSClass *clasp)
|
||||
{
|
||||
jsval v;
|
||||
JSObject *ctor;
|
||||
VOUCH_DOES_NOT_REQUIRE_STACK();
|
||||
JS_ASSERT(JSProto_Null <= protoKey);
|
||||
JS_ASSERT(protoKey < JSProto_LIMIT);
|
||||
|
||||
if (!js_FindClassObject(cx, scope, id, &v))
|
||||
/* Query cache. */
|
||||
JSClassProtoCache::GlobalAndProto *cacheEntry = NULL;
|
||||
if (protoKey != JSProto_Null) {
|
||||
if (!scope) {
|
||||
if (cx->fp)
|
||||
scope = cx->fp->scopeChain;
|
||||
if (!scope) {
|
||||
scope = cx->globalObject;
|
||||
if (!scope) {
|
||||
*protop = NULL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (JSObject *tmp = scope->getParent())
|
||||
scope = tmp;
|
||||
|
||||
JS_STATIC_ASSERT(JSProto_Null == 0);
|
||||
JS_STATIC_ASSERT(JSProto_Object == 1);
|
||||
cacheEntry = &cx->classProtoCache.entries[protoKey - JSProto_Object];
|
||||
|
||||
PROTO_CACHE_METER(cx, probe);
|
||||
if (cacheEntry->global == scope) {
|
||||
JS_ASSERT(cacheEntry->proto);
|
||||
PROTO_CACHE_METER(cx, hit);
|
||||
*protop = cacheEntry->proto;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
jsval v;
|
||||
if (!js_FindClassObject(cx, scope, protoKey, &v, clasp))
|
||||
return JS_FALSE;
|
||||
if (VALUE_IS_FUNCTION(cx, v)) {
|
||||
ctor = JSVAL_TO_OBJECT(v);
|
||||
JSObject *ctor = JSVAL_TO_OBJECT(v);
|
||||
if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &v))
|
||||
return JS_FALSE;
|
||||
if (!JSVAL_IS_PRIMITIVE(v)) {
|
||||
|
@ -6215,6 +6237,10 @@ js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id,
|
|||
*/
|
||||
cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] =
|
||||
JSVAL_TO_OBJECT(v);
|
||||
if (cacheEntry) {
|
||||
cacheEntry->global = scope;
|
||||
cacheEntry->proto = JSVAL_TO_OBJECT(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
*protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
|
||||
|
@ -6397,7 +6423,6 @@ js_XDRObject(JSXDRState *xdr, JSObject **objp)
|
|||
JSClass *clasp;
|
||||
uint32 classId, classDef;
|
||||
JSProtoKey protoKey;
|
||||
jsid classKey;
|
||||
JSObject *proto;
|
||||
|
||||
cx = xdr->cx;
|
||||
|
@ -6443,10 +6468,7 @@ js_XDRObject(JSXDRState *xdr, JSObject **objp)
|
|||
if (classDef) {
|
||||
/* NB: we know that JSProto_Null is 0 here, for backward compat. */
|
||||
protoKey = (JSProtoKey) (classDef >> 1);
|
||||
classKey = (protoKey != JSProto_Null)
|
||||
? INT_TO_JSID(protoKey)
|
||||
: ATOM_TO_JSID(atom);
|
||||
if (!js_GetClassPrototype(cx, NULL, classKey, &proto))
|
||||
if (!js_GetClassPrototype(cx, NULL, protoKey, &proto, clasp))
|
||||
return JS_FALSE;
|
||||
clasp = OBJ_GET_CLASS(cx, proto);
|
||||
if (!JS_XDRRegisterClass(xdr, clasp, &classId))
|
||||
|
|
|
@ -716,9 +716,6 @@ extern const char js_defineSetter_str[];
|
|||
extern const char js_lookupGetter_str[];
|
||||
extern const char js_lookupSetter_str[];
|
||||
|
||||
extern JSBool
|
||||
js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp);
|
||||
|
||||
extern JSObject *
|
||||
js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto,
|
||||
JSObject *parent, size_t objectSize = 0);
|
||||
|
@ -756,8 +753,13 @@ js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
|
|||
extern JSBool
|
||||
js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj);
|
||||
|
||||
/*
|
||||
* If protoKey is not JSProto_Null, then clasp is ignored. If protoKey is
|
||||
* JSProto_Null, clasp must non-null.
|
||||
*/
|
||||
extern JSBool
|
||||
js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp);
|
||||
js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey key, jsval *vp,
|
||||
JSClass *clasp = NULL);
|
||||
|
||||
extern JSObject *
|
||||
js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
|
||||
|
@ -1015,9 +1017,13 @@ js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj,
|
|||
extern JSBool
|
||||
js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);
|
||||
|
||||
/*
|
||||
* If protoKey is not JSProto_Null, then clasp is ignored. If protoKey is
|
||||
* JSProto_Null, clasp must non-null.
|
||||
*/
|
||||
extern JSBool
|
||||
js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id,
|
||||
JSObject **protop);
|
||||
js_GetClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey,
|
||||
JSObject **protop, JSClass *clasp = NULL);
|
||||
|
||||
extern JSBool
|
||||
js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
|
||||
|
|
|
@ -1596,19 +1596,19 @@ BEGIN_CASE(JSOP_CALLPROP)
|
|||
if (!JSVAL_IS_PRIMITIVE(lval)) {
|
||||
obj = JSVAL_TO_OBJECT(lval);
|
||||
} else {
|
||||
JSProtoKey protoKey;
|
||||
if (JSVAL_IS_STRING(lval)) {
|
||||
i = JSProto_String;
|
||||
protoKey = JSProto_String;
|
||||
} else if (JSVAL_IS_NUMBER(lval)) {
|
||||
i = JSProto_Number;
|
||||
protoKey = JSProto_Number;
|
||||
} else if (JSVAL_IS_BOOLEAN(lval)) {
|
||||
i = JSProto_Boolean;
|
||||
protoKey = JSProto_Boolean;
|
||||
} else {
|
||||
JS_ASSERT(JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval));
|
||||
js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj))
|
||||
if (!js_GetClassPrototype(cx, NULL, protoKey, &obj))
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
@ -2441,22 +2441,26 @@ BEGIN_CASE(JSOP_OBJECT)
|
|||
PUSH_OPND(OBJECT_TO_JSVAL(obj));
|
||||
END_CASE(JSOP_OBJECT)
|
||||
|
||||
BEGIN_CASE(JSOP_REGEXP)
|
||||
BEGIN_CASE(JSOP_REGEXP) {
|
||||
/*
|
||||
* Push a regexp object cloned from the regexp literal object mapped by the
|
||||
* bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was
|
||||
* flouted by many browser-based implementations.
|
||||
*
|
||||
* XXX This code is specific to regular expression objects. If we need a
|
||||
* similar op for other kinds of object literals, we should push cloning
|
||||
* down under JSObjectOps and reuse code here.
|
||||
* We avoid the js_GetScopeChain call here and pass fp->scopeChain as
|
||||
* js_GetClassPrototype uses the latter only to locate the global.
|
||||
*/
|
||||
index = GET_FULL_INDEX(0);
|
||||
obj = js_CloneRegExpObject(cx, script->getRegExp(index));
|
||||
JSObject *proto;
|
||||
if (!js_GetClassPrototype(cx, fp->scopeChain, JSProto_RegExp, &proto))
|
||||
goto error;
|
||||
JS_ASSERT(proto);
|
||||
obj = js_CloneRegExpObject(cx, script->getRegExp(index), proto);
|
||||
if (!obj)
|
||||
goto error;
|
||||
rval = OBJECT_TO_JSVAL(obj);
|
||||
PUSH_OPND(rval);
|
||||
}
|
||||
END_CASE(JSOP_REGEXP)
|
||||
|
||||
BEGIN_CASE(JSOP_ZERO)
|
||||
|
@ -2942,7 +2946,7 @@ BEGIN_CASE(JSOP_DEFFUN)
|
|||
* requests in server-side JS.
|
||||
*/
|
||||
if (OBJ_GET_PARENT(cx, obj) != obj2) {
|
||||
obj = js_CloneFunctionObject(cx, fun, obj2);
|
||||
obj = CloneFunctionObject(cx, fun, obj2);
|
||||
if (!obj)
|
||||
goto error;
|
||||
}
|
||||
|
@ -3105,7 +3109,7 @@ BEGIN_CASE(JSOP_DEFLOCALFUN)
|
|||
obj = FUN_OBJECT(fun);
|
||||
|
||||
if (FUN_NULL_CLOSURE(fun)) {
|
||||
obj = js_CloneFunctionObject(cx, fun, fp->scopeChain);
|
||||
obj = CloneFunctionObject(cx, fun, fp->scopeChain);
|
||||
if (!obj)
|
||||
goto error;
|
||||
} else {
|
||||
|
@ -3118,7 +3122,7 @@ BEGIN_CASE(JSOP_DEFLOCALFUN)
|
|||
if (TRACE_RECORDER(cx))
|
||||
AbortRecording(cx, "DEFLOCALFUN for closure");
|
||||
#endif
|
||||
obj = js_CloneFunctionObject(cx, fun, parent);
|
||||
obj = CloneFunctionObject(cx, fun, parent);
|
||||
if (!obj)
|
||||
goto error;
|
||||
}
|
||||
|
@ -3198,7 +3202,7 @@ BEGIN_CASE(JSOP_LAMBDA)
|
|||
goto error;
|
||||
}
|
||||
|
||||
obj = js_CloneFunctionObject(cx, fun, parent);
|
||||
obj = CloneFunctionObject(cx, fun, parent);
|
||||
if (!obj)
|
||||
goto error;
|
||||
} while (0);
|
||||
|
|
|
@ -5860,8 +5860,11 @@ js_NewRegExpObject(JSContext *cx, JSTokenStream *ts,
|
|||
JSObject * JS_FASTCALL
|
||||
js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto)
|
||||
{
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass);
|
||||
JSObject *clone = js_NewObject(cx, &js_RegExpClass, proto, NULL);
|
||||
JS_ASSERT(obj->getClass() == &js_RegExpClass);
|
||||
JS_ASSERT(proto);
|
||||
JS_ASSERT(proto->getClass() == &js_RegExpClass);
|
||||
JSObject *clone = js_NewObjectWithGivenProto(cx, &js_RegExpClass, proto,
|
||||
NULL);
|
||||
if (!clone)
|
||||
return NULL;
|
||||
JSRegExp *re = static_cast<JSRegExp *>(obj->getPrivate());
|
||||
|
|
|
@ -188,7 +188,7 @@ extern JSBool
|
|||
js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp);
|
||||
|
||||
extern JSObject * JS_FASTCALL
|
||||
js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto = NULL);
|
||||
js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto);
|
||||
|
||||
const uint32 JSSLOT_REGEXP_LAST_INDEX = JSSLOT_PRIVATE + 1;
|
||||
const uint32 REGEXP_CLASS_FIXED_RESERVED_SLOTS = 1;
|
||||
|
|
|
@ -126,7 +126,7 @@ JSScope::methodReadBarrier(JSContext *cx, JSScopeProperty *sprop, jsval *vp)
|
|||
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
|
||||
JS_ASSERT(FUN_OBJECT(fun) == funobj && FUN_NULL_CLOSURE(fun));
|
||||
|
||||
funobj = js_CloneFunctionObject(cx, fun, OBJ_GET_PARENT(cx, funobj));
|
||||
funobj = CloneFunctionObject(cx, fun, OBJ_GET_PARENT(cx, funobj));
|
||||
if (!funobj)
|
||||
return false;
|
||||
*vp = OBJECT_TO_JSVAL(funobj);
|
||||
|
|
|
@ -10383,7 +10383,7 @@ TraceRecorder::getClassPrototype(JSProtoKey key, LIns*& proto_ins)
|
|||
#endif
|
||||
|
||||
JSObject* proto;
|
||||
if (!js_GetClassPrototype(cx, globalObj, INT_TO_JSID(key), &proto))
|
||||
if (!js_GetClassPrototype(cx, globalObj, key, &proto))
|
||||
RETURN_ERROR("error in js_GetClassPrototype");
|
||||
|
||||
// This should not have reentered.
|
||||
|
@ -14528,26 +14528,26 @@ TraceRecorder::record_JSOP_CALLPROP()
|
|||
obj_ins = get(&l);
|
||||
this_ins = obj_ins; // |this| for subsequent call
|
||||
} else {
|
||||
jsint i;
|
||||
JSProtoKey protoKey;
|
||||
debug_only_stmt(const char* protoname = NULL;)
|
||||
if (JSVAL_IS_STRING(l)) {
|
||||
i = JSProto_String;
|
||||
protoKey = JSProto_String;
|
||||
debug_only_stmt(protoname = "String.prototype";)
|
||||
} else if (JSVAL_IS_NUMBER(l)) {
|
||||
i = JSProto_Number;
|
||||
protoKey = JSProto_Number;
|
||||
debug_only_stmt(protoname = "Number.prototype";)
|
||||
} else if (JSVAL_IS_SPECIAL(l)) {
|
||||
if (l == JSVAL_VOID)
|
||||
RETURN_STOP_A("callprop on void");
|
||||
guard(false, lir->ins2i(LIR_eq, get(&l), JSVAL_TO_SPECIAL(JSVAL_VOID)), MISMATCH_EXIT);
|
||||
i = JSProto_Boolean;
|
||||
protoKey = JSProto_Boolean;
|
||||
debug_only_stmt(protoname = "Boolean.prototype";)
|
||||
} else {
|
||||
JS_ASSERT(JSVAL_IS_NULL(l) || JSVAL_IS_VOID(l));
|
||||
RETURN_STOP_A("callprop on null or void");
|
||||
}
|
||||
|
||||
if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj))
|
||||
if (!js_GetClassPrototype(cx, NULL, protoKey, &obj))
|
||||
RETURN_ERROR_A("GetClassPrototype failed!");
|
||||
|
||||
obj_ins = INS_CONSTOBJ(obj);
|
||||
|
|
|
@ -1753,7 +1753,7 @@ GetXMLSetting(JSContext *cx, const char *name, jsval *vp)
|
|||
{
|
||||
jsval v;
|
||||
|
||||
if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v))
|
||||
if (!js_FindClassObject(cx, NULL, JSProto_XML, &v))
|
||||
return JS_FALSE;
|
||||
if (!VALUE_IS_FUNCTION(cx, v)) {
|
||||
*vp = JSVAL_VOID;
|
||||
|
@ -4734,7 +4734,7 @@ HasFunctionProperty(JSContext *cx, JSObject *obj, jsid funid, JSBool *found)
|
|||
* GetXMLFunction returns existing function.
|
||||
*/
|
||||
JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr);
|
||||
ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_String),
|
||||
ok = js_GetClassPrototype(cx, NULL, JSProto_String,
|
||||
&tvr.u.object);
|
||||
JS_ASSERT(tvr.u.object);
|
||||
if (ok) {
|
||||
|
@ -7836,8 +7836,7 @@ GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
|||
xml = (JSXML *) obj->getPrivate();
|
||||
if (HasSimpleContent(xml)) {
|
||||
/* Search in String.prototype to implement 11.2.2.1 Step 3(f). */
|
||||
ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_String),
|
||||
&tvr.u.object);
|
||||
ok = js_GetClassPrototype(cx, NULL, JSProto_String, &tvr.u.object);
|
||||
if (!ok)
|
||||
goto out;
|
||||
JS_ASSERT(tvr.u.object);
|
||||
|
|
Загрузка…
Ссылка в новой задаче