 }
 
 function printClasses(classes) {
+    var destructors = 0;
+    var exacts = 0;
+    var classcount = 0;
+
+    print("Legend:");
+    print("  D - at least one of the definitions has an explicit destructor");
+    print("  E - at least one of the definitions is exactly traced");
+    print("  M - multiple definitions in the tree (in different namespaces or nested within classes)");
+    print("  # - continuation line for M with additional locations");
+    print("  X - definitions of the same name exist outside the tree");
+    print("  @ - continuation line for @ with locations");
+    print("");
+
     printCls(classes[rootclass], 0);
+    print("");
+    print("Classes: " + classcount);
+    print("Explicit destructors: " + destructors);
+    print("Exactly traced: " + exacts + " (" + Math.round(exacts/classcount*100) + "%)");
 
     function printCls(cls, indent) {
         if (!ignoreIt(cls)) {
-            var d = cls.destructor ? " * " : "   ";
+            classcount++;
+            if (cls.destructor) destructors++;
+            if (cls.isExact) exacts++;
+            var d = " " + (cls.destructor ? "D" : " ") + (cls.isExact ? "E" : " ") + (cls.defcount > 1 ? "M" : " ") + (cls.external ? "X" : " ") + "  ";
             print(d + padTo50(spaces(indent) + cls.derived) + " " + cls.filename + ":" + cls.lineno);
+            if (cls.defcount > 1) {
+                var locs = cls.otherlines;
+                for ( var l=0 ; l < locs.length ; l++ )
+                    print("   #    " + padTo50("") + locs[l].filename + ":" + locs[l].lineno);
+            }
+            if (cls.external) {
+                var locs = cls.exolines;
+                for ( var l=0 ; l < locs.length ; l++ )
+                    print("    @   " + padTo50("") + locs[l].filename + ":" + locs[l].lineno);
+            }
         }
         cls.subrefs.sort(function (a,b) { return strcmp(a.derived, b.derived); })
         for ( var i=0 ; i < cls.subrefs.length ; i++ ) {

    [ new RegExp("^.*/shell/.*$"),
     new RegExp("^.*/extensions/ST_.*$"),
     new RegExp("^.*/extensions/JavaGlue.*$"),
     new RegExp("^.*/MMgc/GCTests.*$")];

class LineInfo
{
    function LineInfo(_derived, _base, _filename, _lineno) {
        derived = _derived;
        base = _base;
        filename = _filename;
        lineno = _lineno;
    }
    var derived;            // name of the class being defined
    var base;               // name of its base class
    var filename;           // file where we found the definition of 'derived'
    var lineno;             // line where we found the definition of 'derived'
    var baseref = null;     // reference to the LineInfo for the base class
    var subrefs = [];       // list of known subclasses
    var destructor = false; // true if we believe this has a destructor
}

main(System.argv);

function readFiles(argv) {
    var filenames_dict = {};
    var probable_destructors = {};
    var probable_tracers = {};
    var all_lines = [];
    // Hack, hack, hack
    var re1 = new RegExp("class\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*:.*(?:public|private|virtual)\\s*(?:MMgc\\s*::\\s*)?([A-Za-z0-9_:]+)");
    var re2 = new RegExp("class\\s+GC_(?:MANUAL|AS3|CPP)_EXACT(?:[A-Z_]+)?\\(([^,\\)]+)\\s*,\\s*([^,\\)]+)");
    var red = new RegExp("~\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*\\(");
    var red2 = new RegExp("virtual\\s*~\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*\\(.*\\)\\s*{\\s*}");
    var rtrace = new RegExp("([A-Za-z0-9_]+)\\s*::\\s*gcTrace");

    compute(argv,false);
    annotateDestructors();
    annotateTracers();
    return all_lines;

    function annotateDestructors() {
        for ( var i=0 ; i < all_lines.length ; i++ ) {
            var ln = all_lines[i];
            ln.destructor = probable_destructors.hasOwnProperty(ln.derived);
        }
    }

    function annotateTracers() {
        for ( var i=0 ; i < all_lines.length ; i++ ) {
            var ln = all_lines[i];
            ln.isExact = ln.isExact || probable_tracers.hasOwnProperty(ln.derived);
        }
    }

