nsWrapperCache.cpp (5275B)
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 #include "js/Class.h" 8 #include "js/Proxy.h" 9 #include "jsfriendapi.h" 10 #include "mozilla/CycleCollectedJSRuntime.h" 11 #include "mozilla/HoldDropJSObjects.h" 12 #include "nsCycleCollectionTraversalCallback.h" 13 #include "nsCycleCollector.h" 14 #include "nsWrapperCacheInlines.h" 15 16 using namespace mozilla; 17 using namespace mozilla::dom; 18 19 #ifdef DEBUG 20 /* static */ 21 bool nsWrapperCache::HasJSObjectMovedOp(JSObject* aWrapper) { 22 return js::HasObjectMovedOp(aWrapper); 23 } 24 #endif 25 26 void nsWrapperCache::HoldJSObjects(void* aScriptObjectHolder, 27 nsScriptObjectTracer* aTracer, 28 JS::Zone* aWrapperZone) { 29 cyclecollector::HoldJSObjectsImpl(aScriptObjectHolder, aTracer, aWrapperZone); 30 if (mWrapper && !JS::ObjectIsTenured(mWrapper)) { 31 JS::HeapObjectPostWriteBarrier(&mWrapper, nullptr, mWrapper); 32 } 33 } 34 35 static inline bool IsNurseryWrapper(JSObject* aWrapper) { 36 return aWrapper && !JS::ObjectIsTenured(aWrapper); 37 } 38 39 void nsWrapperCache::SetWrapperJSObject(JSObject* aNewWrapper) { 40 JSObject* oldWrapper = mWrapper; 41 mWrapper = aNewWrapper; 42 UnsetWrapperFlags(kWrapperFlagsMask); 43 44 if (IsNurseryWrapper(aNewWrapper) && !IsNurseryWrapper(oldWrapper)) { 45 CycleCollectedJSRuntime::Get()->NurseryWrapperAdded(this); 46 } 47 } 48 49 void nsWrapperCache::ReleaseWrapper(void* aScriptObjectHolder) { 50 MOZ_ASSERT(aScriptObjectHolder); 51 ReleaseWrapperAndMaybeDropHolder(aScriptObjectHolder); 52 } 53 54 void nsWrapperCache::ReleaseWrapperWithoutDrop() { 55 // Special case version of ReleaseWrapper for Rule::UnlinkDeclarationWrapper. 56 // This allows it to release two separate wrappers with the same CC 57 // participant correctly. 58 ReleaseWrapperAndMaybeDropHolder(nullptr); 59 } 60 61 void nsWrapperCache::ReleaseWrapperAndMaybeDropHolder( 62 void* aScriptObjectHolderToDrop) { 63 if (PreservingWrapper()) { 64 SetPreservingWrapper(false); 65 if (aScriptObjectHolderToDrop) { 66 cyclecollector::DropJSObjectsImpl(aScriptObjectHolderToDrop); 67 } 68 JS::HeapObjectPostWriteBarrier(&mWrapper, mWrapper, nullptr); 69 } 70 } 71 72 #ifdef DEBUG 73 74 void nsWrapperCache::AssertUpdatedWrapperZone(const JSObject* aNewObject, 75 const JSObject* aOldObject) { 76 MOZ_ASSERT(js::GetObjectZoneFromAnyThread(aNewObject) == 77 js::GetObjectZoneFromAnyThread(aOldObject)); 78 } 79 80 class DebugWrapperTraversalCallback 81 : public nsCycleCollectionTraversalCallback { 82 public: 83 explicit DebugWrapperTraversalCallback(JSObject* aWrapper) 84 : mFound(false), mWrapper(JS::GCCellPtr(aWrapper)) { 85 mFlags = WANT_ALL_TRACES; 86 } 87 88 NS_IMETHOD_(void) 89 DescribeRefCountedNode(nsrefcnt aRefCount, const char* aObjName) override {} 90 NS_IMETHOD_(void) 91 DescribeGCedNode(bool aIsMarked, const char* aObjName, 92 uint64_t aCompartmentAddress) override {} 93 94 NS_IMETHOD_(void) NoteJSChild(JS::GCCellPtr aChild) override { 95 if (aChild == mWrapper) { 96 mFound = true; 97 } 98 } 99 NS_IMETHOD_(void) NoteXPCOMChild(nsISupports* aChild) override {} 100 NS_IMETHOD_(void) 101 NoteNativeChild(void* aChild, 102 nsCycleCollectionParticipant* aHelper) override {} 103 104 NS_IMETHOD_(void) 105 NoteWeakMapping(JSObject* aKey, nsISupports* aVal, 106 nsCycleCollectionParticipant* aValParticipant) override {} 107 108 NS_IMETHOD_(void) NoteNextEdgeName(const char* aName) override {} 109 110 bool mFound; 111 112 private: 113 JS::GCCellPtr mWrapper; 114 }; 115 116 static void DebugWrapperTraceCallback(JS::GCCellPtr aPtr, const char* aName, 117 void* aClosure) { 118 DebugWrapperTraversalCallback* callback = 119 static_cast<DebugWrapperTraversalCallback*>(aClosure); 120 if (aPtr.is<JSObject>()) { 121 callback->NoteJSChild(aPtr); 122 } 123 } 124 125 void nsWrapperCache::CheckCCWrapperTraversal(void* aScriptObjectHolder, 126 nsScriptObjectTracer* aTracer) { 127 JSObject* wrapper = GetWrapperPreserveColor(); 128 if (!wrapper) { 129 return; 130 } 131 132 // Temporarily make this a preserving wrapper so that TraceWrapper() traces 133 // it. 134 bool wasPreservingWrapper = PreservingWrapper(); 135 SetPreservingWrapper(true); 136 137 DebugWrapperTraversalCallback callback(wrapper); 138 139 // The CC traversal machinery cannot trigger GC; however, the analysis cannot 140 // see through the COM layer, so we use a suppression to help it. 141 JS::AutoSuppressGCAnalysis suppress; 142 143 aTracer->TraverseNativeAndJS(aScriptObjectHolder, callback); 144 MOZ_ASSERT(callback.mFound, 145 "Cycle collection participant didn't traverse to preserved " 146 "wrapper! This will probably crash."); 147 148 callback.mFound = false; 149 aTracer->Trace(aScriptObjectHolder, 150 TraceCallbackFunc(DebugWrapperTraceCallback), &callback); 151 MOZ_ASSERT(callback.mFound, 152 "Cycle collection participant didn't trace preserved wrapper! " 153 "This will probably crash."); 154 155 SetPreservingWrapper(wasPreservingWrapper); 156 } 157 158 #endif // DEBUG