blob: 610b2a1c6a62208c077201ed9b96e6c737d2e209 [file] [log] [blame]
/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
"use strict";
loadRelativeToScript('utility.js');
loadRelativeToScript('annotations.js');
var annotatedGCPointers = [];
function processCSU(csu, body)
{
if (!("DataField" in body))
return;
for (var field of body.DataField) {
var type = field.Field.Type;
var fieldName = field.Field.Name[0];
if (type.Kind == "Pointer") {
var target = type.Type;
if (target.Kind == "CSU")
addNestedPointer(csu, target.Name, fieldName);
}
if (type.Kind == "Array") {
var target = type.Type;
if (target.Kind == "CSU")
addNestedStructure(csu, target.Name, fieldName);
}
if (type.Kind == "CSU") {
// Ignore nesting in classes which are AutoGCRooters. We only consider
// types with fields that may not be properly rooted.
if (type.Name == "JS::AutoGCRooter" || type.Name == "JS::CustomAutoRooter")
return;
addNestedStructure(csu, type.Name, fieldName);
}
}
if (isGCPointer(csu))
annotatedGCPointers.push(csu);
}
var structureParents = {}; // Map from field => list of <parent, fieldName>
var pointerParents = {}; // Map from field => list of <parent, fieldName>
function addNestedStructure(csu, inner, field)
{
if (!(inner in structureParents))
structureParents[inner] = [];
structureParents[inner].push([ csu, field ]);
}
function addNestedPointer(csu, inner, field)
{
if (!(inner in pointerParents))
pointerParents[inner] = [];
pointerParents[inner].push([ csu, field ]);
}
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());
assert(json.length == 1);
processCSU(csu.readString(), json[0]);
xdb.free_string(csu);
xdb.free_string(data);
}
var gcTypes = {}; // map from parent struct => Set of GC typed children
var gcPointers = {}; // map from parent struct => Set of GC typed children
var nonGCTypes = {}; // set of types that would ordinarily be GC types but we are suppressing
var nonGCPointers = {}; // set of types that would ordinarily be GC pointers but we are suppressing
var gcFields = new Map;
function stars(n) { return n ? '*' + stars(n-1) : '' };
// "typeName is a (pointer to a)^'typePtrLevel' GC type because it contains a field
// named 'child' of type 'why' (or pointer to 'why' if fieldPtrLevel == 1), which is
// itself a GCThing or GCPointer."
function markGCType(typeName, child, why, typePtrLevel, fieldPtrLevel, indent)
{
//printErr(`${indent}${typeName}${stars(typePtrLevel)} may be a gctype/ptr because of its child '${child}' of type ${why}${stars(fieldPtrLevel)}`);
// Some types, like UniquePtr, do not mark/trace/relocate their contained
// pointers and so should not hold them live across a GC. UniquePtr in
// particular should be the only thing pointing to a structure containing a
// GCPointer, so nothing else can possibly trace it and it'll die when the
// UniquePtr goes out of scope. So we say that memory pointed to by a
// UniquePtr is just as unsafe as the stack for storing GC pointers.
if (!fieldPtrLevel && isUnsafeStorage(typeName)) {
// The UniquePtr itself is on the stack but when you dereference the
// contained pointer, you get to the unsafe memory that we are treating
// as if it were the stack (aka ptrLevel 0). Note that
// UniquePtr<UniquePtr<JSObject*>> is fine, so we don't want to just
// hardcode the ptrLevel.
fieldPtrLevel = -1;
}
// Example: with:
// struct Pair { JSObject* foo; int bar; };
// struct { Pair** info }***
// make a call to:
// child='info' typePtrLevel=3 fieldPtrLevel=2
// for a final ptrLevel of 5, used to later call:
// child='foo' typePtrLevel=5 fieldPtrLevel=1
//
var ptrLevel = typePtrLevel + fieldPtrLevel;
// ...except when > 2 levels of pointers away from an actual GC thing, stop
// searching the graph. (This would just be > 1, except that a UniquePtr
// field might still have a GC pointer.)
if (ptrLevel > 2)
return;
if (ptrLevel == 0 && isRootedGCTypeName(typeName))
return;
if (ptrLevel == 1 && isRootedGCPointerTypeName(typeName))
return;
if (ptrLevel == 0) {
if (typeName in nonGCTypes)
return;
if (!(typeName in gcTypes))
gcTypes[typeName] = new Set();
gcTypes[typeName].add(why);
} else if (ptrLevel == 1) {
if (typeName in nonGCPointers)
return;
if (!(typeName in gcPointers))
gcPointers[typeName] = new Set();
gcPointers[typeName].add(why);
}
if (ptrLevel < 2) {
if (!gcFields.has(typeName))
gcFields.set(typeName, new Map());
gcFields.get(typeName).set(child, [ why, fieldPtrLevel ]);
}
if (typeName in structureParents) {
for (var field of structureParents[typeName]) {
var [ holderType, fieldName ] = field;
markGCType(holderType, fieldName, typeName, ptrLevel, 0, indent + " ");
}
}
if (typeName in pointerParents) {
for (var field of pointerParents[typeName]) {
var [ holderType, fieldName ] = field;
markGCType(holderType, fieldName, typeName, ptrLevel, 1, indent + " ");
}
}
}
function addGCType(typeName, child, why, depth, fieldPtrLevel)
{
markGCType(typeName, '<annotation>', '(annotation)', 0, 0, "");
}
function addGCPointer(typeName)
{
markGCType(typeName, '<pointer-annotation>', '(annotation)', 1, 0, "");
}
for (var type of listNonGCTypes())
nonGCTypes[type] = true;
for (var type of listNonGCPointers())
nonGCPointers[type] = true;
for (var type of listGCTypes())
addGCType(type);
for (var type of listGCPointers())
addGCPointer(type);
for (var typeName of annotatedGCPointers)
addGCPointer(typeName);
function explain(csu, indent, seen) {
if (!seen)
seen = new Set();
seen.add(csu);
if (!gcFields.has(csu))
return;
var fields = gcFields.get(csu);
if (fields.has('<annotation>')) {
print(indent + "which is a GCThing because I said so");
return;
}
if (fields.has('<pointer-annotation>')) {
print(indent + "which is a GCPointer because I said so");
return;
}
for (var [ field, [ child, ptrdness ] ] of fields) {
var inherit = "";
if (field == "field:0")
inherit = " (probably via inheritance)";
var msg = indent + "contains field '" + field + "' ";
if (ptrdness == -1)
msg += "(with a pointer to unsafe storage) holding a ";
else if (ptrdness == 0)
msg += "of type ";
else
msg += "pointing to type ";
msg += child + inherit;
print(msg);
if (!seen.has(child))
explain(child, indent + " ", seen);
}
}
for (var csu in gcTypes) {
print("GCThing: " + csu);
explain(csu, " ");
}
for (var csu in gcPointers) {
print("GCPointer: " + csu);
explain(csu, " ");
}