mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-11 20:35:50 +00:00
Bug 720753 - hoist NewObjectCache from JSCompartment into JSRuntime (r=bhackett)
This commit is contained in:
parent
facff78a38
commit
4d418a1913
@ -3789,7 +3789,7 @@ NewArray(JSContext *cx, uint32_t length, JSObject *proto)
|
||||
|
||||
GlobalObject *parent = GetCurrentGlobal(cx);
|
||||
|
||||
NewObjectCache &cache = cx->compartment->newObjectCache;
|
||||
NewObjectCache &cache = cx->runtime->newObjectCache;
|
||||
|
||||
NewObjectCache::EntryIndex entry = -1;
|
||||
if (cache.lookupGlobal(&ArrayClass, parent, kind, &entry)) {
|
||||
|
@ -190,6 +190,84 @@ struct ConservativeGCData
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Cache for speeding up repetitive creation of objects in the VM.
|
||||
* When an object is created which matches the criteria in the 'key' section
|
||||
* below, an entry is filled with the resulting object.
|
||||
*/
|
||||
class NewObjectCache
|
||||
{
|
||||
/* Statically asserted to be equal to sizeof(JSObject_Slots16) */
|
||||
static const unsigned MAX_OBJ_SIZE = 4 * sizeof(void*) + 16 * sizeof(Value);
|
||||
static inline void staticAsserts();
|
||||
|
||||
struct Entry
|
||||
{
|
||||
/* Class of the constructed object. */
|
||||
Class *clasp;
|
||||
|
||||
/*
|
||||
* Key with one of three possible values:
|
||||
*
|
||||
* - Global for the object. The object must have a standard class for
|
||||
* which the global's prototype can be determined, and the object's
|
||||
* parent will be the global.
|
||||
*
|
||||
* - Prototype for the object (cannot be global). The object's parent
|
||||
* will be the prototype's parent.
|
||||
*
|
||||
* - Type for the object. The object's parent will be the type's
|
||||
* prototype's parent.
|
||||
*/
|
||||
gc::Cell *key;
|
||||
|
||||
/* Allocation kind for the constructed object. */
|
||||
gc::AllocKind kind;
|
||||
|
||||
/* Number of bytes to copy from the template object. */
|
||||
uint32_t nbytes;
|
||||
|
||||
/*
|
||||
* Template object to copy from, with the initial values of fields,
|
||||
* fixed slots (undefined) and private data (NULL).
|
||||
*/
|
||||
char templateObject[MAX_OBJ_SIZE];
|
||||
};
|
||||
|
||||
Entry entries[41]; // TODO: reconsider size
|
||||
|
||||
public:
|
||||
|
||||
typedef int EntryIndex;
|
||||
|
||||
NewObjectCache() { PodZero(this); }
|
||||
void purge() { PodZero(this); }
|
||||
|
||||
/*
|
||||
* Get the entry index for the given lookup, return whether there was a hit
|
||||
* on an existing entry.
|
||||
*/
|
||||
inline bool lookupProto(Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry);
|
||||
inline bool lookupGlobal(Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry);
|
||||
inline bool lookupType(Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry);
|
||||
|
||||
/* Return a new object from a cache hit produced by a lookup method. */
|
||||
inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry);
|
||||
|
||||
/* Fill an entry after a cache miss. */
|
||||
inline void fillProto(EntryIndex entry, Class *clasp, JSObject *proto, gc::AllocKind kind, JSObject *obj);
|
||||
inline void fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj);
|
||||
inline void fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj);
|
||||
|
||||
/* Invalidate any entries which might produce an object with shape/proto. */
|
||||
void invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto);
|
||||
|
||||
private:
|
||||
inline bool lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry);
|
||||
inline void fill(EntryIndex entry, Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj);
|
||||
static inline void copyCachedToObject(JSObject *dst, JSObject *src);
|
||||
};
|
||||
|
||||
/*
|
||||
* A FreeOp can do one thing: free memory. For convenience, it has delete_
|
||||
* convenience methods that also call destructors.
|
||||
@ -626,6 +704,7 @@ struct JSRuntime : js::RuntimeFriendFields
|
||||
|
||||
js::GSNCache gsnCache;
|
||||
js::PropertyCache propertyCache;
|
||||
js::NewObjectCache newObjectCache;
|
||||
|
||||
/* State used by jsdtoa.cpp. */
|
||||
DtoaState *dtoaState;
|
||||
|
@ -45,14 +45,126 @@
|
||||
#include "jscompartment.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "jsinterp.h"
|
||||
#include "jsprobes.h"
|
||||
#include "jsxml.h"
|
||||
#include "jsgc.h"
|
||||
|
||||
#include "frontend/ParseMaps.h"
|
||||
#include "vm/RegExpObject.h"
|
||||
|
||||
#include "jsgcinlines.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
inline void
|
||||
NewObjectCache::staticAsserts()
|
||||
{
|
||||
JS_STATIC_ASSERT(NewObjectCache::MAX_OBJ_SIZE == sizeof(JSObject_Slots16));
|
||||
JS_STATIC_ASSERT(gc::FINALIZE_OBJECT_LAST == gc::FINALIZE_OBJECT16_BACKGROUND);
|
||||
}
|
||||
|
||||
inline bool
|
||||
NewObjectCache::lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry)
|
||||
{
|
||||
uintptr_t hash = (uintptr_t(clasp) ^ uintptr_t(key)) + kind;
|
||||
*pentry = hash % js::ArrayLength(entries);
|
||||
|
||||
Entry *entry = &entries[*pentry];
|
||||
|
||||
/* N.B. Lookups with the same clasp/key but different kinds map to different entries. */
|
||||
return (entry->clasp == clasp && entry->key == key);
|
||||
}
|
||||
|
||||
inline bool
|
||||
NewObjectCache::lookupProto(Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry)
|
||||
{
|
||||
JS_ASSERT(!proto->isGlobal());
|
||||
return lookup(clasp, proto, kind, pentry);
|
||||
}
|
||||
|
||||
inline bool
|
||||
NewObjectCache::lookupGlobal(Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry)
|
||||
{
|
||||
return lookup(clasp, global, kind, pentry);
|
||||
}
|
||||
|
||||
inline bool
|
||||
NewObjectCache::lookupType(Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry)
|
||||
{
|
||||
return lookup(clasp, type, kind, pentry);
|
||||
}
|
||||
|
||||
inline void
|
||||
NewObjectCache::fill(EntryIndex entry_, Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(unsigned(entry_) < ArrayLength(entries));
|
||||
Entry *entry = &entries[entry_];
|
||||
|
||||
JS_ASSERT(!obj->hasDynamicSlots() && !obj->hasDynamicElements());
|
||||
|
||||
entry->clasp = clasp;
|
||||
entry->key = key;
|
||||
entry->kind = kind;
|
||||
|
||||
entry->nbytes = obj->sizeOfThis();
|
||||
js_memcpy(&entry->templateObject, obj, entry->nbytes);
|
||||
}
|
||||
|
||||
inline void
|
||||
NewObjectCache::fillProto(EntryIndex entry, Class *clasp, JSObject *proto, gc::AllocKind kind, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(!proto->isGlobal());
|
||||
JS_ASSERT(obj->getProto() == proto);
|
||||
return fill(entry, clasp, proto, kind, obj);
|
||||
}
|
||||
|
||||
inline void
|
||||
NewObjectCache::fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj)
|
||||
{
|
||||
//JS_ASSERT(global == obj->getGlobal());
|
||||
return fill(entry, clasp, global, kind, obj);
|
||||
}
|
||||
|
||||
inline void
|
||||
NewObjectCache::fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(obj->type() == type);
|
||||
return fill(entry, clasp, type, kind, obj);
|
||||
}
|
||||
|
||||
inline JSObject *
|
||||
NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_)
|
||||
{
|
||||
JS_ASSERT(unsigned(entry_) < ArrayLength(entries));
|
||||
Entry *entry = &entries[entry_];
|
||||
|
||||
JSObject *obj = js_TryNewGCObject(cx, entry->kind);
|
||||
if (obj) {
|
||||
copyCachedToObject(obj, reinterpret_cast<JSObject *>(&entry->templateObject));
|
||||
Probes::createObject(cx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* Copy the entry to the stack first in case it is purged by a GC. */
|
||||
size_t nbytes = entry->nbytes;
|
||||
char stackObject[sizeof(JSObject_Slots16)];
|
||||
JS_ASSERT(nbytes <= sizeof(stackObject));
|
||||
js_memcpy(&stackObject, &entry->templateObject, nbytes);
|
||||
|
||||
JSObject *baseobj = (JSObject *) stackObject;
|
||||
RootShape shapeRoot(cx, (Shape **) baseobj->addressOfShape());
|
||||
RootTypeObject typeRoot(cx, (types::TypeObject **) baseobj->addressOfType());
|
||||
|
||||
obj = js_NewGCObject(cx, entry->kind);
|
||||
if (obj) {
|
||||
copyCachedToObject(obj, baseobj);
|
||||
Probes::createObject(cx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct PreserveRegsGuard
|
||||
{
|
||||
PreserveRegsGuard(JSContext *cx, FrameRegs ®s)
|
||||
|
@ -115,8 +115,6 @@ JSCompartment::init(JSContext *cx)
|
||||
activeAnalysis = activeInference = false;
|
||||
types.init(cx);
|
||||
|
||||
newObjectCache.reset();
|
||||
|
||||
if (!crossCompartmentWrappers.init())
|
||||
return false;
|
||||
|
||||
@ -471,8 +469,6 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes)
|
||||
if (emptyTypeObject && IsAboutToBeFinalized(emptyTypeObject))
|
||||
emptyTypeObject = NULL;
|
||||
|
||||
newObjectCache.reset();
|
||||
|
||||
sweepBreakpoints(fop);
|
||||
|
||||
{
|
||||
|
@ -283,9 +283,6 @@ struct JSCompartment
|
||||
|
||||
js::types::TypeObject *getLazyType(JSContext *cx, JSObject *proto);
|
||||
|
||||
/* Cache to speed up object creation. */
|
||||
js::NewObjectCache newObjectCache;
|
||||
|
||||
/*
|
||||
* Keeps track of the total number of malloc bytes connected to a
|
||||
* compartment's GC things. This counter should be used in preference to
|
||||
|
@ -2887,10 +2887,10 @@ PurgeRuntime(JSTracer *trc)
|
||||
}
|
||||
|
||||
rt->tempLifoAlloc.freeUnused();
|
||||
rt->gsnCache.purge();
|
||||
|
||||
/* FIXME: bug 506341 */
|
||||
rt->gsnCache.purge();
|
||||
rt->propertyCache.purge(rt);
|
||||
rt->newObjectCache.purge();
|
||||
|
||||
for (ContextIter acx(rt); !acx.done(); acx.next())
|
||||
acx->purge();
|
||||
|
@ -2771,7 +2771,7 @@ js::NewObjectWithGivenProto(JSContext *cx, js::Class *clasp, JSObject *proto, JS
|
||||
if (CanBeFinalizedInBackground(kind, clasp))
|
||||
kind = GetBackgroundAllocKind(kind);
|
||||
|
||||
NewObjectCache &cache = cx->compartment->newObjectCache;
|
||||
NewObjectCache &cache = cx->runtime->newObjectCache;
|
||||
|
||||
NewObjectCache::EntryIndex entry = -1;
|
||||
if (proto && (!parent || parent == proto->getParent()) && !proto->isGlobal()) {
|
||||
@ -2827,7 +2827,7 @@ js::NewObjectWithClassProto(JSContext *cx, js::Class *clasp, JSObject *proto, JS
|
||||
*/
|
||||
JSProtoKey protoKey = GetClassProtoKey(clasp);
|
||||
|
||||
NewObjectCache &cache = cx->compartment->newObjectCache;
|
||||
NewObjectCache &cache = cx->runtime->newObjectCache;
|
||||
|
||||
NewObjectCache::EntryIndex entry = -1;
|
||||
if (parent->isGlobal() && protoKey != JSProto_Null) {
|
||||
@ -2863,7 +2863,7 @@ js::NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc
|
||||
if (CanBeFinalizedInBackground(kind, &ObjectClass))
|
||||
kind = GetBackgroundAllocKind(kind);
|
||||
|
||||
NewObjectCache &cache = cx->compartment->newObjectCache;
|
||||
NewObjectCache &cache = cx->runtime->newObjectCache;
|
||||
|
||||
NewObjectCache::EntryIndex entry = -1;
|
||||
if (parent == type->proto->getParent()) {
|
||||
|
@ -1079,83 +1079,6 @@ IsStandardClassResolved(JSObject *obj, js::Class *clasp);
|
||||
void
|
||||
MarkStandardClassInitializedNoProto(JSObject *obj, js::Class *clasp);
|
||||
|
||||
/*
|
||||
* Cache for speeding up repetitive creation of objects in the VM.
|
||||
* When an object is created which matches the criteria in the 'key' section
|
||||
* below, an entry is filled with the resulting object.
|
||||
*/
|
||||
class NewObjectCache
|
||||
{
|
||||
struct Entry
|
||||
{
|
||||
/* Class of the constructed object. */
|
||||
Class *clasp;
|
||||
|
||||
/*
|
||||
* Key with one of three possible values:
|
||||
*
|
||||
* - Global for the object. The object must have a standard class for
|
||||
* which the global's prototype can be determined, and the object's
|
||||
* parent will be the global.
|
||||
*
|
||||
* - Prototype for the object (cannot be global). The object's parent
|
||||
* will be the prototype's parent.
|
||||
*
|
||||
* - Type for the object. The object's parent will be the type's
|
||||
* prototype's parent.
|
||||
*/
|
||||
gc::Cell *key;
|
||||
|
||||
/* Allocation kind for the constructed object. */
|
||||
gc::AllocKind kind;
|
||||
|
||||
/* Number of bytes to copy from the template object. */
|
||||
uint32_t nbytes;
|
||||
|
||||
/*
|
||||
* Template object to copy from, with the initial values of fields,
|
||||
* fixed slots (undefined) and private data (NULL).
|
||||
*/
|
||||
JSObject_Slots16 templateObject;
|
||||
};
|
||||
|
||||
Entry entries[41];
|
||||
|
||||
void staticAsserts() {
|
||||
JS_STATIC_ASSERT(gc::FINALIZE_OBJECT_LAST == gc::FINALIZE_OBJECT16_BACKGROUND);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
typedef int EntryIndex;
|
||||
|
||||
void reset() { PodZero(this); }
|
||||
|
||||
/*
|
||||
* Get the entry index for the given lookup, return whether there was a hit
|
||||
* on an existing entry.
|
||||
*/
|
||||
inline bool lookupProto(Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry);
|
||||
inline bool lookupGlobal(Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry);
|
||||
inline bool lookupType(Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry);
|
||||
|
||||
/* Return a new object from a cache hit produced by a lookup method. */
|
||||
inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry);
|
||||
|
||||
/* Fill an entry after a cache miss. */
|
||||
inline void fillProto(EntryIndex entry, Class *clasp, JSObject *proto, gc::AllocKind kind, JSObject *obj);
|
||||
inline void fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj);
|
||||
inline void fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj);
|
||||
|
||||
/* Invalidate any entries which might produce an object with shape/proto. */
|
||||
void invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto);
|
||||
|
||||
private:
|
||||
inline bool lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry);
|
||||
inline void fill(EntryIndex entry, Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj);
|
||||
static inline void copyCachedToObject(JSObject *dst, JSObject *src);
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/*
|
||||
|
@ -1331,75 +1331,6 @@ class AutoPropertyDescriptorRooter : private AutoGCRooter, public PropertyDescri
|
||||
SkipRoot skip;
|
||||
};
|
||||
|
||||
inline bool
|
||||
NewObjectCache::lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry)
|
||||
{
|
||||
uintptr_t hash = (uintptr_t(clasp) ^ uintptr_t(key)) + kind;
|
||||
*pentry = hash % js::ArrayLength(entries);
|
||||
|
||||
Entry *entry = &entries[*pentry];
|
||||
|
||||
/* N.B. Lookups with the same clasp/key but different kinds map to different entries. */
|
||||
return (entry->clasp == clasp && entry->key == key);
|
||||
}
|
||||
|
||||
inline bool
|
||||
NewObjectCache::lookupProto(Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry)
|
||||
{
|
||||
JS_ASSERT(!proto->isGlobal());
|
||||
return lookup(clasp, proto, kind, pentry);
|
||||
}
|
||||
|
||||
inline bool
|
||||
NewObjectCache::lookupGlobal(Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry)
|
||||
{
|
||||
return lookup(clasp, global, kind, pentry);
|
||||
}
|
||||
|
||||
inline bool
|
||||
NewObjectCache::lookupType(Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry)
|
||||
{
|
||||
return lookup(clasp, type, kind, pentry);
|
||||
}
|
||||
|
||||
inline void
|
||||
NewObjectCache::fill(EntryIndex entry_, Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(unsigned(entry_) < ArrayLength(entries));
|
||||
Entry *entry = &entries[entry_];
|
||||
|
||||
JS_ASSERT(!obj->hasDynamicSlots() && !obj->hasDynamicElements());
|
||||
|
||||
entry->clasp = clasp;
|
||||
entry->key = key;
|
||||
entry->kind = kind;
|
||||
|
||||
entry->nbytes = obj->sizeOfThis();
|
||||
js_memcpy(&entry->templateObject, obj, entry->nbytes);
|
||||
}
|
||||
|
||||
inline void
|
||||
NewObjectCache::fillProto(EntryIndex entry, Class *clasp, JSObject *proto, gc::AllocKind kind, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(!proto->isGlobal());
|
||||
JS_ASSERT(obj->getProto() == proto);
|
||||
return fill(entry, clasp, proto, kind, obj);
|
||||
}
|
||||
|
||||
inline void
|
||||
NewObjectCache::fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj)
|
||||
{
|
||||
//JS_ASSERT(global == obj->getGlobal());
|
||||
return fill(entry, clasp, global, kind, obj);
|
||||
}
|
||||
|
||||
inline void
|
||||
NewObjectCache::fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(obj->type() == type);
|
||||
return fill(entry, clasp, type, kind, obj);
|
||||
}
|
||||
|
||||
inline void
|
||||
NewObjectCache::copyCachedToObject(JSObject *dst, JSObject *src)
|
||||
{
|
||||
@ -1410,39 +1341,6 @@ NewObjectCache::copyCachedToObject(JSObject *dst, JSObject *src)
|
||||
#endif
|
||||
}
|
||||
|
||||
inline JSObject *
|
||||
NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_)
|
||||
{
|
||||
JS_ASSERT(unsigned(entry_) < ArrayLength(entries));
|
||||
Entry *entry = &entries[entry_];
|
||||
|
||||
JSObject *obj = js_TryNewGCObject(cx, entry->kind);
|
||||
if (obj) {
|
||||
copyCachedToObject(obj, &entry->templateObject);
|
||||
Probes::createObject(cx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* Copy the entry to the stack first in case it is purged by a GC. */
|
||||
size_t nbytes = entry->nbytes;
|
||||
char stackObject[sizeof(JSObject_Slots16)];
|
||||
JS_ASSERT(nbytes <= sizeof(stackObject));
|
||||
js_memcpy(&stackObject, &entry->templateObject, nbytes);
|
||||
|
||||
JSObject *baseobj = (JSObject *) stackObject;
|
||||
RootShape shapeRoot(cx, (Shape **) baseobj->addressOfShape());
|
||||
RootTypeObject typeRoot(cx, (types::TypeObject **) baseobj->addressOfType());
|
||||
|
||||
obj = js_NewGCObject(cx, entry->kind);
|
||||
if (obj) {
|
||||
copyCachedToObject(obj, baseobj);
|
||||
Probes::createObject(cx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
CanBeFinalizedInBackground(gc::AllocKind kind, Class *clasp)
|
||||
{
|
||||
|
@ -1397,7 +1397,7 @@ EmptyShape::insertInitialShape(JSContext *cx, Shape *shape, JSObject *proto)
|
||||
* the appropriate properties if found. Clearing the cache entry avoids
|
||||
* this duplicate regeneration.
|
||||
*/
|
||||
cx->compartment->newObjectCache.invalidateEntriesForShape(cx, shape, proto);
|
||||
cx->runtime->newObjectCache.invalidateEntriesForShape(cx, shape, proto);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -359,7 +359,7 @@ GlobalObject::clear(JSContext *cx)
|
||||
* Reset the new object cache in the compartment, which assumes that
|
||||
* prototypes cached on the global object are immutable.
|
||||
*/
|
||||
cx->compartment->newObjectCache.reset();
|
||||
cx->runtime->newObjectCache.purge();
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user