From 9f938c20cc7771d8c2686b5738afa8e98f507765 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Tue, 2 Sep 2014 11:07:22 +0200 Subject: [PATCH] Bug 650161 - Add a class hook that's called when an object is moved r=terrence --- js/public/Class.h | 23 ++++++++++++++++++++--- js/src/jsfriendapi.cpp | 5 +++++ js/src/jsfriendapi.h | 5 ++++- js/src/jsgc.cpp | 20 +++++--------------- js/src/jsproxy.cpp | 12 ++++++++++++ js/src/jsproxy.h | 1 + js/src/vm/ArrayBufferObject.cpp | 14 ++++++++++++-- js/src/vm/ArrayBufferObject.h | 2 +- js/src/vm/TypedArrayObject.cpp | 20 +++++++++++++++++++- js/src/vm/TypedArrayObject.h | 2 ++ 10 files changed, 81 insertions(+), 23 deletions(-) diff --git a/js/public/Class.h b/js/public/Class.h index bf96371a63e0..e2172598c4fd 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -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 */ diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index e8e05cdb574e..9ea44eef814d 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -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 diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index bc57f046c609..bb50ce6d803b 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -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) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 852e692d8e50..f7509f682e1d 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -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(src); JSObject *dstObj = static_cast(dst); + + // Fixup the pointer to inline object elements if necessary. if (srcObj->hasFixedElements()) dstObj->setFixedElements(); - if (srcObj->is()) { - // 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(), dstObj->as()); - } else if (srcObj->is()) { - TypedArrayObject &typedArray = srcObj->as(); - 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)); diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index f27815523ecb..b29c046d8fa7 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -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().handler()->finalize(fop, obj); } +void +js::proxy_ObjectMoved(JSObject *obj, const JSObject *old) +{ + JS_ASSERT(obj->is()); + obj->as().handler()->objectMoved(obj, old); +} + bool js::proxy_HasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp) { diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 8f3a9b1b9f52..4be6da78d54e 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -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; diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index 82983a7c864f..9cc95337c712 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -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(); + const ArrayBufferObject &src = old->as(); + // Fix up possible inline data pointer. const size_t reservedSlots = JSCLASS_RESERVED_SLOTS(&ArrayBufferObject::class_); if (src.dataPointer() == src.fixedData(reservedSlots)) diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h index fee20652000d..da5f166224b9 100644 --- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -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); diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index fd12e0d30646..d9e7a58a45a9 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -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(); + 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 diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h index 0fdbbba4a6d8..35393fee8a62 100644 --- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -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