Bug 850026 - Allow metadata objects to be associated with JS objects, and add a hook for attaching metadata to newly created objects, r=luke.

This commit is contained in:
Brian Hackett 2013-05-30 17:37:22 -06:00
parent 590f37edd9
commit ceb33f4d35
21 changed files with 329 additions and 68 deletions

View File

@ -892,6 +892,74 @@ js::testingFunc_inParallelSection(JSContext *cx, unsigned argc, jsval *vp)
return true;
}
static JSObject *objectMetadataFunction = NULL;
static JSObject *
ShellObjectMetadataCallback(JSContext *cx)
{
Value thisv = UndefinedValue();
Value rval;
if (!Invoke(cx, thisv, ObjectValue(*objectMetadataFunction), 0, NULL, &rval)) {
cx->clearPendingException();
return NULL;
}
return rval.isObject() ? &rval.toObject() : NULL;
}
static JSBool
SetObjectMetadataCallback(JSContext *cx, unsigned argc, jsval *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setUndefined();
if (argc == 0 || !args[0].isObject() || !args[0].toObject().isFunction()) {
if (objectMetadataFunction)
JS_RemoveObjectRoot(cx, &objectMetadataFunction);
objectMetadataFunction = NULL;
js::SetObjectMetadataCallback(cx, NULL);
return true;
}
if (!objectMetadataFunction && !JS_AddObjectRoot(cx, &objectMetadataFunction))
return false;
objectMetadataFunction = &args[0].toObject();
js::SetObjectMetadataCallback(cx, ShellObjectMetadataCallback);
return true;
}
static JSBool
SetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (argc != 2 || !args[0].isObject() || !args[1].isObject()) {
JS_ReportError(cx, "Both arguments must be objects");
return false;
}
args.rval().setUndefined();
RootedObject obj(cx, &args[0].toObject());
RootedObject metadata(cx, &args[1].toObject());
return SetObjectMetadata(cx, obj, metadata);
}
static JSBool
GetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (argc != 1 || !args[0].isObject()) {
JS_ReportError(cx, "Argument must be an object");
return false;
}
args.rval().setObjectOrNull(GetObjectMetadata(&args[0].toObject()));
return true;
}
#ifndef JS_ION
JSBool
js::IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, Value *vp)
@ -1079,6 +1147,18 @@ static JSFunctionSpecWithHelp TestingFunctions[] = {
"inParallelSection()",
" True if this code is executing within a parallel section."),
JS_FN_HELP("setObjectMetadataCallback", SetObjectMetadataCallback, 1, 0,
"setObjectMetadataCallback(fn)",
" Specify function to supply metadata for all newly created objects."),
JS_FN_HELP("setObjectMetadata", SetObjectMetadata, 2, 0,
"setObjectMetadata(obj, metadataObj)",
" Change the metadata for an object."),
JS_FN_HELP("getObjectMetadata", GetObjectMetadata, 1, 0,
"getObjectMetadata(obj)",
" Get the metadata for an object."),
JS_FS_HELP_END
};

View File

