|
1 /* -*- Mode: Javascript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 |
|
3 "use strict"; |
|
4 |
|
5 // Ignore calls made through these function pointers |
|
6 var ignoreIndirectCalls = { |
|
7 "mallocSizeOf" : true, |
|
8 "aMallocSizeOf" : true, |
|
9 "_malloc_message" : true, |
|
10 "__conv" : true, |
|
11 "__convf" : true, |
|
12 "prerrortable.c:callback_newtable" : true, |
|
13 "mozalloc_oom.cpp:void (* gAbortHandler)(size_t)" : true, |
|
14 |
|
15 // I don't know why these are getting truncated |
|
16 "nsTraceRefcnt.cpp:void (* leakyLogAddRef)(void*": true, |
|
17 "nsTraceRefcnt.cpp:void (* leakyLogAddRef)(void*, int, int)": true, |
|
18 "nsTraceRefcnt.cpp:void (* leakyLogRelease)(void*": true, |
|
19 "nsTraceRefcnt.cpp:void (* leakyLogRelease)(void*, int, int)": true, |
|
20 }; |
|
21 |
|
22 function indirectCallCannotGC(fullCaller, fullVariable) |
|
23 { |
|
24 var caller = readable(fullCaller); |
|
25 |
|
26 // This is usually a simple variable name, but sometimes a full name gets |
|
27 // passed through. And sometimes that name is truncated. Examples: |
|
28 // _ZL13gAbortHandler|mozalloc_oom.cpp:void (* gAbortHandler)(size_t) |
|
29 // _ZL14pMutexUnlockFn|umutex.cpp:void (* pMutexUnlockFn)(const void* |
|
30 var name = readable(fullVariable); |
|
31 |
|
32 if (name in ignoreIndirectCalls) |
|
33 return true; |
|
34 |
|
35 if (name == "mapper" && caller == "ptio.c:pt_MapError") |
|
36 return true; |
|
37 |
|
38 if (name == "params" && caller == "PR_ExplodeTime") |
|
39 return true; |
|
40 |
|
41 if (name == "op" && /GetWeakmapKeyDelegate/.test(caller)) |
|
42 return true; |
|
43 |
|
44 var CheckCallArgs = "AsmJS.cpp:uint8 CheckCallArgs(FunctionCompiler*, js::frontend::ParseNode*, (uint8)(FunctionCompiler*,js::frontend::ParseNode*,Type)*, FunctionCompiler::Call*)"; |
|
45 if (name == "checkArg" && caller == CheckCallArgs) |
|
46 return true; |
|
47 |
|
48 // hook called during script finalization which cannot GC. |
|
49 if (/CallDestroyScriptHook/.test(caller)) |
|
50 return true; |
|
51 |
|
52 // template method called during marking and hence cannot GC |
|
53 if (name == "op" && caller.indexOf("bool js::WeakMap<Key, Value, HashPolicy>::keyNeedsMark(JSObject*)") != -1) |
|
54 { |
|
55 return true; |
|
56 } |
|
57 |
|
58 return false; |
|
59 } |
|
60 |
|
61 // Ignore calls through functions pointers with these types |
|
62 var ignoreClasses = { |
|
63 "JSTracer" : true, |
|
64 "JSStringFinalizer" : true, |
|
65 "SprintfState" : true, |
|
66 "SprintfStateStr" : true, |
|
67 "JSLocaleCallbacks" : true, |
|
68 "JSC::ExecutableAllocator" : true, |
|
69 "PRIOMethods": true, |
|
70 "XPCOMFunctions" : true, // I'm a little unsure of this one |
|
71 "_MD_IOVector" : true, |
|
72 }; |
|
73 |
|
74 // Ignore calls through TYPE.FIELD, where TYPE is the class or struct name containing |
|
75 // a function pointer field named FIELD. |
|
76 var ignoreCallees = { |
|
77 "js::Class.trace" : true, |
|
78 "js::Class.finalize" : true, |
|
79 "JSRuntime.destroyPrincipals" : true, |
|
80 "icu_50::UObject.__deleting_dtor" : true, // destructors in ICU code can't cause GC |
|
81 "mozilla::CycleCollectedJSRuntime.DescribeCustomObjects" : true, // During tracing, cannot GC. |
|
82 "mozilla::CycleCollectedJSRuntime.NoteCustomGCThingXPCOMChildren" : true, // During tracing, cannot GC. |
|
83 "PLDHashTableOps.hashKey" : true, |
|
84 "z_stream_s.zfree" : true, |
|
85 }; |
|
86 |
|
87 function fieldCallCannotGC(csu, fullfield) |
|
88 { |
|
89 if (csu in ignoreClasses) |
|
90 return true; |
|
91 if (fullfield in ignoreCallees) |
|
92 return true; |
|
93 return false; |
|
94 } |
|
95 |
|
96 function ignoreEdgeUse(edge, variable) |
|
97 { |
|
98 // Functions which should not be treated as using variable. |
|
99 if (edge.Kind == "Call") { |
|
100 var callee = edge.Exp[0]; |
|
101 if (callee.Kind == "Var") { |
|
102 var name = callee.Variable.Name[0]; |
|
103 if (/~Anchor/.test(name)) |
|
104 return true; |
|
105 if (/~DebugOnly/.test(name)) |
|
106 return true; |
|
107 if (/~ScopedThreadSafeStringInspector/.test(name)) |
|
108 return true; |
|
109 } |
|
110 } |
|
111 |
|
112 return false; |
|
113 } |
|
114 |
|
115 function ignoreEdgeAddressTaken(edge) |
|
116 { |
|
117 // Functions which may take indirect pointers to unrooted GC things, |
|
118 // but will copy them into rooted locations before calling anything |
|
119 // that can GC. These parameters should usually be replaced with |
|
120 // handles or mutable handles. |
|
121 if (edge.Kind == "Call") { |
|
122 var callee = edge.Exp[0]; |
|
123 if (callee.Kind == "Var") { |
|
124 var name = callee.Variable.Name[0]; |
|
125 if (/js::Invoke\(/.test(name)) |
|
126 return true; |
|
127 } |
|
128 } |
|
129 |
|
130 return false; |
|
131 } |
|
132 |
|
133 // Ignore calls of these functions (so ignore any stack containing these) |
|
134 var ignoreFunctions = { |
|
135 "ptio.c:pt_MapError" : true, |
|
136 "PR_ExplodeTime" : true, |
|
137 "PR_ErrorInstallTable" : true, |
|
138 "PR_SetThreadPrivate" : true, |
|
139 "JSObject* js::GetWeakmapKeyDelegate(JSObject*)" : true, // FIXME: mark with AutoAssertNoGC instead |
|
140 "uint8 NS_IsMainThread()" : true, |
|
141 |
|
142 // FIXME! |
|
143 "NS_LogInit": true, |
|
144 "NS_LogTerm": true, |
|
145 "NS_LogAddRef": true, |
|
146 "NS_LogRelease": true, |
|
147 "NS_LogCtor": true, |
|
148 "NS_LogDtor": true, |
|
149 "NS_LogCOMPtrAddRef": true, |
|
150 "NS_LogCOMPtrRelease": true, |
|
151 |
|
152 // FIXME! |
|
153 "NS_DebugBreak": true, |
|
154 |
|
155 // These are a little overzealous -- these destructors *can* GC if they end |
|
156 // up wrapping a pending exception. See bug 898815 for the heavyweight fix. |
|
157 "void js::AutoCompartment::~AutoCompartment(int32)" : true, |
|
158 "void JSAutoCompartment::~JSAutoCompartment(int32)" : true, |
|
159 |
|
160 // Bug 948646 - the only thing AutoJSContext's constructor calls |
|
161 // is an Init() routine whose entire body is covered with an |
|
162 // AutoAssertNoGC. AutoSafeJSContext is the same thing, just with |
|
163 // a different value for the 'aSafe' parameter. |
|
164 "void mozilla::AutoJSContext::AutoJSContext(mozilla::detail::GuardObjectNotifier*)" : true, |
|
165 "void mozilla::AutoSafeJSContext::~AutoSafeJSContext(int32)" : true, |
|
166 |
|
167 // And these are workarounds to avoid even more analysis work, |
|
168 // which would sadly still be needed even with bug 898815. |
|
169 "void js::AutoCompartment::AutoCompartment(js::ExclusiveContext*, JSCompartment*)": true, |
|
170 }; |
|
171 |
|
172 function ignoreGCFunction(mangled) |
|
173 { |
|
174 assert(mangled in readableNames); |
|
175 var fun = readableNames[mangled][0]; |
|
176 |
|
177 if (fun in ignoreFunctions) |
|
178 return true; |
|
179 |
|
180 // Templatized function |
|
181 if (fun.indexOf("void nsCOMPtr<T>::Assert_NoQueryNeeded()") >= 0) |
|
182 return true; |
|
183 |
|
184 // XXX modify refillFreeList<NoGC> to not need data flow analysis to understand it cannot GC. |
|
185 if (/refillFreeList/.test(fun) && /\(js::AllowGC\)0u/.test(fun)) |
|
186 return true; |
|
187 return false; |
|
188 } |
|
189 |
|
190 function isRootedTypeName(name) |
|
191 { |
|
192 if (name == "mozilla::ErrorResult" || |
|
193 name == "JSErrorResult" || |
|
194 name == "WrappableJSErrorResult" || |
|
195 name == "js::frontend::TokenStream" || |
|
196 name == "js::frontend::TokenStream::Position" || |
|
197 name == "ModuleCompiler") |
|
198 { |
|
199 return true; |
|
200 } |
|
201 return false; |
|
202 } |
|
203 |
|
204 function isRootedPointerTypeName(name) |
|
205 { |
|
206 if (name.startsWith('struct ')) |
|
207 name = name.substr(7); |
|
208 if (name.startsWith('class ')) |
|
209 name = name.substr(6); |
|
210 if (name.startsWith('const ')) |
|
211 name = name.substr(6); |
|
212 if (name.startsWith('js::ctypes::')) |
|
213 name = name.substr(12); |
|
214 if (name.startsWith('js::')) |
|
215 name = name.substr(4); |
|
216 if (name.startsWith('JS::')) |
|
217 name = name.substr(4); |
|
218 if (name.startsWith('mozilla::dom::')) |
|
219 name = name.substr(14); |
|
220 |
|
221 if (name.startsWith('MaybeRooted<')) |
|
222 return /\(js::AllowGC\)1u>::RootType/.test(name); |
|
223 |
|
224 return name.startsWith('Rooted') || name.startsWith('PersistentRooted'); |
|
225 } |
|
226 |
|
227 function isSuppressConstructor(name) |
|
228 { |
|
229 return name.indexOf("::AutoSuppressGC") != -1 |
|
230 || name.indexOf("::AutoEnterAnalysis") != -1 |
|
231 || name.indexOf("::AutoAssertNoGC") != -1 |
|
232 || name.indexOf("::AutoIgnoreRootingHazards") != -1; |
|
233 } |
|
234 |
|
235 // nsISupports subclasses' methods may be scriptable (or overridden |
|
236 // via binary XPCOM), and so may GC. But some fields just aren't going |
|
237 // to get overridden with something that can GC. |
|
238 function isOverridableField(initialCSU, csu, field) |
|
239 { |
|
240 if (csu != 'nsISupports') |
|
241 return false; |
|
242 if (field == 'GetCurrentJSContext') |
|
243 return false; |
|
244 if (field == 'IsOnCurrentThread') |
|
245 return false; |
|
246 if (field == 'GetNativeContext') |
|
247 return false; |
|
248 if (field == "GetGlobalJSObject") |
|
249 return false; |
|
250 if (field == "GetIsMainThread") |
|
251 return false; |
|
252 if (initialCSU == 'nsIXPConnectJSObjectHolder' && field == 'GetJSObject') |
|
253 return false; |
|
254 |
|
255 return true; |
|
256 } |