nsWrapperCache.h (30475B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef nsWrapperCache_h___ 8 #define nsWrapperCache_h___ 9 10 #include <type_traits> 11 12 #include "js/HeapAPI.h" 13 #include "js/RootingAPI.h" 14 #include "js/TracingAPI.h" 15 #include "js/TypeDecls.h" 16 #include "mozilla/Assertions.h" 17 #include "mozilla/RustCell.h" 18 #include "mozilla/ServoUtils.h" 19 #include "nsCycleCollectionParticipant.h" 20 #include "nsISupports.h" 21 #include "nsISupportsUtils.h" 22 23 namespace mozilla::dom::binding_detail { 24 class CastableToWrapperCacheHelper; 25 } // namespace mozilla::dom::binding_detail 26 27 #define NS_WRAPPERCACHE_IID \ 28 {0x6f3179a1, 0x36f7, 0x4a5c, {0x8c, 0xf1, 0xad, 0xc8, 0x7c, 0xde, 0x3e, 0x87}} 29 30 // There are two sets of flags used by DOM nodes. One comes from reusing the 31 // remaining bits of the inherited nsWrapperCache flags (mFlags), and another is 32 // exclusive to nsINode (mBoolFlags). 33 // 34 // Both sets of flags are 32 bits. On 64-bit platforms, this can cause two 35 // wasted 32-bit fields due to alignment requirements. Some compilers are 36 // smart enough to coalesce the fields if we make mBoolFlags the first member 37 // of nsINode, but others (such as MSVC, but that's not officially supported 38 // by us anymore) are not. Also note that this kind of coalascing tends to 39 // interact poorly with rust's bindgen, see: 40 // https://github.com/rust-lang/rust-bindgen/issues/380 41 // 42 // So we just store mBoolFlags directly on nsWrapperCache on 64-bit platforms. 43 // This may waste space for some other nsWrapperCache-derived objects that have 44 // a 32-bit field as their first member, but those objects are unlikely to be as 45 // numerous or performance-critical as DOM nodes. 46 #ifdef HAVE_64BIT_BUILD 47 static_assert(sizeof(void*) == 8, "These architectures should be 64-bit"); 48 # define BOOL_FLAGS_ON_WRAPPER_CACHE 49 #else 50 static_assert(sizeof(void*) == 4, "Only support 32-bit and 64-bit"); 51 #endif 52 53 /** 54 * Class to store the wrapper for an object. This can only be used with objects 55 * that only have one non-security wrapper at a time (for an XPCWrappedNative 56 * this is usually ensured by setting an explicit parent in the PreCreate hook 57 * for the class). 58 * 59 * An instance of nsWrapperCache can be gotten from an object that implements 60 * a wrapper cache by calling QueryInterface on it. Note that this breaks XPCOM 61 * rules a bit (this object doesn't derive from nsISupports). 62 * 63 * The cache can store objects other than wrappers. We allow wrappers to use a 64 * separate JSObject to store their state (mostly expandos). If the wrapper is 65 * collected and we want to preserve this state we actually store the state 66 * object in the cache. 67 * 68 * The cache can store 3 types of objects: a DOM binding object (regular JS 69 * object or proxy), an nsOuterWindowProxy or an XPCWrappedNative wrapper. 70 * 71 * The finalizer for the wrapper clears the cache. 72 * 73 * A compacting GC can move the wrapper object. Pointers to moved objects are 74 * usually found and updated by tracing the heap, however non-preserved wrappers 75 * are weak references and are not traced, so another approach is 76 * necessary. Instead a class hook (objectMovedOp) is provided that is called 77 * when an object is moved and is responsible for ensuring pointers are 78 * updated. It does this by calling UpdateWrapper() on the wrapper 79 * cache. SetWrapper() asserts that the hook is implemented for any wrapper set. 80 * 81 * A number of the methods are implemented in nsWrapperCacheInlines.h because we 82 * have to include some JS headers that don't play nicely with the rest of the 83 * codebase. Include nsWrapperCacheInlines.h if you need to call those methods. 84 */ 85 86 class JS_HAZ_ROOTED nsWrapperCache { 87 public: 88 NS_INLINE_DECL_STATIC_IID(NS_WRAPPERCACHE_IID) 89 90 nsWrapperCache() = default; 91 ~nsWrapperCache() { 92 // Preserved wrappers should never end up getting cleared, but this can 93 // happen during shutdown when a leaked wrapper object is finalized, causing 94 // its wrapper to be cleared. 95 MOZ_ASSERT(!PreservingWrapper() || js::RuntimeIsBeingDestroyed(), 96 "Destroying cache with a preserved wrapper!"); 97 } 98 99 /** 100 * Get the cached wrapper. 101 * 102 * This getter clears the gray bit before handing out the JSObject which means 103 * that the object is guaranteed to be kept alive past the next CC. 104 */ 105 JSObject* GetWrapper() const; 106 107 /** 108 * Get the cached wrapper. 109 * 110 * This getter does not change the color of the JSObject meaning that the 111 * object returned is not guaranteed to be kept alive past the next CC. 112 * 113 * This should only be called if you are certain that the return value won't 114 * be passed into a JSAPI function and that it won't be stored without being 115 * rooted (or otherwise signaling the stored value to the CC). 116 */ 117 JSObject* GetWrapperPreserveColor() const; 118 119 /** 120 * Get the cached wrapper. 121 * 122 * This getter does not check whether the wrapper is dead and in the process 123 * of being finalized. 124 * 125 * This should only be called if you really need to see the raw contents of 126 * this cache, for example as part of finalization. Don't store the result 127 * anywhere or pass it into JSAPI functions that may cause the value to 128 * escape. 129 */ 130 JSObject* GetWrapperMaybeDead() const { return mWrapper; } 131 132 #ifdef DEBUG 133 private: 134 static bool HasJSObjectMovedOp(JSObject* aWrapper); 135 136 static void AssertUpdatedWrapperZone(const JSObject* aNewObject, 137 const JSObject* aOldObject); 138 139 public: 140 #endif 141 142 void SetWrapper(JSObject* aWrapper) { 143 MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!"); 144 MOZ_ASSERT(aWrapper, "Use ClearWrapper!"); 145 MOZ_ASSERT(HasJSObjectMovedOp(aWrapper), 146 "Object has not provided the hook to update the wrapper if it " 147 "is moved"); 148 149 SetWrapperJSObject(aWrapper); 150 } 151 152 /** 153 * Clear the cache. 154 */ 155 void ClearWrapper() { 156 // Preserved wrappers should never end up getting cleared, but this can 157 // happen during shutdown when a leaked wrapper object is finalized, causing 158 // its wrapper to be cleared. 159 MOZ_ASSERT(!PreservingWrapper() || js::RuntimeIsBeingDestroyed(), 160 "Clearing a preserved wrapper!"); 161 SetWrapperJSObject(nullptr); 162 } 163 164 /** 165 * Clear the cache if it still contains a specific wrapper object. This should 166 * be called from the finalizer for the wrapper. 167 */ 168 void ClearWrapper(JSObject* obj) { 169 if (obj == mWrapper) { 170 ClearWrapper(); 171 } 172 } 173 174 /** 175 * Update the wrapper when the object moves between globals. 176 */ 177 template <typename T> 178 void UpdateWrapperForNewGlobal(T* aScriptObjectHolder, JSObject* aNewWrapper); 179 180 /** 181 * Update the wrapper if the object it contains is moved. 182 * 183 * This method must be called from the objectMovedOp class extension hook for 184 * any wrapper cached object. 185 */ 186 void UpdateWrapper(JSObject* aNewObject, const JSObject* aOldObject) { 187 #ifdef DEBUG 188 AssertUpdatedWrapperZone(aNewObject, aOldObject); 189 #endif 190 if (mWrapper) { 191 MOZ_ASSERT(mWrapper == aOldObject); 192 mWrapper = aNewObject; 193 if (PreservingWrapper() && !JS::ObjectIsTenured(mWrapper)) { 194 // Can pass prevp as null here since a previous store buffer entry has 195 // been cleared by the current nursery collection. 196 JS::HeapObjectPostWriteBarrier(&mWrapper, nullptr, mWrapper); 197 } 198 } 199 } 200 201 bool PreservingWrapper() const { 202 return HasWrapperFlag(WRAPPER_BIT_PRESERVED); 203 } 204 205 /** 206 * Wrap the object corresponding to this wrapper cache. If non-null is 207 * returned, the object has already been stored in the wrapper cache. 208 */ 209 virtual JSObject* WrapObject(JSContext* cx, 210 JS::Handle<JSObject*> aGivenProto) = 0; 211 212 /** 213 * Returns true if the object has a wrapper that is known live from the point 214 * of view of cycle collection. 215 */ 216 bool HasKnownLiveWrapper() const; 217 218 /** 219 * Returns true if the object has a known-live wrapper (from the CC point of 220 * view) and all the GC things it is keeping alive are already known-live from 221 * CC's point of view. 222 */ 223 bool HasKnownLiveWrapperAndDoesNotNeedTracing(nsISupports* aThis); 224 225 bool HasNothingToTrace(nsISupports* aThis); 226 227 /** 228 * Mark our wrapper, if any, as live as far as the CC is concerned. 229 */ 230 void MarkWrapperLive(); 231 232 // Only meant to be called by code that preserves a wrapper. 233 void SetPreservingWrapper(bool aPreserve) { 234 if (aPreserve) { 235 SetWrapperFlags(WRAPPER_BIT_PRESERVED); 236 } else { 237 UnsetWrapperFlags(WRAPPER_BIT_PRESERVED); 238 } 239 } 240 241 void TraceWrapper(const TraceCallbacks& aCallbacks, void* aClosure) { 242 if (PreservingWrapper() && mWrapper) { 243 aCallbacks.Trace(this, "Preserved wrapper", aClosure); 244 } 245 } 246 247 /* 248 * The following methods for getting and manipulating flags allow the unused 249 * bits of mFlags to be used by derived classes. 250 */ 251 252 using FlagsType = uint32_t; 253 254 FlagsType GetFlags() const { 255 MOZ_ASSERT(NS_IsMainThread()); 256 MOZ_ASSERT(!mozilla::IsInServoTraversal()); 257 return mFlags.Get() & ~kWrapperFlagsMask; 258 } 259 260 // This can be called from stylo threads too, so it needs to be atomic, as 261 // this value may be mutated from multiple threads during servo traversal from 262 // rust. 263 bool HasFlag(FlagsType aFlag) const { 264 MOZ_ASSERT((aFlag & kWrapperFlagsMask) == 0, "Bad flag mask"); 265 return __atomic_load_n(mFlags.AsPtr(), __ATOMIC_RELAXED) & aFlag; 266 } 267 268 // Identical to HasFlag, but more explicit about its handling of multiple 269 // flags. This can be called from stylo threads too. 270 bool HasAnyOfFlags(FlagsType aFlags) const { return HasFlag(aFlags); } 271 272 // This can also be called from stylo, in the sequential part of the 273 // traversal, though it's probably not worth differentiating them for the 274 // purposes of assertions. 275 bool HasAllFlags(FlagsType aFlags) const { 276 MOZ_ASSERT((aFlags & kWrapperFlagsMask) == 0, "Bad flag mask"); 277 return (__atomic_load_n(mFlags.AsPtr(), __ATOMIC_RELAXED) & aFlags) == 278 aFlags; 279 } 280 281 void SetFlags(FlagsType aFlagsToSet) { 282 MOZ_ASSERT(NS_IsMainThread()); 283 MOZ_ASSERT((aFlagsToSet & kWrapperFlagsMask) == 0, "Bad flag mask"); 284 mFlags.Set(mFlags.Get() | aFlagsToSet); 285 } 286 287 void UnsetFlags(FlagsType aFlagsToUnset) { 288 MOZ_ASSERT(NS_IsMainThread()); 289 MOZ_ASSERT((aFlagsToUnset & kWrapperFlagsMask) == 0, "Bad flag mask"); 290 mFlags.Set(mFlags.Get() & ~aFlagsToUnset); 291 } 292 293 void PreserveWrapper(nsISupports* aScriptObjectHolder) { 294 if (PreservingWrapper()) { 295 return; 296 } 297 298 nsISupports* ccISupports; 299 aScriptObjectHolder->QueryInterface(NS_GET_IID(nsCycleCollectionISupports), 300 reinterpret_cast<void**>(&ccISupports)); 301 MOZ_ASSERT(ccISupports); 302 303 nsXPCOMCycleCollectionParticipant* participant; 304 CallQueryInterface(ccISupports, &participant); 305 PreserveWrapper(ccISupports, participant); 306 } 307 308 void PreserveWrapper(void* aScriptObjectHolder, 309 nsScriptObjectTracer* aTracer) { 310 if (PreservingWrapper()) { 311 return; 312 } 313 314 JSObject* wrapper = GetWrapper(); // Read barrier for incremental GC. 315 HoldJSObjects(aScriptObjectHolder, aTracer, JS::GetObjectZone(wrapper)); 316 SetPreservingWrapper(true); 317 #ifdef DEBUG 318 // Make sure the cycle collector will be able to traverse to the wrapper. 319 CheckCCWrapperTraversal(aScriptObjectHolder, aTracer); 320 #endif 321 } 322 323 void ReleaseWrapper(void* aScriptObjectHolder); 324 325 // Special case version of ReleaseWrapper. For use by 326 // Rule::UnlinkDeclarationWrapper only. 327 void ReleaseWrapperWithoutDrop(); 328 329 void TraceWrapper(JSTracer* aTrc, const char* name) { 330 if (mWrapper) { 331 js::UnsafeTraceManuallyBarrieredEdge(aTrc, &mWrapper, name); 332 } 333 } 334 335 protected: 336 void PoisonWrapper() { 337 if (mWrapper) { 338 // Set the pointer to a value that will cause a crash if it is 339 // dereferenced. 340 mWrapper = reinterpret_cast<JSObject*>(1); 341 } 342 } 343 344 private: 345 void SetWrapperJSObject(JSObject* aWrapper); 346 347 void ReleaseWrapperAndMaybeDropHolder(void* aScriptObjectHolderToDrop); 348 349 // We'd like to assert that these aren't used from servo threads, but we don't 350 // have a great way to do that because: 351 // * We can't just assert that they get used on the main thread, because 352 // these are used from workers. 353 // * We can't just assert that they aren't used when IsInServoTraversal(), 354 // because the traversal has a sequential, main-thread-only phase, where we 355 // run animations that can fiddle with JS promises. 356 FlagsType GetWrapperFlags() const { return mFlags.Get() & kWrapperFlagsMask; } 357 358 bool HasWrapperFlag(FlagsType aFlag) const { 359 MOZ_ASSERT((aFlag & ~kWrapperFlagsMask) == 0, "Bad wrapper flag bits"); 360 return !!(mFlags.Get() & aFlag); 361 } 362 363 void SetWrapperFlags(FlagsType aFlagsToSet) { 364 MOZ_ASSERT((aFlagsToSet & ~kWrapperFlagsMask) == 0, 365 "Bad wrapper flag bits"); 366 mFlags.Set(mFlags.Get() | aFlagsToSet); 367 } 368 369 void UnsetWrapperFlags(FlagsType aFlagsToUnset) { 370 MOZ_ASSERT((aFlagsToUnset & ~kWrapperFlagsMask) == 0, 371 "Bad wrapper flag bits"); 372 mFlags.Set(mFlags.Get() & ~aFlagsToUnset); 373 } 374 375 void HoldJSObjects(void* aScriptObjectHolder, nsScriptObjectTracer* aTracer, 376 JS::Zone* aZone); 377 378 #ifdef DEBUG 379 public: 380 void CheckCCWrapperTraversal(void* aScriptObjectHolder, 381 nsScriptObjectTracer* aTracer); 382 #endif // DEBUG 383 384 private: 385 friend class mozilla::dom::binding_detail::CastableToWrapperCacheHelper; 386 387 /** 388 * If this bit is set then we're preserving the wrapper, which in effect ties 389 * the lifetime of the JS object stored in the cache to the lifetime of the 390 * native object. We rely on the cycle collector to break the cycle that this 391 * causes between the native object and the JS object, so it is important that 392 * any native object that supports preserving of its wrapper 393 * traces/traverses/unlinks the cached JS object (see 394 * NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER and 395 * NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER). 396 */ 397 enum { WRAPPER_BIT_PRESERVED = 1 << 0 }; 398 399 enum { kWrapperFlagsMask = WRAPPER_BIT_PRESERVED }; 400 401 JSObject* mWrapper = nullptr; 402 403 // Rust code needs to read and write some flags atomically, but we don't want 404 // to make the wrapper flags atomic whole-sale because main-thread code would 405 // become more expensive (loads wouldn't change, but flag setting / 406 // unsetting could become slower enough to be noticeable). Making this an 407 // Atomic whole-sale needs more measuring. 408 // 409 // In order to not mess with aliasing rules the type should not be frozen, so 410 // we use a RustCell, which contains an UnsafeCell internally. See also the 411 // handling of ServoData (though that's a bit different). 412 mozilla::RustCell<FlagsType> mFlags{0}; 413 414 protected: 415 #ifdef BOOL_FLAGS_ON_WRAPPER_CACHE 416 uint32_t mBoolFlags = 0; 417 #endif 418 }; 419 420 enum { WRAPPER_CACHE_FLAGS_BITS_USED = 1 }; 421 422 #define NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY \ 423 if (aIID.Equals(NS_GET_IID(nsWrapperCache))) { \ 424 *aInstancePtr = static_cast<nsWrapperCache*>(this); \ 425 return NS_OK; \ 426 } 427 428 #define NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY \ 429 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY \ 430 else 431 432 // Cycle collector macros for wrapper caches. 433 // 434 // The NS_DECL_*WRAPPERCACHE_* macros make it easier to mark classes as holding 435 // just a single pointer to a JS value. That information is then used for 436 // certain GC optimizations. 437 438 #define NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS_AMBIGUOUS(_class, _base) \ 439 class NS_CYCLE_COLLECTION_INNERCLASS \ 440 : public nsXPCOMCycleCollectionParticipant { \ 441 public: \ 442 constexpr explicit NS_CYCLE_COLLECTION_INNERCLASS(Flags aFlags = 0) \ 443 : nsXPCOMCycleCollectionParticipant(aFlags | \ 444 FlagMaybeSingleZoneJSHolder) {} \ 445 \ 446 private: \ 447 NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \ 448 NS_IMETHOD_(void) \ 449 Trace(void* p, const TraceCallbacks& cb, void* closure) override; \ 450 NS_IMETHOD_(void) \ 451 TraceWrapper(void* aPtr, const TraceCallbacks& aCb, void* aClosure) final; \ 452 NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \ 453 }; \ 454 NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class) \ 455 static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME; \ 456 NOT_INHERITED_CANT_OVERRIDE 457 458 #define NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(_class) \ 459 NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS_AMBIGUOUS(_class, _class) 460 461 #define NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS_INHERITED(_class, \ 462 _base_class) \ 463 class NS_CYCLE_COLLECTION_INNERCLASS \ 464 : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class) { \ 465 public: \ 466 constexpr explicit NS_CYCLE_COLLECTION_INNERCLASS(Flags aFlags) \ 467 : NS_CYCLE_COLLECTION_CLASSNAME(_base_class)( \ 468 aFlags | FlagMaybeSingleZoneJSHolder) {} \ 469 \ 470 private: \ 471 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY(_class, _base_class) \ 472 NS_IMETHOD_(void) \ 473 Trace(void* p, const TraceCallbacks& cb, void* closure) override; \ 474 NS_IMETHOD_(void) \ 475 TraceWrapper(void* aPtr, const TraceCallbacks& aCb, void* aClosure) final; \ 476 NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \ 477 }; \ 478 NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class) \ 479 static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME; 480 481 #define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_WRAPPERCACHE_CLASS_AMBIGUOUS( \ 482 _class, _base) \ 483 class NS_CYCLE_COLLECTION_INNERCLASS \ 484 : public nsXPCOMCycleCollectionParticipant { \ 485 public: \ 486 constexpr explicit NS_CYCLE_COLLECTION_INNERCLASS(Flags aFlags) \ 487 : nsXPCOMCycleCollectionParticipant(aFlags | FlagMightSkip | \ 488 FlagMaybeSingleZoneJSHolder) {} \ 489 \ 490 private: \ 491 NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \ 492 NS_IMETHOD_(void) \ 493 Trace(void* p, const TraceCallbacks& cb, void* closure) override; \ 494 NS_IMETHOD_(void) \ 495 TraceWrapper(void* aPtr, const TraceCallbacks& aCb, void* aClosure) final; \ 496 NS_IMETHOD_(bool) CanSkipReal(void* p, bool aRemovingAllowed) override; \ 497 NS_IMETHOD_(bool) CanSkipInCCReal(void* p) override; \ 498 NS_IMETHOD_(bool) CanSkipThisReal(void* p) override; \ 499 NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \ 500 }; \ 501 NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class) \ 502 static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME; \ 503 NOT_INHERITED_CANT_OVERRIDE 504 505 #define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_WRAPPERCACHE_CLASS(_class) \ 506 NS_DECL_CYCLE_COLLECTION_SKIPPABLE_WRAPPERCACHE_CLASS_AMBIGUOUS(_class, \ 507 _class) 508 509 #define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_WRAPPERCACHE_CLASS_INHERITED( \ 510 _class, _base_class) \ 511 class NS_CYCLE_COLLECTION_INNERCLASS \ 512 : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class) { \ 513 public: \ 514 constexpr explicit NS_CYCLE_COLLECTION_INNERCLASS(Flags aFlags = 0) \ 515 : NS_CYCLE_COLLECTION_CLASSNAME(_base_class)( \ 516 aFlags | FlagMightSkip | FlagMaybeSingleZoneJSHolder) {} \ 517 \ 518 private: \ 519 NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base_class) \ 520 NS_IMETHOD_(void) \ 521 Trace(void* p, const TraceCallbacks& cb, void* closure) override; \ 522 NS_IMETHOD_(void) \ 523 TraceWrapper(void* aPtr, const TraceCallbacks& aCb, void* aClosure) final; \ 524 NS_IMETHOD_(bool) CanSkipReal(void* p, bool aRemovingAllowed) override; \ 525 NS_IMETHOD_(bool) CanSkipInCCReal(void* p) override; \ 526 NS_IMETHOD_(bool) CanSkipThisReal(void* p) override; \ 527 NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \ 528 }; \ 529 NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class) \ 530 static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME; 531 532 #define NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(_class) \ 533 void DeleteCycleCollectable(void) { delete this; } \ 534 class NS_CYCLE_COLLECTION_INNERCLASS : public nsScriptObjectTracer { \ 535 public: \ 536 constexpr explicit NS_CYCLE_COLLECTION_INNERCLASS(Flags aFlags = 0) \ 537 : nsScriptObjectTracer(aFlags | FlagMaybeSingleZoneJSHolder) {} \ 538 \ 539 private: \ 540 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class) \ 541 NS_IMETHOD_(void) \ 542 Trace(void* p, const TraceCallbacks& cb, void* closure) override; \ 543 NS_IMETHOD_(void) \ 544 TraceWrapper(void* aPtr, const TraceCallbacks& aCb, void* aClosure) final; \ 545 static constexpr nsScriptObjectTracer* GetParticipant() { \ 546 return &_class::NS_CYCLE_COLLECTION_INNERNAME; \ 547 } \ 548 }; \ 549 static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME; 550 551 #define NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER \ 552 tmp->TraceWrapper(aCallbacks, aClosure); 553 554 #define NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \ 555 tmp->ReleaseWrapper(p); 556 557 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(_class) \ 558 static_assert(std::is_base_of<nsWrapperCache, _class>::value, \ 559 "Class should inherit nsWrapperCache"); \ 560 NS_IMPL_CYCLE_COLLECTION_SINGLE_ZONE_SCRIPT_HOLDER_CLASS(_class) \ 561 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(_class) \ 562 TraceWrapper(p, aCallbacks, aClosure); \ 563 NS_IMPL_CYCLE_COLLECTION_TRACE_END \ 564 void NS_CYCLE_COLLECTION_CLASSNAME(_class)::TraceWrapper( \ 565 void* p, const TraceCallbacks& aCallbacks, void* aClosure) { \ 566 _class* tmp = DowncastCCParticipant<_class>(p); \ 567 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER \ 568 } 569 570 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(_class) \ 571 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(_class) \ 572 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \ 573 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \ 574 NS_IMPL_CYCLE_COLLECTION_UNLINK_END \ 575 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class) \ 576 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 577 578 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(_class, ...) \ 579 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(_class) \ 580 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \ 581 NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__) \ 582 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \ 583 NS_IMPL_CYCLE_COLLECTION_UNLINK_END \ 584 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class) \ 585 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__) \ 586 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 587 588 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK(_class, ...) \ 589 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(_class) \ 590 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \ 591 NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__) \ 592 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \ 593 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE \ 594 NS_IMPL_CYCLE_COLLECTION_UNLINK_END \ 595 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class) \ 596 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__) \ 597 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 598 599 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(_class, ...) \ 600 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(_class) \ 601 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \ 602 NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__) \ 603 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \ 604 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR \ 605 NS_IMPL_CYCLE_COLLECTION_UNLINK_END \ 606 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class) \ 607 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__) \ 608 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 609 610 // This is used for wrapper cached classes that inherit from cycle 611 // collected non-wrapper cached classes. 612 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_INHERITED(_class, _base, ...) \ 613 static_assert(!std::is_base_of<nsWrapperCache, _base>::value, \ 614 "Base class should not inherit nsWrapperCache"); \ 615 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(_class) \ 616 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(_class, _base) \ 617 NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__) \ 618 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \ 619 NS_IMPL_CYCLE_COLLECTION_UNLINK_END \ 620 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(_class, _base) \ 621 /* Assert somewhere, in this case in the traverse method, that the */ \ 622 /* parent isn't a single zone holder*/ \ 623 MOZ_ASSERT(!_base::NS_CYCLE_COLLECTION_INNERNAME.IsSingleZoneJSHolder()); \ 624 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__) \ 625 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 626 627 // if NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS is used to implement 628 // a wrappercache class, one needs to use 629 // NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS and its variants in the class 630 // declaration. 631 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS( \ 632 class_, native_members_, js_members_) \ 633 static_assert(std::is_base_of<nsWrapperCache, class_>::value, \ 634 "Class should inherit nsWrapperCache"); \ 635 NS_IMPL_CYCLE_COLLECTION_CLASS(class_) \ 636 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(class_) \ 637 using ::ImplCycleCollectionUnlink; \ 638 NS_IMPL_CYCLE_COLLECTION_UNLINK( \ 639 MOZ_FOR_EACH_EXPAND_HELPER native_members_) \ 640 NS_IMPL_CYCLE_COLLECTION_UNLINK(MOZ_FOR_EACH_EXPAND_HELPER js_members_) \ 641 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \ 642 NS_IMPL_CYCLE_COLLECTION_UNLINK_END \ 643 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(class_) \ 644 NS_IMPL_CYCLE_COLLECTION_TRAVERSE( \ 645 MOZ_FOR_EACH_EXPAND_HELPER native_members_) \ 646 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END \ 647 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(class_) \ 648 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBERS( \ 649 MOZ_FOR_EACH_EXPAND_HELPER js_members_) \ 650 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER \ 651 NS_IMPL_CYCLE_COLLECTION_TRACE_END 652 653 #endif /* nsWrapperCache_h___ */