analyzer.run(function () { let cx; let c=0; // Look for any JS object in order to build a new JSContext for(var i in analyzer.graph) { let o = analyzer.graph[i]; if (o.name.indexOf("JS Object (Ob") != 0) continue; obj = api.getPointerForAddress(o.address); if (!cx) { let rt = api.JS_GetObjectRuntime(obj); cx = api.JS_NewContext(rt, 8192); } let attributes = api.enumerate(cx, obj); console.log("attribute: " + JSON.stringify(attributes)); let id = api.createJsid(); api.JS_GetObjectId(cx, obj, id.address()); if (c++ > 10) break; } if (!cx) { assert.fail("Unable to fetch any JSContext from CC"); done(); return; } done(); });
let paths = path.map(function (o) { let classname = jsapi.getClassName(o); return { self: o, src: classname == "Function" ? jsapi.stringifyFunction(cx, o) : "", name: classname, enum: jsapi.enumerate(cx, o) }; });
function dumpObject(cx, description, leak) { let obj = jsapi.getPointerForAddress(leak.address); log(""); log(">>> " + description + " " + obj + " - "+leak.name); // In case of proxy, dump the target object if (leak.name == "JS Object (Proxy)") { let target; leak.edges.some(function (e) { if (e.name == "private") { target = e.to; return true; } return false; }); if (target) { dumpObjectEdges("proxy", leak); dumpObject(cx, "proxy target object", target); return; } else { console.error("!!! Unable to find private edged of Proxy"); } } // There is enough information in fragment class name... if (leak.name.indexOf("FragmentOrElement") == 0) return; // When using bind for ex, the binded function because a native method // so that we won't be able to decompile the function source. // The original function is eventually stored in 'parent' edge. // Otherwise, parent is just the global object. if (leak.name.indexOf("JS Object (Function") == 0) { let parent = getEdgeWithName(leak, "parent"); if (parent.name.indexOf("JS Object (Function") == 0) { log("Binded function for:"); dumpObject(cx, "Function parent", parent); return; } } if (leak.name == "JS Object (ChromeWindow)" || leak.name == "JS Object (Window)" || leak.name == "Backstagepass" || leak.name == "Sandbox") { let d = inspect.getGlobalDescription(cx, obj); log("Global description:") log(JSON.stringify(d, null, 2)); return; } if (leak.name == "nsXPCWrappedJS (nsIDOMEventListener)") { dumpObjectEdges("nsXPCWrappedJS", leak); leak.owners .filter(function (e) {return e.name == "mListeners[i]";}) .forEach(function (e) { dumpObject(cx, "event manager", e.from); }); leak.edges .filter(function (e) {return e.name == "root";}) .forEach(function (e) { dumpObject(cx, "root", e.to); }); return; } if (leak.name == "nsEventListenerManager") { dumpObjectEdges("nsEventListenerManager", leak); leak.owners .filter(function (e) {return e.name == "target";}) .forEach(function (e) { dumpObject(cx, "event.target", e.from); }); return; } if (leak.name.indexOf("JS Object (") == -1 || leak.name == "JS Object (Call)") { dumpObjectEdges(leak.name, leak); return; } if (leak.name.indexOf("JS Object (Function") == 0) { let global = getPathToGlobal(leak, []); log(" * global: " + global); if (global) log(" * global description: " + JSON.stringify(inspect.getGlobalDescription(cx, global))); log("Function source:\n" + jsapi.stringifyFunction(cx, obj)); } if (!verbose) return; let path = []; let global = getPathToGlobal(leak, path); let paths = path.map(function (o) { let classname = jsapi.getClassName(o); return { self: o, src: classname == "Function" ? jsapi.stringifyFunction(cx, o) : "", name: classname, enum: jsapi.enumerate(cx, o) }; }); log(" * global: "+global); if (global) log(" * global description: " + JSON.stringify(inspect.getGlobalDescription(cx, global))); let n=0; let nMax = 100; leak.edges.forEach(function (e) { let o2 = jsapi.getPointerForAddress(e.to.address); let g2; if (e.to.name.indexOf("JS Object (") == 0) { if (n++>nMax) return; g2 = getPathToGlobal(e.to, []); } log(" * edge." + e.name + " " + o2 + "=" + e.to.name + (g2?" global:"+g2:"")); }); n=0; leak.owners.forEach(function (e) { let o2 = jsapi.getPointerForAddress(e.from.address); let g2; if (e.from.name.indexOf("JS Object (") == 0) { if (n++>nMax) return; g2 = getPathToGlobal(e.from, []); } log(" * owner." + e.name + " " + o2 + "=" + e.from.name + (g2?" global:"+g2:"")); }); log(" * enum: \n" + JSON.stringify(jsapi.enumerate(cx, obj), null, 4)); log(" * name: " + jsapi.getPropertyString(cx, obj, "name")); log("<<<"); }