@ -433,6 +433,11 @@ MacroAssembler::newGCThing(const Register &result, gc::AllocKind allocKind, Labe
branch32(Assembler::NotEqual, result, Imm32(0), fail);
#endif
// Don't execute the inline path if the compartment has an object metadata callback,
// as the metadata to use for the object may vary between executions of the op.
if (GetIonContext()->compartment->objectMetadataCallback)
jump(fail);
#ifdef JSGC_GENERATIONAL
Nursery &nursery = zone->rt->gcNursery;
if (nursery.isEnabled() && allocKind <= gc::FINALIZE_OBJECT_LAST) {

View File

@ -0,0 +1,39 @@
x = [1,2,3];
setObjectMetadata(x, {y:0});
assertEq(getObjectMetadata(x).y, 0);
incallback = false;
count = 0;
setObjectMetadataCallback(function(obj) {
if (incallback)
return null;
incallback = true;
var res = {count:++count, location:Error().stack};
incallback = false;
return res;
});
function Foo() {
this.x = 0;
this.y = 1;
}
function f() {
w = new Foo();
x = [1,2,3];
y = [2,3,5];
z = {a:0,b:1};
}
f();
var wc = getObjectMetadata(w).count;
var xc = getObjectMetadata(x).count;
var yc = getObjectMetadata(y).count;
var zc = getObjectMetadata(z).count;
assertEq(xc > wc, true);
assertEq(yc > xc, true);
assertEq(zc > yc, true);
assertEq(/\.js/.test(getObjectMetadata(x).location), true);

View File

@ -132,19 +132,6 @@ js::StringIsArrayIndex(JSLinearString *str, uint32_t *indexp)
return false;
}
Shape *
js::GetDenseArrayShape(JSContext *cx, HandleObject globalObj)
{
JS_ASSERT(globalObj);
JSObject *proto = globalObj->global().getOrCreateArrayPrototype(cx);
if (!proto)
return NULL;
return EmptyShape::getInitialShape(cx, &ArrayClass, proto, proto->getParent(),
gc::FINALIZE_OBJECT0);
}
bool
DoubleIndexToId(JSContext *cx, double index, MutableHandleId id)
{
@ -2821,7 +2808,8 @@ js_InitArrayClass(JSContext *cx, HandleObject obj)
if (!type)
return NULL;
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayClass, TaggedProto(proto), proto->getParent(),
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayClass, TaggedProto(proto),
proto->getParent(), NewObjectMetadata(cx),
gc::FINALIZE_OBJECT0));
RootedObject arrayProto(cx, JSObject::createArray(cx, gc::FINALIZE_OBJECT4, gc::TenuredHeap, shape, type, 0));
@ -2890,6 +2878,7 @@ NewArray(JSContext *cx, uint32_t length, JSObject *protoArg, NewObjectKind newKi
NewObjectCache::EntryIndex entry = -1;
if (newKind == GenericObject &&
!cx->compartment->objectMetadataCallback &&
cache.lookupGlobal(&ArrayClass, cx->global(), allocKind, &entry))
{
RootedObject obj(cx, cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, &ArrayClass)));
@ -2919,7 +2908,7 @@ NewArray(JSContext *cx, uint32_t length, JSObject *protoArg, NewObjectKind newKi
* See JSObject::createArray.
*/
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayClass, TaggedProto(proto),
cx->global(), gc::FINALIZE_OBJECT0));
cx->global(), NewObjectMetadata(cx), gc::FINALIZE_OBJECT0));
if (!shape)
return NULL;

View File

@ -87,10 +87,6 @@ WouldDefinePastNonwritableLength(JSContext *cx, HandleObject obj, uint32_t index
extern bool
CanonicalizeArrayLengthValue(JSContext *cx, HandleValue v, uint32_t *canonicalized);
/* Get the common shape used by all dense arrays with a prototype at globalObj. */
extern Shape *
GetDenseArrayShape(JSContext *cx, HandleObject globalObj);
extern JSBool
GetLengthProperty(JSContext *cx, HandleObject obj, uint32_t *lengthp);

View File

@ -110,6 +110,9 @@ NewObjectCache::fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *
inline JSObject *
NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_, js::gc::InitialHeap heap)
{
// The new object cache does not account for metadata attached via callbacks.
JS_ASSERT(!cx->compartment->objectMetadataCallback);
JS_ASSERT(unsigned(entry_) < mozilla::ArrayLength(entries));
Entry *entry = &entries[entry_];

View File

@ -42,6 +42,7 @@ JSCompartment::JSCompartment(Zone *zone)
lastCodeRelease(0),
analysisLifoAlloc(ANALYSIS_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
data(NULL),
objectMetadataCallback(NULL),
lastAnimationTime(0),
regExps(rt),
propertyTree(thisForCtor()),

View File

@ -182,6 +182,8 @@ struct JSCompartment
void *data;
js::ObjectMetadataCallback objectMetadataCallback;
private:
js::WrapperMap crossCompartmentWrappers;

View File

@ -1053,6 +1053,28 @@ js::AutoCTypesActivityCallback::AutoCTypesActivityCallback(JSContext *cx,
callback(cx, beginType);
}
JS_FRIEND_API(void)
js::SetObjectMetadataCallback(JSContext *cx, ObjectMetadataCallback callback)
{
// Clear any jitcode in the runtime, which behaves differently depending on
// whether there is a creation callback.
ReleaseAllJITCode(cx->runtime->defaultFreeOp());
cx->compartment->objectMetadataCallback = callback;
}
JS_FRIEND_API(bool)
js::SetObjectMetadata(JSContext *cx, JSHandleObject obj, JSHandleObject metadata)
{
return JSObject::setMetadata(cx, obj, metadata);
}
JS_FRIEND_API(JSObject *)
js::GetObjectMetadata(JSObject *obj)
{
return obj->getMetadata();
}
JS_FRIEND_API(JSBool)
js_DefineOwnProperty(JSContext *cx, JSObject *objArg, jsid idArg,
const js::PropertyDescriptor& descriptor, JSBool *bp)

View File

@ -324,8 +324,9 @@ struct TypeObject {
};
struct BaseShape {
js::Class *clasp;
JSObject *parent;
js::Class *clasp;
JSObject *parent;
JSObject *_1;
JSCompartment *compartment;
};
@ -1696,6 +1697,26 @@ assertEnteredPolicy(JSContext *cx, JSObject *obj, jsid id);
inline void assertEnteredPolicy(JSContext *cx, JSObject *obj, jsid id) {};
#endif
typedef JSObject *
(* ObjectMetadataCallback)(JSContext *cx);
/*
* Specify a callback to invoke when creating each JS object in the current
* compartment, which may return a metadata object to associate with the
* object. Objects with different metadata have different shape hierarchies,
* so for efficiency, objects should generally try to share metadata objects.
*/
JS_FRIEND_API(void)
SetObjectMetadataCallback(JSContext *cx, ObjectMetadataCallback callback);
/* Manipulate the metadata associated with an object. */
JS_FRIEND_API(bool)
SetObjectMetadata(JSContext *cx, JSHandleObject obj, JSHandleObject metadata);
JS_FRIEND_API(JSObject *)
GetObjectMetadata(JSObject *obj);
} /* namespace js */
extern JS_FRIEND_API(JSBool)

View File

@ -378,7 +378,7 @@ NewPropertyIteratorObject(JSContext *cx, unsigned flags)
return NULL;
Class *clasp = &PropertyIteratorObject::class_;
RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, NULL, NULL,
RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, NULL, NULL, NewObjectMetadata(cx),
ITERATOR_FINALIZE_KIND));
if (!shape)
return NULL;

