Bug 650161 - Add a class hook that's called when an object is moved r=terrence

This commit is contained in:
Jon Coppeard 2014-09-02 11:07:22 +02:00
parent 8a2d317b16
commit d8d02056fe
10 changed files with 81 additions and 23 deletions

View File

@ -10,7 +10,7 @@
#define js_Class_h
#include "mozilla/NullPtr.h"
#include "jstypes.h"
#include "js/CallArgs.h"
@ -185,6 +185,9 @@ typedef JSObject *
typedef JSObject *
(* JSWeakmapKeyDelegateOp)(JSObject *obj);
typedef void
(* JSObjectMovedOp)(JSObject *obj, const JSObject *old);
/* js::Class operation signatures. */
namespace js {
@ -318,10 +321,19 @@ struct ClassExtension
* wrapped object is collected.
*/
JSWeakmapKeyDelegateOp weakmapKeyDelegateOp;
/*
* Optional hook called when an object is moved by a compacting GC.
*
* There may exist weak pointers to an object that are not traced through
* when the normal trace APIs are used, for example objects in the wrapper
* cache. This hook allows these pointers to be updated.
*/
JSObjectMovedOp objectMovedOp;
};
#define JS_NULL_CLASS_SPEC {nullptr,nullptr,nullptr,nullptr,nullptr,nullptr}
#define JS_NULL_CLASS_EXT {nullptr,nullptr,nullptr,false,nullptr}
#define JS_NULL_CLASS_EXT {nullptr,nullptr,nullptr,false,nullptr,nullptr}
struct ObjectOps
{
@ -361,7 +373,7 @@ typedef void (*JSClassInternal)();
struct JSClass {
JS_CLASS_MEMBERS(JSFinalizeOp);
void *reserved[31];
void *reserved[32];
};
#define JSCLASS_HAS_PRIVATE (1<<0) // objects have private slot
@ -540,6 +552,11 @@ IsObjectWithClass(const JS::Value &v, ESClassValue classValue, JSContext *cx);
inline bool
Unbox(JSContext *cx, JS::HandleObject obj, JS::MutableHandleValue vp);
#ifdef DEBUG
JS_FRIEND_API(bool)
HasObjectMovedOp(JSObject *obj);
#endif
} /* namespace js */
#endif /* js_Class_h */

View File

@ -1195,6 +1195,11 @@ js::IsInRequest(JSContext *cx)
{
return !!cx->runtime()->requestDepth;
}
bool
js::HasObjectMovedOp(JSObject *obj) {
return !!GetObjectClass(obj)->ext.objectMovedOp;
}
#endif
#ifdef JSGC_GENERATIONAL

View File

@ -262,7 +262,8 @@ namespace js {
innerObject, \
iteratorObject, \
isWrappedNative, \
js::proxy_WeakmapKeyDelegate \
js::proxy_WeakmapKeyDelegate, \
js::proxy_ObjectMoved \
}
#define PROXY_CLASS_WITH_EXT(name, extraSlots, flags, callOp, constructOp, ext) \
@ -377,6 +378,8 @@ extern JS_FRIEND_API(bool)
proxy_Convert(JSContext *cx, JS::HandleObject proxy, JSType hint, JS::MutableHandleValue vp);
extern JS_FRIEND_API(void)
proxy_Finalize(FreeOp *fop, JSObject *obj);
extern JS_FRIEND_API(void)
proxy_ObjectMoved(JSObject *obj, const JSObject *old);
extern JS_FRIEND_API(bool)
proxy_HasInstance(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool *bp);
extern JS_FRIEND_API(bool)

View File

