зеркало из https://github.com/mozilla/pjs.git
Get new object empty shapes with a common hash table, bug 701509.
This commit is contained in:
Родитель
135431de6e
Коммит
83d4c45568
|
@ -1372,9 +1372,8 @@ JSObject::makeDenseArraySlow(JSContext *cx)
|
|||
|
||||
/* Create a native scope. */
|
||||
gc::AllocKind kind = getAllocKind();
|
||||
Shape *shape = GetInitialShapeForObject(cx, &SlowArrayClass,
|
||||
oldShape->getObjectParent(),
|
||||
getProto()->getNewType(cx), kind);
|
||||
Shape *shape = EmptyShape::lookupInitialShape(cx, &SlowArrayClass, getProto(),
|
||||
oldShape->getObjectParent(), kind);
|
||||
if (!shape)
|
||||
return false;
|
||||
setLastPropertyInfallible(shape);
|
||||
|
@ -3892,7 +3891,8 @@ NewArray(JSContext *cx, jsuint length, JSObject *proto)
|
|||
if (!type)
|
||||
return NULL;
|
||||
|
||||
Shape *shape = GetInitialShapeForObject(cx, &ArrayClass, proto->getParent(), type, kind);
|
||||
Shape *shape = EmptyShape::lookupInitialShape(cx, &ArrayClass, proto,
|
||||
proto->getParent(), kind);
|
||||
if (!shape)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -469,6 +469,7 @@ JSCompartment::sweep(JSContext *cx, bool releaseTypes)
|
|||
/* Remove dead references held weakly by the compartment. */
|
||||
|
||||
sweepBaseShapeTable(cx);
|
||||
sweepInitialShapeTable(cx);
|
||||
sweepNewTypeObjectTable(cx, newTypeObjects);
|
||||
sweepNewTypeObjectTable(cx, lazyTypeObjects);
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "jsgc.h"
|
||||
#include "jsgcstats.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsscope.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
@ -474,54 +475,18 @@ struct JS_FRIEND_API(JSCompartment) {
|
|||
jsrefcount liveDictModeNodes;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set of all unowned base shapes in the compartment, with optional initial
|
||||
* shape for objects with the base shape. The initial shape is filled in
|
||||
* several circumstances:
|
||||
*
|
||||
* - For non-native classes and classes without class prototypes (scope
|
||||
* chain classes, arguments, and iterators), the initial shape is set to
|
||||
* an empty shape for the class/parent.
|
||||
*
|
||||
* - For the String and RegExp classes, the initial shape is preloaded with
|
||||
* non-configurable properties of the objects mapping the class's various
|
||||
* fixed slots.
|
||||
*/
|
||||
|
||||
struct BaseShapeEntry {
|
||||
js::UnownedBaseShape *base;
|
||||
js::ShapeKindArray *shapes;
|
||||
|
||||
typedef const js::BaseShape *Lookup;
|
||||
|
||||
static inline js::HashNumber hash(const js::BaseShape *base);
|
||||
static inline bool match(const BaseShapeEntry &key, const js::BaseShape *lookup);
|
||||
};
|
||||
|
||||
typedef js::HashSet<BaseShapeEntry, BaseShapeEntry, js::SystemAllocPolicy> BaseShapeSet;
|
||||
|
||||
BaseShapeSet baseShapes;
|
||||
|
||||
/* Set of all unowned base shapes in the compartment. */
|
||||
js::BaseShapeSet baseShapes;
|
||||
void sweepBaseShapeTable(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Set of all type objects in the compartment which are the default 'new'
|
||||
* or the lazy types of some (possibly NULL) prototype.
|
||||
*/
|
||||
/* Set of initial shapes in the compartment. */
|
||||
js::InitialShapeSet initialShapes;
|
||||
void sweepInitialShapeTable(JSContext *cx);
|
||||
|
||||
struct NewTypeObjectEntry {
|
||||
typedef JSObject *Lookup;
|
||||
|
||||
static inline js::HashNumber hash(JSObject *base);
|
||||
static inline bool match(js::types::TypeObject *key, JSObject *lookup);
|
||||
};
|
||||
|
||||
typedef js::HashSet<js::types::TypeObject *, NewTypeObjectEntry, js::SystemAllocPolicy> NewTypeObjectSet;
|
||||
|
||||
NewTypeObjectSet newTypeObjects;
|
||||
NewTypeObjectSet lazyTypeObjects;
|
||||
|
||||
void sweepNewTypeObjectTable(JSContext *cx, NewTypeObjectSet &table);
|
||||
/* Set of default 'new' or lazy types in the compartment. */
|
||||
js::types::TypeObjectSet newTypeObjects;
|
||||
js::types::TypeObjectSet lazyTypeObjects;
|
||||
void sweepNewTypeObjectTable(JSContext *cx, js::types::TypeObjectSet &table);
|
||||
|
||||
js::types::TypeObject *emptyTypeObject;
|
||||
|
||||
|
@ -530,6 +495,7 @@ struct JS_FRIEND_API(JSCompartment) {
|
|||
|
||||
js::types::TypeObject *getLazyType(JSContext *cx, JSObject *proto);
|
||||
|
||||
/* Cache to speed up object creation. */
|
||||
js::NewObjectCache newObjectCache;
|
||||
|
||||
private:
|
||||
|
|
|
@ -139,8 +139,9 @@ ArgumentsObject::create(JSContext *cx, uint32 argc, JSObject &callee)
|
|||
|
||||
bool strict = callee.toFunction()->inStrictMode();
|
||||
Class *clasp = strict ? &StrictArgumentsObjectClass : &NormalArgumentsObjectClass;
|
||||
Shape *emptyArgumentsShape = BaseShape::lookupInitialShape(cx, clasp, proto->getParent(),
|
||||
FINALIZE_OBJECT4);
|
||||
Shape *emptyArgumentsShape =
|
||||
EmptyShape::lookupInitialShape(cx, clasp, proto,
|
||||
proto->getParent(), FINALIZE_OBJECT4);
|
||||
if (!emptyArgumentsShape)
|
||||
return NULL;
|
||||
|
||||
|
@ -707,8 +708,8 @@ NewDeclEnvObject(JSContext *cx, StackFrame *fp)
|
|||
return NULL;
|
||||
|
||||
JSObject *parent = fp->scopeChain().getGlobal();
|
||||
Shape *emptyDeclEnvShape = BaseShape::lookupInitialShape(cx, &DeclEnvClass, parent,
|
||||
FINALIZE_OBJECT2);
|
||||
Shape *emptyDeclEnvShape = EmptyShape::lookupInitialShape(cx, &DeclEnvClass, NULL,
|
||||
parent, FINALIZE_OBJECT2);
|
||||
if (!emptyDeclEnvShape)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -874,14 +874,6 @@ ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
|
|||
}
|
||||
}
|
||||
|
||||
if (type->emptyShapes) {
|
||||
for (unsigned i = 0; i < ShapeKindArray::SHAPE_COUNT; i++) {
|
||||
Shape *shape = type->emptyShapes->getIndex(i);
|
||||
if (shape)
|
||||
PushMarkStack(gcmarker, shape);
|
||||
}
|
||||
}
|
||||
|
||||
if (type->proto)
|
||||
PushMarkStack(gcmarker, type->proto);
|
||||
|
||||
|
@ -914,14 +906,6 @@ MarkChildren(JSTracer *trc, types::TypeObject *type)
|
|||
}
|
||||
}
|
||||
|
||||
if (type->emptyShapes) {
|
||||
for (unsigned i = 0; i < ShapeKindArray::SHAPE_COUNT; i++) {
|
||||
Shape *shape = type->emptyShapes->getIndex(i);
|
||||
if (shape)
|
||||
MarkShape(trc, shape, "empty_shape");
|
||||
}
|
||||
}
|
||||
|
||||
if (type->proto)
|
||||
MarkObject(trc, *type->proto, "type_proto");
|
||||
|
||||
|
|
|
@ -5703,13 +5703,13 @@ JSObject::makeLazyType(JSContext *cx)
|
|||
}
|
||||
|
||||
/* static */ inline HashNumber
|
||||
JSCompartment::NewTypeObjectEntry::hash(JSObject *proto)
|
||||
TypeObjectEntry::hash(JSObject *proto)
|
||||
{
|
||||
return PointerHasher<JSObject *, 3>::hash(proto);
|
||||
}
|
||||
|
||||
/* static */ inline bool
|
||||
JSCompartment::NewTypeObjectEntry::match(TypeObject *key, JSObject *lookup)
|
||||
TypeObjectEntry::match(TypeObject *key, JSObject *lookup)
|
||||
{
|
||||
return key->proto == lookup;
|
||||
}
|
||||
|
@ -5718,12 +5718,12 @@ JSCompartment::NewTypeObjectEntry::match(TypeObject *key, JSObject *lookup)
|
|||
bool
|
||||
JSObject::hasNewType(TypeObject *type)
|
||||
{
|
||||
JSCompartment::NewTypeObjectSet &table = compartment()->newTypeObjects;
|
||||
TypeObjectSet &table = compartment()->newTypeObjects;
|
||||
|
||||
if (!table.initialized())
|
||||
return false;
|
||||
|
||||
JSCompartment::NewTypeObjectSet::Ptr p = table.lookup(this);
|
||||
TypeObjectSet::Ptr p = table.lookup(this);
|
||||
return p && *p == type;
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
@ -5739,9 +5739,9 @@ JSObject::setNewTypeUnknown(JSContext *cx)
|
|||
* not have the SETS_MARKED_UNKNOWN bit set, so may require a type set
|
||||
* crawl if prototypes of the object change dynamically in the future.
|
||||
*/
|
||||
JSCompartment::NewTypeObjectSet &table = compartment()->newTypeObjects;
|
||||
TypeObjectSet &table = compartment()->newTypeObjects;
|
||||
if (table.initialized()) {
|
||||
JSCompartment::NewTypeObjectSet::Ptr p = table.lookup(this);
|
||||
TypeObjectSet::Ptr p = table.lookup(this);
|
||||
if (p)
|
||||
MarkTypeObjectUnknownProperties(cx, *p);
|
||||
}
|
||||
|
@ -5755,12 +5755,12 @@ JSObject::getNewType(JSContext *cx, JSFunction *fun)
|
|||
if (!setDelegate(cx))
|
||||
return NULL;
|
||||
|
||||
JSCompartment::NewTypeObjectSet &table = cx->compartment->newTypeObjects;
|
||||
TypeObjectSet &table = cx->compartment->newTypeObjects;
|
||||
|
||||
if (!table.initialized() && !table.init())
|
||||
return NULL;
|
||||
|
||||
JSCompartment::NewTypeObjectSet::AddPtr p = table.lookupForAdd(this);
|
||||
TypeObjectSet::AddPtr p = table.lookupForAdd(this);
|
||||
if (p) {
|
||||
TypeObject *type = *p;
|
||||
|
||||
|
@ -5833,12 +5833,12 @@ JSObject::getNewType(JSContext *cx, JSFunction *fun)
|
|||
TypeObject *
|
||||
JSCompartment::getLazyType(JSContext *cx, JSObject *proto)
|
||||
{
|
||||
JSCompartment::NewTypeObjectSet &table = cx->compartment->lazyTypeObjects;
|
||||
TypeObjectSet &table = cx->compartment->lazyTypeObjects;
|
||||
|
||||
if (!table.initialized() && !table.init())
|
||||
return NULL;
|
||||
|
||||
JSCompartment::NewTypeObjectSet::AddPtr p = table.lookupForAdd(proto);
|
||||
TypeObjectSet::AddPtr p = table.lookupForAdd(proto);
|
||||
if (p) {
|
||||
TypeObject *type = *p;
|
||||
JS_ASSERT(type->lazy());
|
||||
|
@ -5931,7 +5931,6 @@ TypeObject::sweep(JSContext *cx)
|
|||
contribution = 0;
|
||||
|
||||
if (singleton) {
|
||||
JS_ASSERT(!emptyShapes);
|
||||
JS_ASSERT(!newScript);
|
||||
|
||||
/*
|
||||
|
@ -5944,8 +5943,6 @@ TypeObject::sweep(JSContext *cx)
|
|||
}
|
||||
|
||||
if (!isMarked()) {
|
||||
if (emptyShapes)
|
||||
Foreground::free_(emptyShapes);
|
||||
if (newScript)
|
||||
Foreground::free_(newScript);
|
||||
return;
|
||||
|
@ -6113,10 +6110,10 @@ TypeCompartment::sweep(JSContext *cx)
|
|||
}
|
||||
|
||||
void
|
||||
JSCompartment::sweepNewTypeObjectTable(JSContext *cx, NewTypeObjectSet &table)
|
||||
JSCompartment::sweepNewTypeObjectTable(JSContext *cx, TypeObjectSet &table)
|
||||
{
|
||||
if (table.initialized()) {
|
||||
for (NewTypeObjectSet::Enum e(table); !e.empty(); e.popFront()) {
|
||||
for (TypeObjectSet::Enum e(table); !e.empty(); e.popFront()) {
|
||||
TypeObject *type = e.front();
|
||||
if (!type->isMarked())
|
||||
e.removeFront();
|
||||
|
@ -6328,7 +6325,7 @@ JS_GetTypeInferenceObjectStats(void *object_, TypeInferenceMemoryStats *stats, J
|
|||
* every GC. The type object is normally destroyed too, but we don't
|
||||
* charge this to 'temporary' as this is not for GC heap values.
|
||||
*/
|
||||
JS_ASSERT(!object->newScript && !object->emptyShapes);
|
||||
JS_ASSERT(!object->newScript);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -6347,11 +6344,6 @@ JS_GetTypeInferenceObjectStats(void *object_, TypeInferenceMemoryStats *stats, J
|
|||
}
|
||||
}
|
||||
|
||||
if (object->emptyShapes) {
|
||||
size_t usable = usf(object->emptyShapes);
|
||||
stats->emptyShapes += usable ? usable : sizeof(ShapeKindArray);
|
||||
}
|
||||
|
||||
/*
|
||||
* This counts memory that is in the temp pool but gets attributed
|
||||
* elsewhere. See JS_GetTypeInferenceMemoryStats for more details.
|
||||
|
|
|
@ -730,19 +730,9 @@ struct TypeObject : gc::Cell
|
|||
static const size_t LAZY_SINGLETON = 1;
|
||||
bool lazy() const { return singleton == (JSObject *) LAZY_SINGLETON; }
|
||||
|
||||
/* Lazily filled array of empty shapes for each size of objects with this type. */
|
||||
js::ShapeKindArray *emptyShapes;
|
||||
|
||||
/* Flags for this object. */
|
||||
TypeObjectFlags flags;
|
||||
|
||||
/*
|
||||
* If non-NULL, objects of this type have always been constructed using
|
||||
* 'new' on the specified script, which adds some number of properties to
|
||||
* the object in a definite order before the object escapes.
|
||||
*/
|
||||
TypeNewScript *newScript;
|
||||
|
||||
/*
|
||||
* Estimate of the contribution of this object to the type sets it appears in.
|
||||
* This is the sum of the sizes of those sets at the point when the object
|
||||
|
@ -757,6 +747,13 @@ struct TypeObject : gc::Cell
|
|||
uint32 contribution;
|
||||
static const uint32 CONTRIBUTION_LIMIT = 2000;
|
||||
|
||||
/*
|
||||
* If non-NULL, objects of this type have always been constructed using
|
||||
* 'new' on the specified script, which adds some number of properties to
|
||||
* the object in a definite order before the object escapes.
|
||||
*/
|
||||
TypeNewScript *newScript;
|
||||
|
||||
/*
|
||||
* Properties of this object. This may contain JSID_VOID, representing the
|
||||
* types of all integer indexes of the object, and/or JSID_EMPTY, holding
|
||||
|
@ -792,6 +789,10 @@ struct TypeObject : gc::Cell
|
|||
/* If this is an interpreted function, the function object. */
|
||||
JSFunction *interpretedFunction;
|
||||
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
void *padding;
|
||||
#endif
|
||||
|
||||
inline TypeObject(JSObject *proto, bool isFunction, bool unknown);
|
||||
|
||||
bool isFunction() { return !!(flags & OBJECT_FLAG_FUNCTION); }
|
||||
|
@ -811,14 +812,6 @@ struct TypeObject : gc::Cell
|
|||
return !!(flags & OBJECT_FLAG_UNKNOWN_PROPERTIES);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return an immutable, shareable, empty shape for objects with this type
|
||||
* and the specified class, and finalize kind (fixed slot count). Objects
|
||||
* created with this shape have the same class and parent as the type's
|
||||
* prototype.
|
||||
*/
|
||||
inline js::EmptyShape *getEmptyShape(JSContext *cx, gc::AllocKind kind);
|
||||
|
||||
/*
|
||||
* Get or create a property of this object. Only call this for properties which
|
||||
* a script accesses explicitly. 'assign' indicates whether this is for an
|
||||
|
@ -882,6 +875,19 @@ struct TypeObject : gc::Cell
|
|||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Entries for the per-compartment set of type objects which are the default
|
||||
* 'new' or the lazy types of some prototype.
|
||||
*/
|
||||
struct TypeObjectEntry
|
||||
{
|
||||
typedef JSObject *Lookup;
|
||||
|
||||
static inline HashNumber hash(JSObject *base);
|
||||
static inline bool match(TypeObject *key, JSObject *lookup);
|
||||
};
|
||||
typedef HashSet<TypeObject *, TypeObjectEntry, SystemAllocPolicy> TypeObjectSet;
|
||||
|
||||
/*
|
||||
* Call to mark a script's arguments as having been created, recompile any
|
||||
* dependencies and walk the stack if necessary to fix any lazy arguments.
|
||||
|
|
|
@ -413,7 +413,7 @@ NewIteratorObject(JSContext *cx, uintN flags)
|
|||
if (!type)
|
||||
return NULL;
|
||||
|
||||
Shape *emptyEnumeratorShape = BaseShape::lookupInitialShape(cx, &IteratorClass, NULL,
|
||||
Shape *emptyEnumeratorShape = EmptyShape::lookupInitialShape(cx, &IteratorClass, NULL, NULL,
|
||||
FINALIZE_OBJECT2);
|
||||
if (!emptyEnumeratorShape)
|
||||
return NULL;
|
||||
|
|
|
@ -2917,7 +2917,7 @@ NewObject(JSContext *cx, Class *clasp, types::TypeObject *type, JSObject *parent
|
|||
JS_ASSERT_IF(clasp == &FunctionClass,
|
||||
kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
|
||||
|
||||
Shape *shape = GetInitialShapeForObject(cx, clasp, parent, type, kind);
|
||||
Shape *shape = EmptyShape::lookupInitialShape(cx, clasp, type->proto, parent, kind);
|
||||
if (!shape)
|
||||
return NULL;
|
||||
|
||||
|
@ -3593,7 +3593,7 @@ js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
|
|||
|
||||
StackFrame *priv = js_FloatingFrameIfGenerator(cx, cx->fp());
|
||||
|
||||
Shape *emptyWithShape = BaseShape::lookupInitialShape(cx, &WithClass,
|
||||
Shape *emptyWithShape = EmptyShape::lookupInitialShape(cx, &WithClass, proto,
|
||||
parent->getGlobal(),
|
||||
FINALIZE_OBJECT4);
|
||||
if (!emptyWithShape)
|
||||
|
@ -3627,7 +3627,7 @@ js_NewBlockObject(JSContext *cx)
|
|||
if (!type)
|
||||
return NULL;
|
||||
|
||||
Shape *emptyBlockShape = BaseShape::lookupInitialShape(cx, &BlockClass, NULL,
|
||||
Shape *emptyBlockShape = EmptyShape::lookupInitialShape(cx, &BlockClass, NULL, NULL,
|
||||
FINALIZE_OBJECT4);
|
||||
if (!emptyBlockShape)
|
||||
return NULL;
|
||||
|
@ -3957,7 +3957,8 @@ JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b,
|
|||
if (!a->generateOwnShape(cx))
|
||||
return false;
|
||||
} else {
|
||||
reserved.newbshape = BaseShape::lookupInitialShape(cx, a->getClass(), a->getParent(),
|
||||
reserved.newbshape = EmptyShape::lookupInitialShape(cx, a->getClass(),
|
||||
a->getProto(), a->getParent(),
|
||||
b->getAllocKind());
|
||||
if (!reserved.newbshape)
|
||||
return false;
|
||||
|
@ -3966,7 +3967,8 @@ JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b,
|
|||
if (!b->generateOwnShape(cx))
|
||||
return false;
|
||||
} else {
|
||||
reserved.newashape = BaseShape::lookupInitialShape(cx, b->getClass(), b->getParent(),
|
||||
reserved.newashape = EmptyShape::lookupInitialShape(cx, b->getClass(),
|
||||
b->getProto(), b->getParent(),
|
||||
a->getAllocKind());
|
||||
if (!reserved.newashape)
|
||||
return false;
|
||||
|
@ -5310,7 +5312,7 @@ js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id)
|
|||
return true;
|
||||
}
|
||||
|
||||
const Shape *
|
||||
Shape *
|
||||
js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
|
||||
PropertyOp getter, StrictPropertyOp setter, uint32 slot,
|
||||
uintN attrs, uintN flags, intN shortid)
|
||||
|
@ -5331,9 +5333,9 @@ js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
|
|||
return obj->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid);
|
||||
}
|
||||
|
||||
const Shape *
|
||||
Shape *
|
||||
js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
|
||||
const Shape *shape, uintN attrs, uintN mask,
|
||||
Shape *shape, uintN attrs, uintN mask,
|
||||
PropertyOp getter, StrictPropertyOp setter)
|
||||
{
|
||||
/*
|
||||
|
@ -5409,7 +5411,7 @@ DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value,
|
|||
* update the attributes and property ops. A getter or setter is really
|
||||
* only half of a property.
|
||||
*/
|
||||
const Shape *shape = NULL;
|
||||
Shape *shape = NULL;
|
||||
if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
|
||||
JSObject *pobj;
|
||||
JSProperty *prop;
|
||||
|
@ -5426,7 +5428,7 @@ DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value,
|
|||
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
|
||||
return NULL;
|
||||
if (prop && pobj == obj) {
|
||||
shape = (const Shape *) prop;
|
||||
shape = (Shape *) prop;
|
||||
if (shape->isAccessorDescriptor()) {
|
||||
shape = obj->changeProperty(cx, shape, attrs,
|
||||
JSPROP_GETTER | JSPROP_SETTER,
|
||||
|
|
|
@ -596,7 +596,7 @@ struct JSObject : js::gc::Cell
|
|||
public:
|
||||
inline bool nativeEmpty() const;
|
||||
|
||||
const js::Shape *methodShapeChange(JSContext *cx, const js::Shape &shape);
|
||||
js::Shape *methodShapeChange(JSContext *cx, const js::Shape &shape);
|
||||
bool protoShapeChange(JSContext *cx);
|
||||
bool shadowingShapeChange(JSContext *cx, const js::Shape &shape);
|
||||
|
||||
|
@ -605,7 +605,7 @@ struct JSObject : js::gc::Cell
|
|||
* Defined in jsobjinlines.h, but not declared inline per standard style in
|
||||
* order to avoid gcc warnings.
|
||||
*/
|
||||
const js::Shape *methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp);
|
||||
js::Shape *methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp);
|
||||
|
||||
/* Whether method shapes can be added to this object. */
|
||||
inline bool canHaveMethodBarrier() const;
|
||||
|
@ -1212,7 +1212,7 @@ struct JSObject : js::gc::Cell
|
|||
* 1. getter and setter must be normalized based on flags (see jsscope.cpp).
|
||||
* 2. !isExtensible() checking must be done by callers.
|
||||
*/
|
||||
const js::Shape *addPropertyInternal(JSContext *cx, jsid id,
|
||||
js::Shape *addPropertyInternal(JSContext *cx, jsid id,
|
||||
JSPropertyOp getter, JSStrictPropertyOp setter,
|
||||
uint32 slot, uintN attrs,
|
||||
uintN flags, intN shortid,
|
||||
|
@ -1229,25 +1229,25 @@ struct JSObject : js::gc::Cell
|
|||
|
||||
public:
|
||||
/* Add a property whose id is not yet in this scope. */
|
||||
const js::Shape *addProperty(JSContext *cx, jsid id,
|
||||
js::Shape *addProperty(JSContext *cx, jsid id,
|
||||
JSPropertyOp getter, JSStrictPropertyOp setter,
|
||||
uint32 slot, uintN attrs,
|
||||
uintN flags, intN shortid, bool allowDictionary = true);
|
||||
|
||||
/* Add a data property whose id is not yet in this scope. */
|
||||
const js::Shape *addDataProperty(JSContext *cx, jsid id, uint32 slot, uintN attrs) {
|
||||
js::Shape *addDataProperty(JSContext *cx, jsid id, uint32 slot, uintN attrs) {
|
||||
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
|
||||
return addProperty(cx, id, NULL, NULL, slot, attrs, 0, 0);
|
||||
}
|
||||
|
||||
/* Add or overwrite a property for id in this scope. */
|
||||
const js::Shape *putProperty(JSContext *cx, jsid id,
|
||||
js::Shape *putProperty(JSContext *cx, jsid id,
|
||||
JSPropertyOp getter, JSStrictPropertyOp setter,
|
||||
uint32 slot, uintN attrs,
|
||||
uintN flags, intN shortid);
|
||||
|
||||
/* Change the given property into a sibling with the same id in this scope. */
|
||||
const js::Shape *changeProperty(JSContext *cx, const js::Shape *shape, uintN attrs, uintN mask,
|
||||
js::Shape *changeProperty(JSContext *cx, js::Shape *shape, uintN attrs, uintN mask,
|
||||
JSPropertyOp getter, JSStrictPropertyOp setter);
|
||||
|
||||
/* Remove the property named by id from this object. */
|
||||
|
@ -1691,7 +1691,7 @@ js_CheckForStringIndex(jsid id);
|
|||
* Find or create a property named by id in obj's scope, with the given getter
|
||||
* and setter, slot, attributes, and other members.
|
||||
*/
|
||||
extern const js::Shape *
|
||||
extern js::Shape *
|
||||
js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
|
||||
JSPropertyOp getter, JSStrictPropertyOp setter, uint32 slot,
|
||||
uintN attrs, uintN flags, intN shortid);
|
||||
|
@ -1701,9 +1701,9 @@ js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
|
|||
* it into a potentially new js::Shape. Return a pointer to the changed
|
||||
* or identical property.
|
||||
*/
|
||||
extern const js::Shape *
|
||||
extern js::Shape *
|
||||
js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
|
||||
const js::Shape *shape, uintN attrs, uintN mask,
|
||||
js::Shape *shape, uintN attrs, uintN mask,
|
||||
JSPropertyOp getter, JSStrictPropertyOp setter);
|
||||
|
||||
extern JSBool
|
||||
|
|
|
@ -330,7 +330,7 @@ JSObject::setStaticBlockScopeChain(JSObject *obj)
|
|||
* Property read barrier for deferred cloning of compiler-created function
|
||||
* objects optimized as typically non-escaping, ad-hoc methods in obj.
|
||||
*/
|
||||
inline const js::Shape *
|
||||
inline js::Shape *
|
||||
JSObject::methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp)
|
||||
{
|
||||
JS_ASSERT(nativeContains(cx, shape));
|
||||
|
@ -355,7 +355,7 @@ JSObject::methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp
|
|||
* watchpoint on the property is not triggered.
|
||||
*/
|
||||
uint32 slot = shape.slot();
|
||||
const js::Shape *newshape = methodShapeChange(cx, shape);
|
||||
js::Shape *newshape = methodShapeChange(cx, shape);
|
||||
if (!newshape)
|
||||
return NULL;
|
||||
JS_ASSERT(!newshape->isMethod());
|
||||
|
@ -1611,29 +1611,6 @@ JSObject *
|
|||
NewReshapedObject(JSContext *cx, js::types::TypeObject *type, JSObject *parent,
|
||||
gc::AllocKind kind, const Shape *shape);
|
||||
|
||||
inline Shape *
|
||||
GetInitialShapeForObject(JSContext* cx, Class *clasp, JSObject *parent,
|
||||
types::TypeObject *type, gc::AllocKind kind)
|
||||
{
|
||||
if (clasp->isNative()) {
|
||||
/* Share empty shapes on the type only if the object is similar to the proto. */
|
||||
if (type->proto &&
|
||||
clasp == type->proto->getClass() &&
|
||||
parent == type->proto->getParent()) {
|
||||
return type->getEmptyShape(cx, kind);
|
||||
}
|
||||
|
||||
return EmptyShape::create(cx, clasp, parent, gc::GetGCKindSlots(kind, clasp));
|
||||
}
|
||||
|
||||
Shape *empty = BaseShape::lookupInitialShape(cx, clasp, parent, kind);
|
||||
if (!empty)
|
||||
return NULL;
|
||||
JS_ASSERT(empty->isEmptyShape());
|
||||
|
||||
return empty;
|
||||
}
|
||||
|
||||
/*
|
||||
* As for gc::GetGCObjectKind, where numSlots is a guess at the final size of
|
||||
* the object, zero if the final size is unknown. This should only be used for
|
||||
|
|
|
@ -589,7 +589,7 @@ JSObject::checkShapeConsistency()
|
|||
# define CHECK_SHAPE_CONSISTENCY(obj) ((void)0)
|
||||
#endif
|
||||
|
||||
const Shape *
|
||||
Shape *
|
||||
JSObject::addProperty(JSContext *cx, jsid id,
|
||||
PropertyOp getter, StrictPropertyOp setter,
|
||||
uint32 slot, uintN attrs,
|
||||
|
@ -610,7 +610,7 @@ JSObject::addProperty(JSContext *cx, jsid id,
|
|||
return addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp, allowDictionary);
|
||||
}
|
||||
|
||||
const Shape *
|
||||
Shape *
|
||||
JSObject::addPropertyInternal(JSContext *cx, jsid id,
|
||||
PropertyOp getter, StrictPropertyOp setter,
|
||||
uint32 slot, uintN attrs,
|
||||
|
@ -705,7 +705,7 @@ CheckCanChangeAttrs(JSContext *cx, JSObject *obj, const Shape *shape, uintN *att
|
|||
return true;
|
||||
}
|
||||
|
||||
const Shape *
|
||||
Shape *
|
||||
JSObject::putProperty(JSContext *cx, jsid id,
|
||||
PropertyOp getter, StrictPropertyOp setter,
|
||||
uint32 slot, uintN attrs,
|
||||
|
@ -866,8 +866,8 @@ JSObject::putProperty(JSContext *cx, jsid id,
|
|||
return shape;
|
||||
}
|
||||
|
||||
const Shape *
|
||||
JSObject::changeProperty(JSContext *cx, const Shape *shape, uintN attrs, uintN mask,
|
||||
Shape *
|
||||
JSObject::changeProperty(JSContext *cx, Shape *shape, uintN attrs, uintN mask,
|
||||
PropertyOp getter, StrictPropertyOp setter)
|
||||
{
|
||||
JS_ASSERT(nativeContains(cx, *shape));
|
||||
|
@ -902,7 +902,7 @@ JSObject::changeProperty(JSContext *cx, const Shape *shape, uintN attrs, uintN m
|
|||
* removeProperty because it will free an allocated shape->slot, and
|
||||
* putProperty won't re-allocate it.
|
||||
*/
|
||||
const Shape *newShape = putProperty(cx, shape->propid(), getter, setter, shape->maybeSlot(),
|
||||
Shape *newShape = putProperty(cx, shape->propid(), getter, setter, shape->maybeSlot(),
|
||||
attrs, shape->flags, shape->maybeShortid());
|
||||
|
||||
CHECK_SHAPE_CONSISTENCY(this);
|
||||
|
@ -1087,7 +1087,7 @@ JSObject::generateOwnShape(JSContext *cx, Shape *newShape)
|
|||
return true;
|
||||
}
|
||||
|
||||
const Shape *
|
||||
Shape *
|
||||
JSObject::methodShapeChange(JSContext *cx, const Shape &shape)
|
||||
{
|
||||
JS_ASSERT(shape.isMethod());
|
||||
|
@ -1111,7 +1111,7 @@ JSObject::methodShapeChange(JSContext *cx, const Shape &shape)
|
|||
* method memoized in the property tree to a plain old function-valued
|
||||
* property.
|
||||
*/
|
||||
const Shape *result =
|
||||
Shape *result =
|
||||
putProperty(cx, shape.propid(), NULL, NULL, shape.slot(),
|
||||
shape.attrs,
|
||||
shape.getFlags() & ~Shape::METHOD,
|
||||
|
@ -1223,7 +1223,7 @@ Shape::setObjectFlag(JSContext *cx, BaseShape::Flag flag, Shape **listp)
|
|||
}
|
||||
|
||||
/* static */ inline HashNumber
|
||||
JSCompartment::BaseShapeEntry::hash(const js::BaseShape *base)
|
||||
BaseShapeEntry::hash(const js::BaseShape *base)
|
||||
{
|
||||
JS_ASSERT(!base->isOwned());
|
||||
|
||||
|
@ -1238,10 +1238,9 @@ JSCompartment::BaseShapeEntry::hash(const js::BaseShape *base)
|
|||
}
|
||||
|
||||
/* static */ inline bool
|
||||
JSCompartment::BaseShapeEntry::match(const BaseShapeEntry &entry, const BaseShape *lookup)
|
||||
BaseShapeEntry::match(UnownedBaseShape *key, const BaseShape *lookup)
|
||||
{
|
||||
BaseShape *key = entry.base;
|
||||
JS_ASSERT(!key->isOwned() && !lookup->isOwned());
|
||||
JS_ASSERT(!lookup->isOwned());
|
||||
|
||||
return key->flags == lookup->flags
|
||||
&& key->clasp == lookup->clasp
|
||||
|
@ -1250,83 +1249,29 @@ JSCompartment::BaseShapeEntry::match(const BaseShapeEntry &entry, const BaseShap
|
|||
&& key->setterObj == lookup->setterObj;
|
||||
}
|
||||
|
||||
static inline JSCompartment::BaseShapeEntry *
|
||||
LookupBaseShape(JSContext *cx, const BaseShape &base)
|
||||
{
|
||||
JSCompartment::BaseShapeSet &table = cx->compartment->baseShapes;
|
||||
|
||||
if (!table.initialized() && !table.init())
|
||||
return false;
|
||||
|
||||
JSCompartment::BaseShapeSet::AddPtr p = table.lookupForAdd(&base);
|
||||
if (p)
|
||||
return &const_cast<JSCompartment::BaseShapeEntry &>(*p);
|
||||
|
||||
BaseShape *nbase = js_NewGCBaseShape(cx);
|
||||
if (!nbase)
|
||||
return false;
|
||||
new (nbase) BaseShape(base);
|
||||
|
||||
JSCompartment::BaseShapeEntry entry;
|
||||
entry.base = static_cast<UnownedBaseShape *>(nbase);
|
||||
entry.shapes = NULL;
|
||||
|
||||
if (!table.relookupOrAdd(p, &base, entry))
|
||||
return NULL;
|
||||
|
||||
return &const_cast<JSCompartment::BaseShapeEntry &>(*p);
|
||||
}
|
||||
|
||||
/* static */ UnownedBaseShape *
|
||||
BaseShape::lookup(JSContext *cx, const BaseShape &base)
|
||||
{
|
||||
JSCompartment::BaseShapeEntry *entry = LookupBaseShape(cx, base);
|
||||
return entry ? entry->base : NULL;
|
||||
}
|
||||
BaseShapeSet &table = cx->compartment->baseShapes;
|
||||
|
||||
/* static */ Shape *
|
||||
BaseShape::lookupInitialShape(JSContext *cx, Class *clasp, JSObject *parent,
|
||||
AllocKind kind, uint32 objectFlags, Shape *initial)
|
||||
{
|
||||
BaseShape base(clasp, parent, objectFlags);
|
||||
JSCompartment::BaseShapeEntry *entry = LookupBaseShape(cx, base);
|
||||
if (!entry)
|
||||
if (!table.initialized() && !table.init())
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Hold a reference on the entry's base shape, which will keep the entry
|
||||
* from being swept during a GC under newShape below.
|
||||
*/
|
||||
BaseShape *nbase = entry->base;
|
||||
BaseShapeSet::AddPtr p = table.lookupForAdd(&base);
|
||||
if (p)
|
||||
return *p;
|
||||
|
||||
if (!entry->shapes) {
|
||||
entry->shapes = cx->new_<ShapeKindArray>();
|
||||
if (!entry->shapes)
|
||||
BaseShape *nbase_ = js_NewGCBaseShape(cx);
|
||||
if (!nbase_)
|
||||
return NULL;
|
||||
}
|
||||
new (nbase_) BaseShape(base);
|
||||
|
||||
Shape *&shape = entry->shapes->get(kind);
|
||||
UnownedBaseShape *nbase = static_cast<UnownedBaseShape *>(nbase_);
|
||||
|
||||
if (shape)
|
||||
return shape;
|
||||
|
||||
if (initial) {
|
||||
shape = initial;
|
||||
return initial;
|
||||
}
|
||||
|
||||
shape = JS_PROPERTY_TREE(cx).newShape(cx);
|
||||
if (!shape)
|
||||
if (!table.relookupOrAdd(p, &base, nbase))
|
||||
return NULL;
|
||||
return new (shape) EmptyShape(nbase, gc::GetGCKindSlots(kind, clasp));
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
BaseShape::insertInitialShape(JSContext *cx, AllocKind kind, const Shape *initial)
|
||||
{
|
||||
JSCompartment::BaseShapeEntry *entry = LookupBaseShape(cx, *initial->base());
|
||||
JS_ASSERT(entry && entry->base == initial->base() && entry->shapes);
|
||||
entry->shapes->get(kind) = const_cast<Shape *>(initial);
|
||||
return nbase;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1334,19 +1279,9 @@ JSCompartment::sweepBaseShapeTable(JSContext *cx)
|
|||
{
|
||||
if (baseShapes.initialized()) {
|
||||
for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) {
|
||||
JSCompartment::BaseShapeEntry &entry =
|
||||
const_cast<JSCompartment::BaseShapeEntry &>(e.front());
|
||||
if (!entry.base->isMarked()) {
|
||||
if (entry.shapes)
|
||||
cx->delete_(entry.shapes);
|
||||
UnownedBaseShape *base = e.front();
|
||||
if (!base->isMarked())
|
||||
e.removeFront();
|
||||
} else if (entry.shapes) {
|
||||
for (size_t i = 0; i < ShapeKindArray::SHAPE_COUNT; i++) {
|
||||
Shape *&shape = entry.shapes->getIndex(i);
|
||||
if (shape && !shape->isMarked())
|
||||
shape = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1387,3 +1322,82 @@ Bindings::setParent(JSContext *cx, JSObject *obj)
|
|||
return false;
|
||||
return Shape::setObjectParent(cx, obj, &lastBinding);
|
||||
}
|
||||
|
||||
/* static */ inline HashNumber
|
||||
InitialShapeEntry::hash(const Lookup &lookup)
|
||||
{
|
||||
JSDHashNumber hash = jsuword(lookup.clasp) >> 3;
|
||||
hash = JS_ROTATE_LEFT32(hash, 4) ^ (jsuword(lookup.proto) >> 3);
|
||||
hash = JS_ROTATE_LEFT32(hash, 4) ^ (jsuword(lookup.parent) >> 3);
|
||||
return hash + lookup.nfixed;
|
||||
}
|
||||
|
||||
/* static */ inline bool
|
||||
InitialShapeEntry::match(const InitialShapeEntry &key, const Lookup &lookup)
|
||||
{
|
||||
return lookup.clasp == key.shape->getObjectClass()
|
||||
&& lookup.proto == key.proto
|
||||
&& lookup.parent == key.shape->getObjectParent()
|
||||
&& lookup.nfixed == key.shape->numFixedSlots();
|
||||
}
|
||||
|
||||
/* static */ Shape *
|
||||
EmptyShape::lookupInitialShape(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent,
|
||||
AllocKind kind, uint32 objectFlags)
|
||||
{
|
||||
InitialShapeSet &table = cx->compartment->initialShapes;
|
||||
|
||||
if (!table.initialized() && !table.init())
|
||||
return NULL;
|
||||
|
||||
size_t nfixed = GetGCKindSlots(kind, clasp);
|
||||
InitialShapeEntry::Lookup lookup(clasp, proto, parent, nfixed);
|
||||
|
||||
InitialShapeSet::AddPtr p = table.lookupForAdd(lookup);
|
||||
if (p)
|
||||
return p->shape;
|
||||
|
||||
BaseShape base(clasp, parent, objectFlags);
|
||||
BaseShape *nbase = BaseShape::lookup(cx, base);
|
||||
if (!nbase)
|
||||
return NULL;
|
||||
|
||||
Shape *shape = JS_PROPERTY_TREE(cx).newShape(cx);
|
||||
if (!shape)
|
||||
return NULL;
|
||||
new (shape) EmptyShape(nbase, nfixed);
|
||||
|
||||
InitialShapeEntry entry;
|
||||
entry.shape = shape;
|
||||
entry.proto = proto;
|
||||
|
||||
if (!table.relookupOrAdd(p, lookup, entry))
|
||||
return NULL;
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
EmptyShape::insertInitialShape(JSContext *cx, Shape *shape, JSObject *proto)
|
||||
{
|
||||
InitialShapeEntry::Lookup lookup(shape->getObjectClass(), proto, shape->getObjectParent(),
|
||||
shape->numFixedSlots());
|
||||
|
||||
InitialShapeSet::Ptr p = cx->compartment->initialShapes.lookup(lookup);
|
||||
JS_ASSERT(p);
|
||||
|
||||
InitialShapeEntry &entry = const_cast<InitialShapeEntry &>(*p);
|
||||
entry.shape = shape;
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::sweepInitialShapeTable(JSContext *cx)
|
||||
{
|
||||
if (initialShapes.initialized()) {
|
||||
for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) {
|
||||
const InitialShapeEntry &entry = e.front();
|
||||
if (!entry.shape->isMarked() || (entry.proto && !entry.proto->isMarked()))
|
||||
e.removeFront();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,6 @@
|
|||
#include "jstypes.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsprvtd.h"
|
||||
#include "jspubtd.h"
|
||||
|
@ -346,7 +345,7 @@ class BaseShape : public js::gc::Cell
|
|||
{
|
||||
public:
|
||||
friend struct Shape;
|
||||
friend struct JSCompartment::BaseShapeEntry;
|
||||
friend struct BaseShapeEntry;
|
||||
|
||||
enum Flag {
|
||||
/* Owned by the referring shape. */
|
||||
|
@ -459,18 +458,6 @@ class BaseShape : public js::gc::Cell
|
|||
/* Lookup base shapes from the compartment's baseShapes table. */
|
||||
static UnownedBaseShape *lookup(JSContext *cx, const BaseShape &base);
|
||||
|
||||
/*
|
||||
* Lookup a base shape from the baseShapes table, returning any associated
|
||||
* initial shape and, constructing one (or reusing the 'shape' parameter)
|
||||
* if none was found.
|
||||
*/
|
||||
static Shape *lookupInitialShape(JSContext *cx, Class *clasp, JSObject *parent,
|
||||
gc::AllocKind kind, uint32 objectFlags = 0,
|
||||
Shape *initial = NULL);
|
||||
|
||||
/* Reinsert a possibly modified initial shape to the baseShapes table. */
|
||||
static void insertInitialShape(JSContext *cx, gc::AllocKind kind, const Shape *initial);
|
||||
|
||||
/* Get the canonical base shape. */
|
||||
inline UnownedBaseShape *unowned();
|
||||
|
||||
|
@ -511,6 +498,16 @@ BaseShape::baseUnowned()
|
|||
JS_ASSERT(isOwned() && unowned_); return unowned_;
|
||||
}
|
||||
|
||||
/* Entries for the per-compartment baseShapes set of unowned base shapes. */
|
||||
struct BaseShapeEntry
|
||||
{
|
||||
typedef const BaseShape *Lookup;
|
||||
|
||||
static inline HashNumber hash(const BaseShape *base);
|
||||
static inline bool match(UnownedBaseShape *key, const BaseShape *lookup);
|
||||
};
|
||||
typedef HashSet<UnownedBaseShape *, BaseShapeEntry, SystemAllocPolicy> BaseShapeSet;
|
||||
|
||||
struct Shape : public js::gc::Cell
|
||||
{
|
||||
friend struct ::JSObject;
|
||||
|
@ -958,47 +955,52 @@ struct EmptyShape : public js::Shape
|
|||
{
|
||||
EmptyShape(BaseShape *base, uint32 nfixed);
|
||||
|
||||
static EmptyShape *create(JSContext *cx, js::Class *clasp, JSObject *parent, uint32 nfixed) {
|
||||
BaseShape lookup(clasp, parent, 0);
|
||||
BaseShape *base = BaseShape::lookup(cx, lookup);
|
||||
if (!base)
|
||||
return NULL;
|
||||
/*
|
||||
* Lookup an initial shape matching the given parameters, creating an empty
|
||||
* shape if none was found.
|
||||
*/
|
||||
static Shape *lookupInitialShape(JSContext *cx, Class *clasp, JSObject *proto,
|
||||
JSObject *parent, gc::AllocKind kind, uint32 objectFlags = 0);
|
||||
|
||||
js::Shape *eprop = JS_PROPERTY_TREE(cx).newShape(cx);
|
||||
if (!eprop)
|
||||
return NULL;
|
||||
return new (eprop) EmptyShape(base, nfixed);
|
||||
}
|
||||
/* Reinsert an alternate initial shape to the baseShapes table. */
|
||||
static void insertInitialShape(JSContext *cx, Shape *shape, JSObject *proto);
|
||||
};
|
||||
|
||||
/* Heap-allocated array of shapes for each object allocation size. */
|
||||
class ShapeKindArray
|
||||
/*
|
||||
* Entries for the per-compartment initialShapes set indexing initial shapes
|
||||
* for objects in the compartment and the associated types.
|
||||
*/
|
||||
struct InitialShapeEntry
|
||||
{
|
||||
public:
|
||||
static const uint32 SHAPE_COUNT =
|
||||
((js::gc::FINALIZE_OBJECT_LAST - js::gc::FINALIZE_OBJECT0) / 2) + 1;
|
||||
/*
|
||||
* Initial shape to give to the object. This is an empty shape, except for
|
||||
* certain classes (e.g. String, RegExp) which may add certain baked-in
|
||||
* properties.
|
||||
*/
|
||||
js::Shape *shape;
|
||||
|
||||
ShapeKindArray() { PodZero(this); }
|
||||
/*
|
||||
* Matching prototype for the entry. The shape of an object determines its
|
||||
* prototype, but the prototype cannot be determined from the shape itself.
|
||||
*/
|
||||
JSObject *proto;
|
||||
|
||||
Shape *&get(gc::AllocKind kind) {
|
||||
JS_ASSERT(kind >= gc::FINALIZE_OBJECT0 && kind <= gc::FINALIZE_OBJECT_LAST);
|
||||
int i = (kind - gc::FINALIZE_OBJECT0) / 2;
|
||||
return shapes[i];
|
||||
}
|
||||
|
||||
Shape *&getIndex(size_t i) {
|
||||
JS_ASSERT(i < SHAPE_COUNT);
|
||||
return shapes[i];
|
||||
}
|
||||
|
||||
private:
|
||||
Shape *shapes[SHAPE_COUNT];
|
||||
|
||||
void staticAsserts() {
|
||||
JS_STATIC_ASSERT(gc::FINALIZE_OBJECT0 % 2 == 0);
|
||||
}
|
||||
/* State used to determine a match on an initial shape. */
|
||||
struct Lookup {
|
||||
Class *clasp;
|
||||
JSObject *proto;
|
||||
JSObject *parent;
|
||||
size_t nfixed;
|
||||
Lookup(Class *clasp, JSObject *proto, JSObject *parent, size_t nfixed)
|
||||
: clasp(clasp), proto(proto), parent(parent), nfixed(nfixed)
|
||||
{}
|
||||
};
|
||||
|
||||
static inline HashNumber hash(const Lookup &lookup);
|
||||
static inline bool match(const InitialShapeEntry &key, const Lookup &lookup);
|
||||
};
|
||||
typedef HashSet<InitialShapeEntry, InitialShapeEntry, SystemAllocPolicy> InitialShapeSet;
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/* js::Shape pointer tag bit indicating a collision. */
|
||||
|
|
|
@ -57,33 +57,6 @@
|
|||
#include "jsgcinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
inline js::EmptyShape *
|
||||
js::types::TypeObject::getEmptyShape(JSContext *cx, gc::AllocKind kind)
|
||||
{
|
||||
JS_ASSERT(!singleton);
|
||||
|
||||
/*
|
||||
* Empty shapes can only be on the default 'new' type for a prototype.
|
||||
* Objects with a common prototype use the same shape lineage, even if
|
||||
* their types differ.
|
||||
*/
|
||||
JS_ASSERT(proto->hasNewType(this));
|
||||
|
||||
if (!emptyShapes) {
|
||||
emptyShapes = cx->new_<ShapeKindArray>();
|
||||
if (!emptyShapes)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Shape *&empty = emptyShapes->get(kind);
|
||||
if (!empty) {
|
||||
empty = EmptyShape::create(cx, proto->getClass(), proto->getParent(),
|
||||
gc::GetGCKindSlots(kind, proto->getClass()));
|
||||
}
|
||||
|
||||
return static_cast<EmptyShape *>(empty);
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSObject::updateFlags(JSContext *cx, jsid id, bool isDefinitelyAtom)
|
||||
{
|
||||
|
@ -107,38 +80,6 @@ JSObject::extend(JSContext *cx, const js::Shape *shape, bool isDefinitelyAtom)
|
|||
|
||||
namespace js {
|
||||
|
||||
inline bool
|
||||
StringObject::init(JSContext *cx, JSString *str)
|
||||
{
|
||||
JS_ASSERT(nativeEmpty());
|
||||
JS_ASSERT(gc::GetGCKindSlots(getAllocKind()) == 2);
|
||||
|
||||
if (isDelegate()) {
|
||||
if (!assignInitialShape(cx))
|
||||
return false;
|
||||
} else {
|
||||
const js::Shape *shape =
|
||||
BaseShape::lookupInitialShape(cx, getClass(), getParent(),
|
||||
gc::FINALIZE_OBJECT2, 0,
|
||||
lastProperty());
|
||||
if (!shape)
|
||||
return false;
|
||||
if (shape != lastProperty()) {
|
||||
setLastPropertyInfallible(shape);
|
||||
} else {
|
||||
shape = assignInitialShape(cx);
|
||||
if (!shape)
|
||||
return false;
|
||||
BaseShape::insertInitialShape(cx, gc::FINALIZE_OBJECT2, shape);
|
||||
}
|
||||
}
|
||||
JS_ASSERT(!nativeEmpty());
|
||||
JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))->slot() == LENGTH_SLOT);
|
||||
|
||||
setStringThis(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void
|
||||
BaseShape::adoptUnowned(UnownedBaseShape *other)
|
||||
{
|
||||
|
|
|
@ -93,7 +93,7 @@ Bindings::ensureShape(JSContext *cx)
|
|||
gc::AllocKind kind = gc::FINALIZE_OBJECT4;
|
||||
JS_ASSERT(gc::GetGCKindSlots(kind) == CallObject::RESERVED_SLOTS + 1);
|
||||
|
||||
lastBinding = BaseShape::lookupInitialShape(cx, &CallClass, NULL, kind,
|
||||
lastBinding = EmptyShape::lookupInitialShape(cx, &CallClass, NULL, NULL, kind,
|
||||
BaseShape::VAROBJ);
|
||||
if (!lastBinding)
|
||||
return false;
|
||||
|
|
|
@ -3001,7 +3001,7 @@ static JSFunctionSpec string_static_methods[] = {
|
|||
JS_FS_END
|
||||
};
|
||||
|
||||
const Shape *
|
||||
Shape *
|
||||
StringObject::assignInitialShape(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(nativeEmpty());
|
||||
|
|
|
@ -222,7 +222,8 @@ ArrayBuffer::create(JSContext *cx, int32 nbytes)
|
|||
|
||||
JS_ASSERT(obj->getClass() == &ArrayBuffer::slowClass);
|
||||
|
||||
js::Shape *empty = BaseShape::lookupInitialShape(cx, &ArrayBufferClass, obj->getParent(),
|
||||
js::Shape *empty = EmptyShape::lookupInitialShape(cx, &ArrayBufferClass,
|
||||
obj->getProto(), obj->getParent(),
|
||||
gc::FINALIZE_OBJECT16);
|
||||
if (!empty)
|
||||
return false;
|
||||
|
@ -1381,7 +1382,8 @@ class TypedArrayTemplate
|
|||
|
||||
JS_ASSERT(obj->getClass() == slowClass());
|
||||
|
||||
js::Shape *empty = BaseShape::lookupInitialShape(cx, fastClass(), obj->getParent(),
|
||||
js::Shape *empty = EmptyShape::lookupInitialShape(cx, fastClass(),
|
||||
obj->getProto(), obj->getParent(),
|
||||
gc::FINALIZE_OBJECT8,
|
||||
BaseShape::NOT_EXTENSIBLE);
|
||||
if (!empty)
|
||||
|
|
|
@ -151,19 +151,10 @@ RegExpObject::init(JSContext *cx, JSLinearString *source, RegExpFlag flags)
|
|||
if (!assignInitialShape(cx))
|
||||
return false;
|
||||
} else {
|
||||
const js::Shape *shape =
|
||||
js::BaseShape::lookupInitialShape(cx, getClass(), getParent(),
|
||||
js::gc::FINALIZE_OBJECT8, 0,
|
||||
lastProperty());
|
||||
Shape *shape = assignInitialShape(cx);
|
||||
if (!shape)
|
||||
return false;
|
||||
if (shape == lastProperty()) {
|
||||
shape = assignInitialShape(cx);
|
||||
if (!shape)
|
||||
return false;
|
||||
js::BaseShape::insertInitialShape(cx, js::gc::FINALIZE_OBJECT8, shape);
|
||||
}
|
||||
setLastPropertyInfallible(shape);
|
||||
EmptyShape::insertInitialShape(cx, shape, getProto());
|
||||
}
|
||||
JS_ASSERT(!nativeEmpty());
|
||||
}
|
||||
|
|
|
@ -252,7 +252,7 @@ RegExpObject::execute(JSContext *cx, const jschar *chars, size_t length, size_t
|
|||
return getPrivate()->execute(cx, chars, length, lastIndex, allocScope, output);
|
||||
}
|
||||
|
||||
const Shape *
|
||||
Shape *
|
||||
RegExpObject::assignInitialShape(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(isRegExp());
|
||||
|
|
|
@ -187,7 +187,7 @@ class RegExpObject : public ::JSObject
|
|||
* encoding their initial properties. Return the shape after
|
||||
* changing this regular expression object's last property to it.
|
||||
*/
|
||||
const Shape *assignInitialShape(JSContext *cx);
|
||||
Shape *assignInitialShape(JSContext *cx);
|
||||
|
||||
RegExpObject();
|
||||
RegExpObject &operator=(const RegExpObject &reo);
|
||||
|
|
|
@ -52,6 +52,28 @@ JSObject::asString()
|
|||
|
||||
namespace js {
|
||||
|
||||
inline bool
|
||||
StringObject::init(JSContext *cx, JSString *str)
|
||||
{
|
||||
JS_ASSERT(nativeEmpty());
|
||||
JS_ASSERT(gc::GetGCKindSlots(getAllocKind()) == 2);
|
||||
|
||||
if (isDelegate()) {
|
||||
if (!assignInitialShape(cx))
|
||||
return false;
|
||||
} else {
|
||||
Shape *shape = assignInitialShape(cx);
|
||||
if (!shape)
|
||||
return false;
|
||||
EmptyShape::insertInitialShape(cx, shape, getProto());
|
||||
}
|
||||
JS_ASSERT(!nativeEmpty());
|
||||
JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))->slot() == LENGTH_SLOT);
|
||||
|
||||
setStringThis(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline StringObject *
|
||||
StringObject::create(JSContext *cx, JSString *str)
|
||||
{
|
||||
|
|
|
@ -92,7 +92,7 @@ class StringObject : public ::JSObject
|
|||
* encodes the initial length property. Return the shape after changing
|
||||
* this String object's last property to it.
|
||||
*/
|
||||
const js::Shape *assignInitialShape(JSContext *cx);
|
||||
Shape *assignInitialShape(JSContext *cx);
|
||||
|
||||
private:
|
||||
StringObject();
|
||||
|
|
Загрузка…
Ссылка в новой задаче