blob: 1590d61ba956400ccfcd39861092da3c4da49c3d [file] [log] [blame]
/* -*- Mode: Javascript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
"use strict";
var calleeGraph = {};
var callerGraph = {};
var gcFunctions = {};
var suppressedFunctions = {};
function addGCFunction(caller, reason)
{
if (caller in suppressedFunctions)
return false;
if (ignoreGCFunction(caller))
return false;
if (!(caller in gcFunctions)) {
gcFunctions[caller] = reason;
return true;
}
return false;
}
function addCallEdge(caller, callee, suppressed)
{
if (!(caller in calleeGraph))
calleeGraph[caller] = [];
calleeGraph[caller].push({callee:callee, suppressed:suppressed});
if (!(callee in callerGraph))
callerGraph[callee] = [];
callerGraph[callee].push({caller:caller, suppressed:suppressed});
}
var functionNames = [""];
function loadCallgraph(file)
{
var textLines = snarf(file).split('\n');
for (var line of textLines) {
var match;
if (match = /^\#(\d+) (.*)/.exec(line)) {
assert(functionNames.length == match[1]);
functionNames.push(match[2]);
continue;
}
var suppressed = false;
if (/SUPPRESS_GC/.test(line)) {
match = /^(..)SUPPRESS_GC (.*)/.exec(line);
line = match[1] + match[2];
suppressed = true;
}
if (match = /^I (\d+) VARIABLE ([^\,]*)/.exec(line)) {
var caller = functionNames[match[1]];
var name = match[2];
if (!indirectCallCannotGC(caller, name) && !suppressed)
addGCFunction(caller, "IndirectCall: " + name);
} else if (match = /^F (\d+) CLASS (.*?) FIELD (.*)/.exec(line)) {
var caller = functionNames[match[1]];
var csu = match[2];
var fullfield = csu + "." + match[3];
if (!fieldCallCannotGC(csu, fullfield) && !suppressed)
addGCFunction(caller, "FieldCall: " + fullfield);
} else if (match = /^D (\d+) (\d+)/.exec(line)) {
var caller = functionNames[match[1]];
var callee = functionNames[match[2]];
addCallEdge(caller, callee, suppressed);
}
}
var worklist = [];
for (var name in callerGraph)
suppressedFunctions[name] = true;
for (var name in calleeGraph) {
if (!(name in callerGraph)) {
suppressedFunctions[name] = true;
worklist.push(name);
}
}
while (worklist.length) {
name = worklist.pop();
if (shouldSuppressGC(name))
continue;
if (!(name in suppressedFunctions))
continue;
delete suppressedFunctions[name];
if (!(name in calleeGraph))
continue;
for (var entry of calleeGraph[name]) {
if (!entry.suppressed)
worklist.push(entry.callee);
}
}
for (var name in gcFunctions) {
if (name in suppressedFunctions)
delete gcFunctions[name];
}
var gcName = 'void js::GC(JSRuntime*, uint32, uint32)';
assert(gcName in callerGraph);
addGCFunction(gcName, "GC");
var worklist = [];
for (var name in gcFunctions)
worklist.push(name);
while (worklist.length) {
name = worklist.pop();
assert(name in gcFunctions);
if (!(name in callerGraph))
continue;
for (var entry of callerGraph[name]) {
if (!entry.suppressed && addGCFunction(entry.caller, name))
worklist.push(entry.caller);
}
}
}