@ -2172,27 +2172,17 @@ RelocateCell(Zone *zone, Cell *src, AllocKind thingKind, size_t thingSize)
// Copy source cell contents to destination.
memcpy(dst, src, thingSize);
// Fixup the pointer to inline object elements if necessary.
if (thingKind <= FINALIZE_OBJECT_LAST) {
JSObject *srcObj = static_cast<JSObject *>(src);
JSObject *dstObj = static_cast<JSObject *>(dst);
// Fixup the pointer to inline object elements if necessary.
if (srcObj->hasFixedElements())
dstObj->setFixedElements();
if (srcObj->is<ArrayBufferObject>()) {
// We must fix up any inline data pointers while we know the source
// object and before we mark any of the views.
ArrayBufferObject::fixupDataPointerAfterMovingGC(
srcObj->as<ArrayBufferObject>(), dstObj->as<ArrayBufferObject>());
} else if (srcObj->is<TypedArrayObject>()) {
TypedArrayObject &typedArray = srcObj->as<TypedArrayObject>();
if (!typedArray.hasBuffer()) {
JS_ASSERT(srcObj->getPrivate() ==
srcObj->fixedData(TypedArrayObject::FIXED_DATA_START));
dstObj->setPrivate(dstObj->fixedData(TypedArrayObject::FIXED_DATA_START));
}
}
// Call object moved hook if present.
if (JSObjectMovedOp op = srcObj->getClass()->ext.objectMovedOp)
op(dstObj, srcObj);
JS_ASSERT_IF(dstObj->isNative(),
!PtrIsInRange((const Value*)dstObj->getDenseElements(), src, thingSize));

View File

@ -362,6 +362,11 @@ BaseProxyHandler::finalize(JSFreeOp *fop, JSObject *proxy) const
{
}
void
BaseProxyHandler::objectMoved(JSObject *proxy, const JSObject *old) const
{
}
JSObject *
BaseProxyHandler::weakmapKeyDelegate(JSObject *proxy) const
{
@ -2878,6 +2883,13 @@ js::proxy_Finalize(FreeOp *fop, JSObject *obj)
obj->as<ProxyObject>().handler()->finalize(fop, obj);
}
void
js::proxy_ObjectMoved(JSObject *obj, const JSObject *old)
{
JS_ASSERT(obj->is<ProxyObject>());
obj->as<ProxyObject>().handler()->objectMoved(obj, old);
}
bool
js::proxy_HasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp)
{

View File

@ -220,6 +220,7 @@ class JS_FRIEND_API(BaseProxyHandler)
virtual bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp) const;
virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp) const;
virtual void finalize(JSFreeOp *fop, JSObject *proxy) const;
virtual void objectMoved(JSObject *proxy, const JSObject *old) const;
virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const;
virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp) const;

View File

@ -122,7 +122,14 @@ const Class ArrayBufferObject::class_ = {
nullptr, /* construct */
ArrayBufferObject::obj_trace,
JS_NULL_CLASS_SPEC,
JS_NULL_CLASS_EXT
{
nullptr, /* outerObject */
nullptr, /* innerObject */
nullptr, /* iteratorObject */
false, /* isWrappedNative */
nullptr, /* weakmapKeyDelegateOp */
ArrayBufferObject::objectMoved
}
};
const JSFunctionSpec ArrayBufferObject::jsfuncs[] = {
@ -929,8 +936,11 @@ ArrayBufferObject::sweep(JSCompartment *compartment)
}
/* static */ void
ArrayBufferObject::fixupDataPointerAfterMovingGC(const ArrayBufferObject &src, ArrayBufferObject &dst)
ArrayBufferObject::objectMoved(JSObject *obj, const JSObject *old)
{
ArrayBufferObject &dst = obj->as<ArrayBufferObject>();
const ArrayBufferObject &src = old->as<ArrayBufferObject>();
// Fix up possible inline data pointer.
const size_t reservedSlots = JSCLASS_RESERVED_SLOTS(&ArrayBufferObject::class_);
if (src.dataPointer() == src.fixedData(reservedSlots))

View File

@ -160,7 +160,7 @@ class ArrayBufferObject : public JSObject
static void sweep(JSCompartment *rt);
static void fixupDataPointerAfterMovingGC(const ArrayBufferObject &src, ArrayBufferObject &dst);
static void objectMoved(JSObject *obj, const JSObject *old);
static void resetArrayBufferList(JSCompartment *rt);
static bool saveArrayBufferList(JSCompartment *c, ArrayBufferVector &vector);

View File

@ -134,6 +134,16 @@ TypedArrayObject::dataOffset()
return JSObject::getPrivateDataOffset(DATA_SLOT);
}
/* static */ void
TypedArrayObject::ObjectMoved(JSObject *obj, const JSObject *old)
{
const TypedArrayObject &src = old->as<TypedArrayObject>();
if (!src.hasBuffer()) {
JS_ASSERT(old->getPrivate() == old->fixedData(FIXED_DATA_START));
obj->setPrivate(obj->fixedData(FIXED_DATA_START));
}
}
/* Helper clamped uint8_t type */
uint32_t JS_FASTCALL
@ -2222,7 +2232,15 @@ IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double)
nullptr, /* hasInstance */ \
nullptr, /* construct */ \
ArrayBufferViewObject::trace, /* trace */ \
TYPED_ARRAY_CLASS_SPEC(_typedArray) \
TYPED_ARRAY_CLASS_SPEC(_typedArray), \
{ \
nullptr, /* outerObject */ \
nullptr, /* innerObject */ \
nullptr, /* iteratorObject */ \
false, /* isWrappedNative */ \
nullptr, /* weakmapKeyDelegateOp */ \
TypedArrayObject::ObjectMoved \
} \
}
template<typename NativeType>

View File

@ -125,6 +125,8 @@ class TypedArrayObject : public ArrayBufferViewObject
static int dataOffset();
static bool isOriginalLengthGetter(Scalar::Type type, Native native);
static void ObjectMoved(JSObject *obj, const JSObject *old);
};
inline bool