bug 545529 - caching Class.prototype lookups. r=brendan

This commit is contained in:
Igor Bukanov 2010-02-19 20:44:23 +03:00
Родитель 8a7d5bb4db
Коммит 06f21330ba
16 изменённых файлов: 248 добавлений и 133 удалений

Просмотреть файл

@ -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, &regexpPool);
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;
}

Просмотреть файл

@ -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);