зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
f24ba25bbd
Коммит
96a8e9128a
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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_];
|
||||
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -182,6 +182,8 @@ struct JSCompartment
|
|||
|
||||
void *data;
|
||||
|
||||
js::ObjectMetadataCallback objectMetadataCallback;
|
||||
|
||||
private:
|
||||
js::WrapperMap crossCompartmentWrappers;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче