Get new object empty shapes with a common hash table, bug 701509.

This commit is contained in:
Brian Hackett 2011-11-10 17:50:05 -08:00
parent 09aa4d18f6
commit ee317367e6
22 changed files with 292 additions and 391 deletions

View File

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

View File

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

View File

@ -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:

View File

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

View File

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

View File

@ -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.

View File

@ -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.

View File

@ -413,8 +413,8 @@ NewIteratorObject(JSContext *cx, uintN flags)
if (!type)
return NULL;
Shape *emptyEnumeratorShape = BaseShape::lookupInitialShape(cx, &IteratorClass, NULL,
FINALIZE_OBJECT2);
Shape *emptyEnumeratorShape = EmptyShape::lookupInitialShape(cx, &IteratorClass, NULL, NULL,
FINALIZE_OBJECT2);
if (!emptyEnumeratorShape)
return NULL;

View File

@ -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,9 +3593,9 @@ js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
StackFrame *priv = js_FloatingFrameIfGenerator(cx, cx->fp());
Shape *emptyWithShape = BaseShape::lookupInitialShape(cx, &WithClass,
parent->getGlobal(),
FINALIZE_OBJECT4);
Shape *emptyWithShape = EmptyShape::lookupInitialShape(cx, &WithClass, proto,
parent->getGlobal(),
FINALIZE_OBJECT4);
if (!emptyWithShape)
return NULL;
@ -3627,8 +3627,8 @@ js_NewBlockObject(JSContext *cx)
if (!type)
return NULL;
Shape *emptyBlockShape = BaseShape::lookupInitialShape(cx, &BlockClass, NULL,
FINALIZE_OBJECT4);
Shape *emptyBlockShape = EmptyShape::lookupInitialShape(cx, &BlockClass, NULL, NULL,
FINALIZE_OBJECT4);
if (!emptyBlockShape)
return NULL;
@ -3957,8 +3957,9 @@ JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b,
if (!a->generateOwnShape(cx))
return false;
} else {
reserved.newbshape = BaseShape::lookupInitialShape(cx, a->getClass(), a->getParent(),
b->getAllocKind());
reserved.newbshape = EmptyShape::lookupInitialShape(cx, a->getClass(),
a->getProto(), a->getParent(),
b->getAllocKind());
if (!reserved.newbshape)
return false;
}
@ -3966,8 +3967,9 @@ JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b,
if (!b->generateOwnShape(cx))
return false;
} else {
reserved.newashape = BaseShape::lookupInitialShape(cx, b->getClass(), b->getParent(),
a->getAllocKind());
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,

View File

@ -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,11 +1212,11 @@ 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,
JSPropertyOp getter, JSStrictPropertyOp setter,
uint32 slot, uintN attrs,
uintN flags, intN shortid,
js::Shape **spp, bool allowDictionary);
js::Shape *addPropertyInternal(JSContext *cx, jsid id,
JSPropertyOp getter, JSStrictPropertyOp setter,
uint32 slot, uintN attrs,
uintN flags, intN shortid,
js::Shape **spp, bool allowDictionary);
bool toDictionaryMode(JSContext *cx);
@ -1229,26 +1229,26 @@ 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,
JSPropertyOp getter, JSStrictPropertyOp setter,
uint32 slot, uintN attrs,
uintN flags, intN shortid, bool allowDictionary = true);
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,
JSPropertyOp getter, JSStrictPropertyOp setter,
uint32 slot, uintN attrs,
uintN flags, intN shortid);
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,
JSPropertyOp getter, JSStrictPropertyOp setter);
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. */
bool removeProperty(JSContext *cx, jsid id);
@ -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

View File

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

View File

@ -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,8 +902,8 @@ 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(),
attrs, shape->flags, shape->maybeShortid());
Shape *newShape = putProperty(cx, shape->propid(), getter, setter, shape->maybeSlot(),
attrs, shape->flags, shape->maybeShortid());
CHECK_SHAPE_CONSISTENCY(this);
return newShape;
@ -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)
return NULL;
}
Shape *&shape = entry->shapes->get(kind);
if (shape)
return shape;
if (initial) {
shape = initial;
return initial;
}
shape = JS_PROPERTY_TREE(cx).newShape(cx);
if (!shape)
BaseShape *nbase_ = js_NewGCBaseShape(cx);
if (!nbase_)
return NULL;
return new (shape) EmptyShape(nbase, gc::GetGCKindSlots(kind, clasp));
}
new (nbase_) BaseShape(base);
/* 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);
UnownedBaseShape *nbase = static_cast<UnownedBaseShape *>(nbase_);
if (!table.relookupOrAdd(p, &base, nbase))
return NULL;
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();
}
}
}

View File

@ -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,46 +955,51 @@ 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];
}
/* 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)
{}
};
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);
}
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 */

View File

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

View File

@ -93,8 +93,8 @@ 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,
BaseShape::VAROBJ);
lastBinding = EmptyShape::lookupInitialShape(cx, &CallClass, NULL, NULL, kind,
BaseShape::VAROBJ);
if (!lastBinding)
return false;
}

View File

@ -3001,7 +3001,7 @@ static JSFunctionSpec string_static_methods[] = {
JS_FS_END
};
const Shape *
Shape *
StringObject::assignInitialShape(JSContext *cx)
{
JS_ASSERT(nativeEmpty());

View File

@ -222,8 +222,9 @@ ArrayBuffer::create(JSContext *cx, int32 nbytes)
JS_ASSERT(obj->getClass() == &ArrayBuffer::slowClass);
js::Shape *empty = BaseShape::lookupInitialShape(cx, &ArrayBufferClass, obj->getParent(),
gc::FINALIZE_OBJECT16);
js::Shape *empty = EmptyShape::lookupInitialShape(cx, &ArrayBufferClass,
obj->getProto(), obj->getParent(),
gc::FINALIZE_OBJECT16);
if (!empty)
return false;
obj->setLastPropertyInfallible(empty);
@ -1381,9 +1382,10 @@ class TypedArrayTemplate
JS_ASSERT(obj->getClass() == slowClass());
js::Shape *empty = BaseShape::lookupInitialShape(cx, fastClass(), obj->getParent(),
gc::FINALIZE_OBJECT8,
BaseShape::NOT_EXTENSIBLE);
js::Shape *empty = EmptyShape::lookupInitialShape(cx, fastClass(),
obj->getProto(), obj->getParent(),
gc::FINALIZE_OBJECT8,
BaseShape::NOT_EXTENSIBLE);
if (!empty)
return false;
obj->setLastPropertyInfallible(empty);

View File

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

View File

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

View File

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

View File

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

View File

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