/src/mozilla-central/dom/base/nsWrapperCache.cpp
Line | Count | Source (jump to first uncovered line) |
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 "nsWrapperCacheInlines.h" |
8 | | |
9 | | #include "js/Class.h" |
10 | | #include "js/Proxy.h" |
11 | | #include "mozilla/CycleCollectedJSRuntime.h" |
12 | | #include "mozilla/HoldDropJSObjects.h" |
13 | | #include "nsCycleCollectionTraversalCallback.h" |
14 | | #include "nsCycleCollector.h" |
15 | | |
16 | | using namespace mozilla; |
17 | | using namespace mozilla::dom; |
18 | | |
19 | | #ifdef DEBUG |
20 | | /* static */ bool |
21 | | nsWrapperCache::HasJSObjectMovedOp(JSObject* aWrapper) |
22 | | { |
23 | | return js::HasObjectMovedOp(aWrapper); |
24 | | } |
25 | | #endif |
26 | | |
27 | | void |
28 | | nsWrapperCache::HoldJSObjects(void* aScriptObjectHolder, |
29 | | nsScriptObjectTracer* aTracer) |
30 | 3 | { |
31 | 3 | cyclecollector::HoldJSObjectsImpl(aScriptObjectHolder, aTracer); |
32 | 3 | if (mWrapper && !JS::ObjectIsTenured(mWrapper)) { |
33 | 0 | CycleCollectedJSRuntime::Get()->NurseryWrapperPreserved(mWrapper); |
34 | 0 | } |
35 | 3 | } |
36 | | |
37 | | void |
38 | | nsWrapperCache::SetWrapperJSObject(JSObject* aWrapper) |
39 | 3 | { |
40 | 3 | mWrapper = aWrapper; |
41 | 3 | UnsetWrapperFlags(kWrapperFlagsMask); |
42 | 3 | |
43 | 3 | if (aWrapper && !JS::ObjectIsTenured(aWrapper)) { |
44 | 0 | CycleCollectedJSRuntime::Get()->NurseryWrapperAdded(this); |
45 | 0 | } |
46 | 3 | |
47 | 3 | if (mozilla::recordreplay::IsReplaying()) { |
48 | 0 | mozilla::recordreplay::SetWeakPointerJSRoot(this, aWrapper); |
49 | 0 | } |
50 | 3 | } |
51 | | |
52 | | void |
53 | | nsWrapperCache::ReleaseWrapper(void* aScriptObjectHolder) |
54 | 0 | { |
55 | 0 | if (PreservingWrapper()) { |
56 | 0 | SetPreservingWrapper(false); |
57 | 0 | cyclecollector::DropJSObjectsImpl(aScriptObjectHolder); |
58 | 0 | } |
59 | 0 | } |
60 | | |
61 | | #ifdef DEBUG |
62 | | |
63 | | class DebugWrapperTraversalCallback : public nsCycleCollectionTraversalCallback |
64 | | { |
65 | | public: |
66 | | explicit DebugWrapperTraversalCallback(JSObject* aWrapper) |
67 | | : mFound(false) |
68 | | , mWrapper(JS::GCCellPtr(aWrapper)) |
69 | | { |
70 | | mFlags = WANT_ALL_TRACES; |
71 | | } |
72 | | |
73 | | NS_IMETHOD_(void) DescribeRefCountedNode(nsrefcnt aRefCount, |
74 | | const char* aObjName) override |
75 | | { |
76 | | } |
77 | | NS_IMETHOD_(void) DescribeGCedNode(bool aIsMarked, |
78 | | const char* aObjName, |
79 | | uint64_t aCompartmentAddress) override |
80 | | { |
81 | | } |
82 | | |
83 | | NS_IMETHOD_(void) NoteJSChild(const JS::GCCellPtr& aChild) override |
84 | | { |
85 | | if (aChild == mWrapper) { |
86 | | mFound = true; |
87 | | } |
88 | | } |
89 | | NS_IMETHOD_(void) NoteXPCOMChild(nsISupports* aChild) override |
90 | | { |
91 | | } |
92 | | NS_IMETHOD_(void) NoteNativeChild(void* aChild, |
93 | | nsCycleCollectionParticipant* aHelper) override |
94 | | { |
95 | | } |
96 | | |
97 | | NS_IMETHOD_(void) NoteNextEdgeName(const char* aName) override |
98 | | { |
99 | | } |
100 | | |
101 | | bool mFound; |
102 | | |
103 | | private: |
104 | | JS::GCCellPtr mWrapper; |
105 | | }; |
106 | | |
107 | | static void |
108 | | DebugWrapperTraceCallback(JS::GCCellPtr aPtr, const char* aName, void* aClosure) |
109 | | { |
110 | | DebugWrapperTraversalCallback* callback = |
111 | | static_cast<DebugWrapperTraversalCallback*>(aClosure); |
112 | | if (aPtr.is<JSObject>()) { |
113 | | callback->NoteJSChild(aPtr); |
114 | | } |
115 | | } |
116 | | |
117 | | void |
118 | | nsWrapperCache::CheckCCWrapperTraversal(void* aScriptObjectHolder, |
119 | | nsScriptObjectTracer* aTracer) |
120 | | { |
121 | | // Skip checking if we are recording or replaying, as calling |
122 | | // GetWrapperPreserveColor() can cause the cache's wrapper to be cleared. |
123 | | if (recordreplay::IsRecordingOrReplaying()) { |
124 | | return; |
125 | | } |
126 | | |
127 | | JSObject* wrapper = GetWrapperPreserveColor(); |
128 | | if (!wrapper) { |
129 | | return; |
130 | | } |
131 | | |
132 | | DebugWrapperTraversalCallback callback(wrapper); |
133 | | |
134 | | // The CC traversal machinery cannot trigger GC; however, the analysis cannot |
135 | | // see through the COM layer, so we use a suppression to help it. |
136 | | JS::AutoSuppressGCAnalysis suppress; |
137 | | |
138 | | aTracer->TraverseNativeAndJS(aScriptObjectHolder, callback); |
139 | | MOZ_ASSERT(callback.mFound, |
140 | | "Cycle collection participant didn't traverse to preserved " |
141 | | "wrapper! This will probably crash."); |
142 | | |
143 | | callback.mFound = false; |
144 | | aTracer->Trace(aScriptObjectHolder, |
145 | | TraceCallbackFunc(DebugWrapperTraceCallback), &callback); |
146 | | MOZ_ASSERT(callback.mFound, |
147 | | "Cycle collection participant didn't trace preserved wrapper! " |
148 | | "This will probably crash."); |
149 | | } |
150 | | |
151 | | #endif // DEBUG |