View File

@ -1115,6 +1115,7 @@ JSObject::sealOrFreeze(JSContext *cx, HandleObject obj, ImmutabilityType it)
RootedShape last(cx, EmptyShape::getInitialShape(cx, obj->getClass(),
obj->getTaggedProto(),
obj->getParent(),
obj->getMetadata(),
obj->numFixedSlots(),
obj->lastProperty()->getObjectFlags()));
if (!last)
@ -1246,7 +1247,7 @@ NewObject(JSContext *cx, Class *clasp, types::TypeObject *type_, JSObject *paren
RootedTypeObject type(cx, type_);
RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(type->proto),
parent, kind));
parent, NewObjectMetadata(cx), kind));
if (!shape)
return NULL;
@ -1287,7 +1288,9 @@ js::NewObjectWithGivenProto(JSContext *cx, js::Class *clasp,
NewObjectCache &cache = cx->runtime->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
if (proto.isObject() && newKind == GenericObject &&
if (proto.isObject() &&
newKind == GenericObject &&
!cx->compartment->objectMetadataCallback &&
(!parent || parent == proto.toObject()->getParent()) && !proto.toObject()->isGlobal())
{
if (cache.lookupProto(clasp, proto.toObject(), allocKind, &entry)) {
@ -1345,7 +1348,11 @@ js::NewObjectWithClassProtoCommon(JSContext *cx, js::Class *clasp, JSObject *pro
NewObjectCache &cache = cx->runtime->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
if (parentArg->isGlobal() && protoKey != JSProto_Null && newKind == GenericObject) {
if (parentArg->isGlobal() &&
protoKey != JSProto_Null &&
newKind == GenericObject &&
!cx->compartment->objectMetadataCallback)
{
if (cache.lookupGlobal(clasp, &parentArg->asGlobal(), allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
if (obj)
@ -1387,7 +1394,10 @@ js::NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc
NewObjectCache &cache = cx->runtime->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
if (parent == type->proto->getParent() && newKind == GenericObject) {
if (parent == type->proto->getParent() &&
newKind == GenericObject &&
!cx->compartment->objectMetadataCallback)
{
if (cache.lookupType(&ObjectClass, type, allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, &ObjectClass));
if (obj)
@ -1493,10 +1503,13 @@ CreateThisForFunctionWithType(JSContext *cx, HandleTypeObject type, JSObject *pa
*/
gc::AllocKind kind = type->newScript->allocKind;
RootedObject res(cx, NewObjectWithType(cx, type, parent, kind, newKind));
if (res) {
RootedShape shape(cx, type->newScript->shape);
JS_ALWAYS_TRUE(JSObject::setLastProperty(cx, res, shape));
}
if (!res)
return NULL;
RootedObject metadata(cx, res->getMetadata());
RootedShape shape(cx, type->newScript->shape);
JS_ALWAYS_TRUE(JSObject::setLastProperty(cx, res, shape));
if (metadata && !JSObject::setMetadata(cx, res, metadata))
return NULL;
return res;
}
@ -1859,7 +1872,7 @@ JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *aArg, JSObject *bArg,
if (!a->generateOwnShape(cx))
return false;
} else {
reserved.newbshape = EmptyShape::getInitialShape(cx, aClass, aProto, a->getParent(),
reserved.newbshape = EmptyShape::getInitialShape(cx, aClass, aProto, a->getParent(), a->getMetadata(),
b->tenuredGetAllocKind());
if (!reserved.newbshape)
return false;
@ -1868,7 +1881,7 @@ JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *aArg, JSObject *bArg,
if (!b->generateOwnShape(cx))
return false;
} else {
reserved.newashape = EmptyShape::getInitialShape(cx, bClass, bProto, b->getParent(),
reserved.newashape = EmptyShape::getInitialShape(cx, bClass, bProto, b->getParent(), b->getMetadata(),
a->tenuredGetAllocKind());
if (!reserved.newashape)
return false;

View File

@ -528,6 +528,10 @@ class JSObject : public js::ObjectImpl
*/
inline JSObject *enclosingScope();
/* Access the metadata on an object. */
inline JSObject *getMetadata() const;
static bool setMetadata(JSContext *cx, js::HandleObject obj, js::HandleObject newMetadata);
inline js::GlobalObject &global() const;
using js::ObjectImpl::compartment;

View File

@ -251,6 +251,12 @@ JSObject::getParent() const
return lastProperty()->getObjectParent();
}
inline JSObject *
JSObject::getMetadata() const
{
return lastProperty()->getObjectMetadata();
}
inline JSObject *
JSObject::enclosingScope()
{
@ -308,6 +314,7 @@ JSObject::canRemoveLastProperty()
JS_ASSERT(!inDictionaryMode());
js::Shape *previous = lastProperty()->previous().get();
return previous->getObjectParent() == lastProperty()->getObjectParent()
&& previous->getObjectMetadata() == lastProperty()->getObjectMetadata()
&& previous->getObjectFlags() == lastProperty()->getObjectFlags();
}
@ -1684,9 +1691,12 @@ CopyInitializerObject(JSContext *cx, HandleObject baseobj, NewObjectKind newKind
if (!obj)
return NULL;
RootedObject metadata(cx, obj->getMetadata());
RootedShape lastProp(cx, baseobj->lastProperty());
if (!JSObject::setLastProperty(cx, obj, lastProp))
return NULL;
if (metadata && !JSObject::setMetadata(cx, obj, metadata))
return NULL;
return obj;
}
@ -1793,6 +1803,18 @@ DefineConstructorAndPrototype(JSContext *cx, HandleObject obj, JSProtoKey key, H
JSObject **ctorp = NULL,
gc::AllocKind ctorKind = JSFunction::FinalizeKind);
static JS_ALWAYS_INLINE JSObject *
NewObjectMetadata(JSContext *cx)
{
// The metadata callback is invoked before each created object, except when
// analysis is active as the callback may reenter JS.
if (JS_UNLIKELY((size_t)cx->compartment->objectMetadataCallback) && !cx->compartment->activeAnalysis) {
gc::AutoSuppressGC suppress(cx);
return cx->compartment->objectMetadataCallback(cx);
}
return NULL;
}
} /* namespace js */
extern JSObject *

View File

@ -93,7 +93,7 @@ Bindings::initWithTemporaryStorage(JSContext *cx, InternalBindingsHandle self,
gc::AllocKind allocKind = gc::FINALIZE_OBJECT2_BACKGROUND;
JS_ASSERT(gc::GetGCKindSlots(allocKind) == CallObject::RESERVED_SLOTS);
RootedShape initial(cx,
EmptyShape::getInitialShape(cx, &CallClass, NULL, cx->global(),
EmptyShape::getInitialShape(cx, &CallClass, NULL, cx->global(), NULL,
allocKind, BaseShape::VAROBJ | BaseShape::DELEGATE));
if (!initial)
return false;
@ -118,7 +118,7 @@ Bindings::initWithTemporaryStorage(JSContext *cx, InternalBindingsHandle self,
return false;
#endif
StackBaseShape base(cx->compartment, &CallClass, cx->global(),
StackBaseShape base(cx->compartment, &CallClass, cx->global(), NULL,
BaseShape::VAROBJ | BaseShape::DELEGATE);
UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);

View File

@ -559,7 +559,7 @@ ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, uint8_t *contents)
JS_ASSERT(obj->getClass() == &ArrayBufferClass);
js::Shape *empty = EmptyShape::getInitialShape(cx, &ArrayBufferClass,
obj->getProto(), obj->getParent(),
obj->getProto(), obj->getParent(), obj->getMetadata(),
gc::FINALIZE_OBJECT16_BACKGROUND);
if (!empty)
return NULL;
@ -1785,7 +1785,7 @@ class TypedArrayTemplate
// would just boil down to a slightly slower wrapper around the
// following code anyway:
js::Shape *empty = EmptyShape::getInitialShape(cx, fastClass(),
obj->getProto(), obj->getParent(),
obj->getProto(), obj->getParent(), obj->getMetadata(),
gc::FINALIZE_OBJECT8_BACKGROUND,
BaseShape::NOT_EXTENSIBLE);
if (!empty)

View File

@ -187,7 +187,7 @@ ArgumentsObject::create(JSContext *cx, HandleScript script, HandleFunction calle
return NULL;
RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(proto),
proto->getParent(), FINALIZE_KIND,
proto->getParent(), NewObjectMetadata(cx), FINALIZE_KIND,
BaseShape::INDEXED));
if (!shape)
return NULL;

View File

@ -294,7 +294,7 @@ DeclEnvObject::createTemplateObject(JSContext *cx, HandleFunction fun, gc::Initi
RootedShape emptyDeclEnvShape(cx);
emptyDeclEnvShape = EmptyShape::getInitialShape(cx, &DeclEnvClass, NULL,
cx->global(), FINALIZE_KIND,
cx->global(), NULL, FINALIZE_KIND,
BaseShape::DELEGATE);
if (!emptyDeclEnvShape)
return NULL;
@ -337,7 +337,7 @@ WithObject::create(JSContext *cx, HandleObject proto, HandleObject enclosing, ui
return NULL;
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &WithClass, TaggedProto(proto),
&enclosing->global(), FINALIZE_KIND));
&enclosing->global(), NULL, FINALIZE_KIND));
if (!shape)
return NULL;
@ -670,7 +670,7 @@ StaticBlockObject::create(JSContext *cx)
return NULL;
RootedShape emptyBlockShape(cx);
emptyBlockShape = EmptyShape::getInitialShape(cx, &BlockClass, NULL, NULL, FINALIZE_KIND,
emptyBlockShape = EmptyShape::getInitialShape(cx, &BlockClass, NULL, NULL, NULL, FINALIZE_KIND,
BaseShape::DELEGATE);
if (!emptyBlockShape)
return NULL;

View File

@ -49,24 +49,28 @@ GetterSetterWriteBarrierPostRemove(JSRuntime *rt, JSObject **objp)
}
inline
BaseShape::BaseShape(JSCompartment *comp, Class *clasp, JSObject *parent, uint32_t objectFlags)
BaseShape::BaseShape(JSCompartment *comp, Class *clasp, JSObject *parent, JSObject *metadata,
uint32_t objectFlags)
{
JS_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK));
mozilla::PodZero(this);
this->clasp = clasp;
this->parent = parent;
this->metadata = metadata;
this->flags = objectFlags;
this->compartment_ = comp;
}
inline
BaseShape::BaseShape(JSCompartment *comp, Class *clasp, JSObject *parent, uint32_t objectFlags,
uint8_t attrs, js::PropertyOp rawGetter, js::StrictPropertyOp rawSetter)
BaseShape::BaseShape(JSCompartment *comp, Class *clasp, JSObject *parent, JSObject *metadata,
uint32_t objectFlags, uint8_t attrs,
PropertyOp rawGetter, StrictPropertyOp rawSetter)
{
JS_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK));
mozilla::PodZero(this);
this->clasp = clasp;
this->parent = parent;
this->metadata = metadata;
this->flags = objectFlags;
this->rawGetter = rawGetter;
this->rawSetter = rawSetter;
@ -87,6 +91,7 @@ BaseShape::BaseShape(const StackBaseShape &base)
mozilla::PodZero(this);
this->clasp = base.clasp;
this->parent = base.parent;
this->metadata = base.metadata;
this->flags = base.flags;
this->rawGetter = base.rawGetter;
this->rawSetter = base.rawSetter;
@ -102,6 +107,7 @@ BaseShape::operator=(const BaseShape &other)
{
clasp = other.clasp;
parent = other.parent;
metadata = other.metadata;
flags = other.flags;
slotSpan_ = other.slotSpan_;
if (flags & HAS_GETTER_OBJECT) {
@ -135,6 +141,7 @@ StackBaseShape::StackBaseShape(Shape *shape)
: flags(shape->getObjectFlags()),
clasp(shape->getObjectClass()),
parent(shape->getObjectParent()),
metadata(shape->getObjectMetadata()),
compartment(shape->compartment())
{
updateGetterSetter(shape->attrs, shape->getter(), shape->setter());
@ -197,6 +204,7 @@ BaseShape::assertConsistency()
JS_ASSERT_IF(hasGetterObject(), getterObject() == unowned->getterObject());
JS_ASSERT_IF(hasSetterObject(), setterObject() == unowned->setterObject());
JS_ASSERT(getObjectParent() == unowned->getObjectParent());
JS_ASSERT(getObjectMetadata() == unowned->getObjectMetadata());
JS_ASSERT(getObjectFlags() == unowned->getObjectFlags());
}
#endif
@ -495,6 +503,9 @@ BaseShape::markChildren(JSTracer *trc)
if (parent)
MarkObject(trc, &parent, "parent");
if (metadata)
MarkObject(trc, &metadata, "metadata");
}
/*

View File

@ -298,7 +298,7 @@ Shape::replaceLastProperty(JSContext *cx, const StackBaseShape &base,
/* Treat as resetting the initial property of the shape hierarchy. */
AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
return EmptyShape::getInitialShape(cx, base.clasp, proto,
base.parent, kind,
base.parent, base.metadata, kind,
base.flags & BaseShape::OBJECT_FLAG_MASK);
}
@ -1050,6 +1050,41 @@ Shape::setObjectParent(JSContext *cx, JSObject *parent, TaggedProto proto, Shape
return replaceLastProperty(cx, base, proto, lastRoot);
}
/* static */ bool
JSObject::setMetadata(JSContext *cx, HandleObject obj, HandleObject metadata)
{
if (obj->inDictionaryMode()) {
StackBaseShape base(obj->lastProperty());
base.metadata = metadata;
UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
if (!nbase)
return false;
obj->lastProperty()->base()->adoptUnowned(nbase);
return true;
}
Shape *newShape = Shape::setObjectMetadata(cx, metadata, obj->getTaggedProto(), obj->shape_);
if (!newShape)
return false;
obj->shape_ = newShape;
return true;
}
/* static */ Shape *
Shape::setObjectMetadata(JSContext *cx, JSObject *metadata, TaggedProto proto, Shape *last)
{
if (last->getObjectMetadata() == metadata)
return last;
StackBaseShape base(last);
base.metadata = metadata;
RootedShape lastRoot(cx, last);
return replaceLastProperty(cx, base, proto, lastRoot);
}
/* static */ bool
js::ObjectImpl::preventExtensions(JSContext *cx, Handle<ObjectImpl*> obj)
{
@ -1154,6 +1189,7 @@ StackBaseShape::hash(const StackBaseShape *base)
HashNumber hash = base->flags;
hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->clasp) >> 3);
hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->parent) >> 3);
hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->metadata) >> 3);
hash = JS_ROTATE_LEFT32(hash, 4) ^ uintptr_t(base->rawGetter);
hash = JS_ROTATE_LEFT32(hash, 4) ^ uintptr_t(base->rawSetter);
return hash;
@ -1165,6 +1201,7 @@ StackBaseShape::match(UnownedBaseShape *key, const StackBaseShape *lookup)
return key->flags == lookup->flags
&& key->clasp == lookup->clasp
&& key->parent == lookup->parent
&& key->metadata == lookup->metadata
&& key->rawGetter == lookup->rawGetter
&& key->rawSetter == lookup->rawSetter;
}
@ -1176,6 +1213,10 @@ StackBaseShape::AutoRooter::trace(JSTracer *trc)
gc::MarkObjectRoot(trc, (JSObject**)&base->parent,
"StackBaseShape::AutoRooter parent");
}
if (base->metadata) {
gc::MarkObjectRoot(trc, (JSObject**)&base->metadata,
"StackBaseShape::AutoRooter metadata");
}
if ((base->flags & BaseShape::HAS_GETTER_OBJECT) && base->rawGetter) {
gc::MarkObjectRoot(trc, (JSObject**)&base->rawGetter,
"StackBaseShape::AutoRooter getter");
@ -1252,7 +1293,7 @@ InitialShapeEntry::InitialShapeEntry(const ReadBarriered<Shape> &shape, TaggedPr
inline InitialShapeEntry::Lookup
InitialShapeEntry::getLookup() const
{
return Lookup(shape->getObjectClass(), proto, shape->getObjectParent(),
return Lookup(shape->getObjectClass(), proto, shape->getObjectParent(), shape->getObjectMetadata(),
shape->numFixedSlots(), shape->getObjectFlags());
}
@ -1261,7 +1302,7 @@ InitialShapeEntry::hash(const Lookup &lookup)
{
HashNumber hash = uintptr_t(lookup.clasp) >> 3;
hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(lookup.proto.toWord()) >> 3);
hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(lookup.parent) >> 3);
hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(lookup.parent) >> 3) ^ (uintptr_t(lookup.metadata) >> 3);
return hash + lookup.nfixed;
}
@ -1272,12 +1313,14 @@ InitialShapeEntry::match(const InitialShapeEntry &key, const Lookup &lookup)
return lookup.clasp == shape->getObjectClass()
&& lookup.proto.toWord() == key.proto.toWord()
&& lookup.parent == shape->getObjectParent()
&& lookup.metadata == shape->getObjectMetadata()
&& lookup.nfixed == shape->numFixedSlots()
&& lookup.baseFlags == shape->getObjectFlags();
}
/* static */ Shape *
EmptyShape::getInitialShape(JSContext *cx, Class *clasp, TaggedProto proto, JSObject *parent,
EmptyShape::getInitialShape(JSContext *cx, Class *clasp, TaggedProto proto,
JSObject *parent, JSObject *metadata,
size_t nfixed, uint32_t objectFlags)
{
JS_ASSERT_IF(proto.isObject(), cx->compartment == proto.toObject()->compartment());
@ -1290,15 +1333,16 @@ EmptyShape::getInitialShape(JSContext *cx, Class *clasp, TaggedProto proto, JSOb
typedef InitialShapeEntry::Lookup Lookup;
InitialShapeSet::AddPtr p =
table.lookupForAdd(Lookup(clasp, proto, parent, nfixed, objectFlags));
table.lookupForAdd(Lookup(clasp, proto, parent, metadata, nfixed, objectFlags));
if (p)
return p->shape;
Rooted<TaggedProto> protoRoot(cx, proto);
RootedObject parentRoot(cx, parent);
RootedObject metadataRoot(cx, metadata);
StackBaseShape base(cx->compartment, clasp, parent, objectFlags);
StackBaseShape base(cx->compartment, clasp, parent, metadata, objectFlags);
Rooted<UnownedBaseShape*> nbase(cx, BaseShape::getUnowned(cx, base));
if (!nbase)
return NULL;
@ -1308,7 +1352,7 @@ EmptyShape::getInitialShape(JSContext *cx, Class *clasp, TaggedProto proto, JSOb
return NULL;
new (shape) EmptyShape(nbase, nfixed);
if (!table.relookupOrAdd(p, Lookup(clasp, protoRoot, parentRoot, nfixed, objectFlags),
if (!table.relookupOrAdd(p, Lookup(clasp, protoRoot, parentRoot, metadataRoot, nfixed, objectFlags),
InitialShapeEntry(shape, protoRoot)))
{
return NULL;
@ -1318,10 +1362,11 @@ EmptyShape::getInitialShape(JSContext *cx, Class *clasp, TaggedProto proto, JSOb
}
/* static */ Shape *
EmptyShape::getInitialShape(JSContext *cx, Class *clasp, TaggedProto proto, JSObject *parent,
EmptyShape::getInitialShape(JSContext *cx, Class *clasp, TaggedProto proto,
JSObject *parent, JSObject *metadata,
AllocKind kind, uint32_t objectFlags)
{
return getInitialShape(cx, clasp, proto, parent, GetGCKindSlots(kind, clasp), objectFlags);
return getInitialShape(cx, clasp, proto, parent, metadata, GetGCKindSlots(kind, clasp), objectFlags);
}
void
@ -1349,8 +1394,8 @@ NewObjectCache::invalidateEntriesForShape(JSContext *cx, HandleShape shape, Hand
EmptyShape::insertInitialShape(JSContext *cx, HandleShape shape, HandleObject proto)
{
InitialShapeEntry::Lookup lookup(shape->getObjectClass(), TaggedProto(proto),
shape->getObjectParent(), shape->numFixedSlots(),
shape->getObjectFlags());
shape->getObjectParent(), shape->getObjectMetadata(),
shape->numFixedSlots(), shape->getObjectFlags());
InitialShapeSet::Ptr p = cx->compartment->initialShapes.lookup(lookup);
JS_ASSERT(p);

View File

@ -266,6 +266,8 @@ class BaseShape : public js::gc::Cell
private:
Class *clasp; /* Class of referring object. */
HeapPtrObject parent; /* Parent of referring object. */
HeapPtrObject metadata; /* Optional holder of metadata about
* the referring object. */
JSCompartment *compartment_; /* Compartment shape belongs to. */
uint32_t flags; /* Vector of above flags. */
uint32_t slotSpan_; /* Object slot span for BaseShapes at
@ -289,18 +291,16 @@ class BaseShape : public js::gc::Cell
/* For owned BaseShapes, the shape's shape table. */
ShapeTable *table_;
#if JS_BITS_PER_WORD == 32
void *padding;
#endif
BaseShape(const BaseShape &base) MOZ_DELETE;
public:
void finalize(FreeOp *fop);
inline BaseShape(JSCompartment *comp, Class *clasp, JSObject *parent, uint32_t objectFlags);
inline BaseShape(JSCompartment *comp, Class *clasp, JSObject *parent, uint32_t objectFlags,
uint8_t attrs, PropertyOp rawGetter, StrictPropertyOp rawSetter);
inline BaseShape(JSCompartment *comp, Class *clasp, JSObject *parent, JSObject *metadata,
uint32_t objectFlags);
inline BaseShape(JSCompartment *comp, Class *clasp, JSObject *parent, JSObject *metadata,
uint32_t objectFlags, uint8_t attrs,
PropertyOp rawGetter, StrictPropertyOp rawSetter);
inline BaseShape(const StackBaseShape &base);
/* Not defined: BaseShapes must not be stack allocated. */
@ -317,6 +317,7 @@ class BaseShape : public js::gc::Cell
inline void setOwned(UnownedBaseShape *unowned);
JSObject *getObjectParent() const { return parent; }
JSObject *getObjectMetadata() const { return metadata; }
uint32_t getObjectFlags() const { return flags & OBJECT_FLAG_MASK; }
bool hasGetterObject() const { return !!(flags & HAS_GETTER_OBJECT); }
@ -396,6 +397,7 @@ struct StackBaseShape
uint32_t flags;
Class *clasp;
JSObject *parent;
JSObject *metadata;
PropertyOp rawGetter;
StrictPropertyOp rawSetter;
JSCompartment *compartment;
@ -404,15 +406,18 @@ struct StackBaseShape
: flags(base->flags & BaseShape::OBJECT_FLAG_MASK),
clasp(base->clasp),
parent(base->parent),
metadata(base->metadata),
rawGetter(NULL),
rawSetter(NULL),
compartment(base->compartment())
{}
StackBaseShape(JSCompartment *comp, Class *clasp, JSObject *parent, uint32_t objectFlags)
StackBaseShape(JSCompartment *comp, Class *clasp,
JSObject *parent, JSObject *metadata, uint32_t objectFlags)
: flags(objectFlags),
clasp(clasp),
parent(parent),
metadata(metadata),
rawGetter(NULL),
rawSetter(NULL),
compartment(comp)
@ -591,8 +596,10 @@ class Shape : public js::gc::Cell
Class *getObjectClass() const { return base()->clasp; }
JSObject *getObjectParent() const { return base()->parent; }
JSObject *getObjectMetadata() const { return base()->metadata; }
static Shape *setObjectParent(JSContext *cx, JSObject *obj, TaggedProto proto, Shape *last);
static Shape *setObjectMetadata(JSContext *cx, JSObject *metadata, TaggedProto proto, Shape *last);
static Shape *setObjectFlag(JSContext *cx, BaseShape::Flag flag, TaggedProto proto, Shape *last);
uint32_t getObjectFlags() const { return base()->getObjectFlags(); }
@ -899,9 +906,9 @@ struct EmptyShape : public js::Shape
* Lookup an initial shape matching the given parameters, creating an empty
* shape if none was found.
*/
static Shape *getInitialShape(JSContext *cx, Class *clasp, TaggedProto proto,
static Shape *getInitialShape(JSContext *cx, Class *clasp, TaggedProto proto, JSObject *metadata,
JSObject *parent, size_t nfixed, uint32_t objectFlags = 0);
static Shape *getInitialShape(JSContext *cx, Class *clasp, TaggedProto proto,
static Shape *getInitialShape(JSContext *cx, Class *clasp, TaggedProto proto, JSObject *metadata,
JSObject *parent, gc::AllocKind kind, uint32_t objectFlags = 0);
/*
@ -936,12 +943,13 @@ struct InitialShapeEntry
Class *clasp;
TaggedProto proto;
JSObject *parent;
JSObject *metadata;
uint32_t nfixed;
uint32_t baseFlags;
Lookup(Class *clasp, TaggedProto proto, JSObject *parent, uint32_t nfixed,
uint32_t baseFlags)
: clasp(clasp), proto(proto), parent(parent),
nfixed(nfixed), baseFlags(baseFlags)
Lookup(Class *clasp, TaggedProto proto, JSObject *parent, JSObject *metadata,
uint32_t nfixed, uint32_t baseFlags)
: clasp(clasp), proto(proto), parent(parent), metadata(metadata),
nfixed(nfixed), baseFlags(baseFlags)
{}
};