michael@0: /* -*- Mode: Javascript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: michael@0: "use strict"; michael@0: michael@0: // Ignore calls made through these function pointers michael@0: var ignoreIndirectCalls = { michael@0: "mallocSizeOf" : true, michael@0: "aMallocSizeOf" : true, michael@0: "_malloc_message" : true, michael@0: "__conv" : true, michael@0: "__convf" : true, michael@0: "prerrortable.c:callback_newtable" : true, michael@0: "mozalloc_oom.cpp:void (* gAbortHandler)(size_t)" : true, michael@0: michael@0: // I don't know why these are getting truncated michael@0: "nsTraceRefcnt.cpp:void (* leakyLogAddRef)(void*": true, michael@0: "nsTraceRefcnt.cpp:void (* leakyLogAddRef)(void*, int, int)": true, michael@0: "nsTraceRefcnt.cpp:void (* leakyLogRelease)(void*": true, michael@0: "nsTraceRefcnt.cpp:void (* leakyLogRelease)(void*, int, int)": true, michael@0: }; michael@0: michael@0: function indirectCallCannotGC(fullCaller, fullVariable) michael@0: { michael@0: var caller = readable(fullCaller); michael@0: michael@0: // This is usually a simple variable name, but sometimes a full name gets michael@0: // passed through. And sometimes that name is truncated. Examples: michael@0: // _ZL13gAbortHandler|mozalloc_oom.cpp:void (* gAbortHandler)(size_t) michael@0: // _ZL14pMutexUnlockFn|umutex.cpp:void (* pMutexUnlockFn)(const void* michael@0: var name = readable(fullVariable); michael@0: michael@0: if (name in ignoreIndirectCalls) michael@0: return true; michael@0: michael@0: if (name == "mapper" && caller == "ptio.c:pt_MapError") michael@0: return true; michael@0: michael@0: if (name == "params" && caller == "PR_ExplodeTime") michael@0: return true; michael@0: michael@0: if (name == "op" && /GetWeakmapKeyDelegate/.test(caller)) michael@0: return true; michael@0: michael@0: var CheckCallArgs = "AsmJS.cpp:uint8 CheckCallArgs(FunctionCompiler*, js::frontend::ParseNode*, (uint8)(FunctionCompiler*,js::frontend::ParseNode*,Type)*, FunctionCompiler::Call*)"; michael@0: if (name == "checkArg" && caller == CheckCallArgs) michael@0: return true; michael@0: michael@0: // hook called during script finalization which cannot GC. michael@0: if (/CallDestroyScriptHook/.test(caller)) michael@0: return true; michael@0: michael@0: // template method called during marking and hence cannot GC michael@0: if (name == "op" && caller.indexOf("bool js::WeakMap::keyNeedsMark(JSObject*)") != -1) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: // Ignore calls through functions pointers with these types michael@0: var ignoreClasses = { michael@0: "JSTracer" : true, michael@0: "JSStringFinalizer" : true, michael@0: "SprintfState" : true, michael@0: "SprintfStateStr" : true, michael@0: "JSLocaleCallbacks" : true, michael@0: "JSC::ExecutableAllocator" : true, michael@0: "PRIOMethods": true, michael@0: "XPCOMFunctions" : true, // I'm a little unsure of this one michael@0: "_MD_IOVector" : true, michael@0: }; michael@0: michael@0: // Ignore calls through TYPE.FIELD, where TYPE is the class or struct name containing michael@0: // a function pointer field named FIELD. michael@0: var ignoreCallees = { michael@0: "js::Class.trace" : true, michael@0: "js::Class.finalize" : true, michael@0: "JSRuntime.destroyPrincipals" : true, michael@0: "icu_50::UObject.__deleting_dtor" : true, // destructors in ICU code can't cause GC michael@0: "mozilla::CycleCollectedJSRuntime.DescribeCustomObjects" : true, // During tracing, cannot GC. michael@0: "mozilla::CycleCollectedJSRuntime.NoteCustomGCThingXPCOMChildren" : true, // During tracing, cannot GC. michael@0: "PLDHashTableOps.hashKey" : true, michael@0: "z_stream_s.zfree" : true, michael@0: }; michael@0: michael@0: function fieldCallCannotGC(csu, fullfield) michael@0: { michael@0: if (csu in ignoreClasses) michael@0: return true; michael@0: if (fullfield in ignoreCallees) michael@0: return true; michael@0: return false; michael@0: } michael@0: michael@0: function ignoreEdgeUse(edge, variable) michael@0: { michael@0: // Functions which should not be treated as using variable. michael@0: if (edge.Kind == "Call") { michael@0: var callee = edge.Exp[0]; michael@0: if (callee.Kind == "Var") { michael@0: var name = callee.Variable.Name[0]; michael@0: if (/~Anchor/.test(name)) michael@0: return true; michael@0: if (/~DebugOnly/.test(name)) michael@0: return true; michael@0: if (/~ScopedThreadSafeStringInspector/.test(name)) michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: function ignoreEdgeAddressTaken(edge) michael@0: { michael@0: // Functions which may take indirect pointers to unrooted GC things, michael@0: // but will copy them into rooted locations before calling anything michael@0: // that can GC. These parameters should usually be replaced with michael@0: // handles or mutable handles. michael@0: if (edge.Kind == "Call") { michael@0: var callee = edge.Exp[0]; michael@0: if (callee.Kind == "Var") { michael@0: var name = callee.Variable.Name[0]; michael@0: if (/js::Invoke\(/.test(name)) michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: // Ignore calls of these functions (so ignore any stack containing these) michael@0: var ignoreFunctions = { michael@0: "ptio.c:pt_MapError" : true, michael@0: "PR_ExplodeTime" : true, michael@0: "PR_ErrorInstallTable" : true, michael@0: "PR_SetThreadPrivate" : true, michael@0: "JSObject* js::GetWeakmapKeyDelegate(JSObject*)" : true, // FIXME: mark with AutoAssertNoGC instead michael@0: "uint8 NS_IsMainThread()" : true, michael@0: michael@0: // FIXME! michael@0: "NS_LogInit": true, michael@0: "NS_LogTerm": true, michael@0: "NS_LogAddRef": true, michael@0: "NS_LogRelease": true, michael@0: "NS_LogCtor": true, michael@0: "NS_LogDtor": true, michael@0: "NS_LogCOMPtrAddRef": true, michael@0: "NS_LogCOMPtrRelease": true, michael@0: michael@0: // FIXME! michael@0: "NS_DebugBreak": true, michael@0: michael@0: // These are a little overzealous -- these destructors *can* GC if they end michael@0: // up wrapping a pending exception. See bug 898815 for the heavyweight fix. michael@0: "void js::AutoCompartment::~AutoCompartment(int32)" : true, michael@0: "void JSAutoCompartment::~JSAutoCompartment(int32)" : true, michael@0: michael@0: // Bug 948646 - the only thing AutoJSContext's constructor calls michael@0: // is an Init() routine whose entire body is covered with an michael@0: // AutoAssertNoGC. AutoSafeJSContext is the same thing, just with michael@0: // a different value for the 'aSafe' parameter. michael@0: "void mozilla::AutoJSContext::AutoJSContext(mozilla::detail::GuardObjectNotifier*)" : true, michael@0: "void mozilla::AutoSafeJSContext::~AutoSafeJSContext(int32)" : true, michael@0: michael@0: // And these are workarounds to avoid even more analysis work, michael@0: // which would sadly still be needed even with bug 898815. michael@0: "void js::AutoCompartment::AutoCompartment(js::ExclusiveContext*, JSCompartment*)": true, michael@0: }; michael@0: michael@0: function ignoreGCFunction(mangled) michael@0: { michael@0: assert(mangled in readableNames); michael@0: var fun = readableNames[mangled][0]; michael@0: michael@0: if (fun in ignoreFunctions) michael@0: return true; michael@0: michael@0: // Templatized function michael@0: if (fun.indexOf("void nsCOMPtr::Assert_NoQueryNeeded()") >= 0) michael@0: return true; michael@0: michael@0: // XXX modify refillFreeList to not need data flow analysis to understand it cannot GC. michael@0: if (/refillFreeList/.test(fun) && /\(js::AllowGC\)0u/.test(fun)) michael@0: return true; michael@0: return false; michael@0: } michael@0: michael@0: function isRootedTypeName(name) michael@0: { michael@0: if (name == "mozilla::ErrorResult" || michael@0: name == "JSErrorResult" || michael@0: name == "WrappableJSErrorResult" || michael@0: name == "js::frontend::TokenStream" || michael@0: name == "js::frontend::TokenStream::Position" || michael@0: name == "ModuleCompiler") michael@0: { michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: function isRootedPointerTypeName(name) michael@0: { michael@0: if (name.startsWith('struct ')) michael@0: name = name.substr(7); michael@0: if (name.startsWith('class ')) michael@0: name = name.substr(6); michael@0: if (name.startsWith('const ')) michael@0: name = name.substr(6); michael@0: if (name.startsWith('js::ctypes::')) michael@0: name = name.substr(12); michael@0: if (name.startsWith('js::')) michael@0: name = name.substr(4); michael@0: if (name.startsWith('JS::')) michael@0: name = name.substr(4); michael@0: if (name.startsWith('mozilla::dom::')) michael@0: name = name.substr(14); michael@0: michael@0: if (name.startsWith('MaybeRooted<')) michael@0: return /\(js::AllowGC\)1u>::RootType/.test(name); michael@0: michael@0: return name.startsWith('Rooted') || name.startsWith('PersistentRooted'); michael@0: } michael@0: michael@0: function isSuppressConstructor(name) michael@0: { michael@0: return name.indexOf("::AutoSuppressGC") != -1 michael@0: || name.indexOf("::AutoEnterAnalysis") != -1 michael@0: || name.indexOf("::AutoAssertNoGC") != -1 michael@0: || name.indexOf("::AutoIgnoreRootingHazards") != -1; michael@0: } michael@0: michael@0: // nsISupports subclasses' methods may be scriptable (or overridden michael@0: // via binary XPCOM), and so may GC. But some fields just aren't going michael@0: // to get overridden with something that can GC. michael@0: function isOverridableField(initialCSU, csu, field) michael@0: { michael@0: if (csu != 'nsISupports') michael@0: return false; michael@0: if (field == 'GetCurrentJSContext') michael@0: return false; michael@0: if (field == 'IsOnCurrentThread') michael@0: return false; michael@0: if (field == 'GetNativeContext') michael@0: return false; michael@0: if (field == "GetGlobalJSObject") michael@0: return false; michael@0: if (field == "GetIsMainThread") michael@0: return false; michael@0: if (initialCSU == 'nsIXPConnectJSObjectHolder' && field == 'GetJSObject') michael@0: return false; michael@0: michael@0: return true; michael@0: }