blob: 2bec0b55535645bb730be939b4fcc52e731c005c [file] [log] [blame]
/* -*- Mode: Javascript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
"use strict";
load('utility.js');
load('annotations.js');
load('suppressedPoints.js');
var subclasses = {};
var superclasses = {};
var classFunctions = {};
function addClassEntry(index, name, other)
{
if (!(name in index)) {
index[name] = [other];
return;
}
for (var entry of index[name]) {
if (entry == other)
return;
}
index[name].push(other);
}
function processCSU(csuName, csu)
{
if (!("FunctionField" in csu))
return;
for (var field of csu.FunctionField) {
if (1 in field.Field) {
var superclass = field.Field[1].Type.Name;
var subclass = field.Field[1].FieldCSU.Type.Name;
assert(subclass == csuName);
addClassEntry(subclasses, superclass, subclass);
addClassEntry(superclasses, subclass, superclass);
}
if ("Variable" in field) {
// Note: not dealing with overloading correctly.
var name = field.Variable.Name[0];
var key = csuName + ":" + field.Field[0].Name[0];
if (!(key in classFunctions))
classFunctions[key] = [];
classFunctions[key].push(name);
}
}
}
function findVirtualFunctions(csu, field)
{
var worklist = [csu];
// Virtual call targets on subclasses of nsISupports may be incomplete,
// if the interface is scriptable. Just treat all indirect calls on
// nsISupports objects as potentially GC'ing, except AddRef/Release
// which should never enter the JS engine (even when calling dtors).
while (worklist.length) {
var csu = worklist.pop();
if (csu == "nsISupports") {
if (field == "AddRef" || field == "Release")
return [];
return null;
}
if (csu in superclasses) {
for (var superclass of superclasses[csu])
worklist.push(superclass);
}
}
var functions = [];
var worklist = [csu];
while (worklist.length) {
var csu = worklist.pop();
var key = csu + ":" + field;
if (key in classFunctions) {
for (var name of classFunctions[key])
functions.push(name);
}
if (csu in subclasses) {
for (var subclass of subclasses[csu])
worklist.push(subclass);
}
}
return functions;
}
var memoized = {};
var memoizedCount = 0;
function memo(name)
{
if (!(name in memoized)) {
memoizedCount++;
memoized[name] = "" + memoizedCount;
print("#" + memoizedCount + " " + name);
}
return memoized[name];
}
var seenCallees = null;
var seenSuppressedCallees = null;
function processBody(caller, body)
{
if (!('PEdge' in body))
return;
for (var edge of body.PEdge) {
if (edge.Kind != "Call")
continue;
var callee = edge.Exp[0];
var suppressText = "";
var seen = seenCallees;
if (edge.Index[0] in body.suppressed) {
suppressText = "SUPPRESS_GC ";
seen = seenSuppressedCallees;
}
var prologue = suppressText + memo(caller) + " ";
if (callee.Kind == "Var") {
assert(callee.Variable.Kind == "Func");
var name = callee.Variable.Name[0];
if (!(name in seen)) {
print("D " + prologue + memo(name));
seen[name] = true;
}
var otherName = otherDestructorName(name);
if (otherName && !(otherName in seen)) {
print("D " + prologue + memo(otherName));
seen[otherName] = true;
}
} else {
assert(callee.Kind == "Drf");
if (callee.Exp[0].Kind == "Fld") {
var field = callee.Exp[0].Field;
var fieldName = field.Name[0];
var csuName = field.FieldCSU.Type.Name;
var functions = null;
if ("FieldInstanceFunction" in field)
functions = findVirtualFunctions(csuName, fieldName);
if (functions) {
// Known set of virtual call targets.
for (var name of functions) {
if (!(name in seen)) {
print("D " + prologue + memo(name));
seen[name] = true;
}
}
} else {
// Unknown set of call targets. Non-virtual field call,
// or virtual call on an nsISupports object.
print("F " + prologue + "CLASS " + csuName + " FIELD " + fieldName);
}
} else if (callee.Exp[0].Kind == "Var") {
// indirect call through a variable.
print("I " + prologue +
"VARIABLE " + callee.Exp[0].Variable.Name[0]);
} else {
// unknown call target.
print("I " + prologue + "VARIABLE UNKNOWN");
}
}
}
}
var callgraph = {};
var xdb = xdbLibrary();
xdb.open("src_comp.xdb");
var minStream = xdb.min_data_stream();
var maxStream = xdb.max_data_stream();
for (var csuIndex = minStream; csuIndex <= maxStream; csuIndex++) {
var csu = xdb.read_key(csuIndex);
var data = xdb.read_entry(csu);
var json = JSON.parse(data.readString());
processCSU(csu.readString(), json[0]);
xdb.free_string(csu);
xdb.free_string(data);
}
xdb.open("src_body.xdb");
var minStream = xdb.min_data_stream();
var maxStream = xdb.max_data_stream();
for (var nameIndex = minStream; nameIndex <= maxStream; nameIndex++) {
var name = xdb.read_key(nameIndex);
var data = xdb.read_entry(name);
functionBodies = JSON.parse(data.readString());
for (var body of functionBodies)
body.suppressed = [];
for (var body of functionBodies)
computeSuppressedPoints(body);
seenCallees = {};
seenSuppressedCallees = {};
for (var body of functionBodies)
processBody(name.readString(), body);
xdb.free_string(name);
xdb.free_string(data);
}