js/src/devtools/rootAnalysis/computeCallgraph.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: Javascript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     3 "use strict";
     5 loadRelativeToScript('utility.js');
     6 loadRelativeToScript('annotations.js');
     7 loadRelativeToScript('CFG.js');
     9 var subclasses = {};
    10 var superclasses = {};
    11 var classFunctions = {};
    13 var fieldCallSeen = {};
    15 function addClassEntry(index, name, other)
    16 {
    17     if (!(name in index)) {
    18         index[name] = [other];
    19         return;
    20     }
    22     for (var entry of index[name]) {
    23         if (entry == other)
    24             return;
    25     }
    27     index[name].push(other);
    28 }
    30 // CSU is "Class/Struct/Union"
    31 function processCSU(csuName, csu)
    32 {
    33     if (!("FunctionField" in csu))
    34         return;
    35     for (var field of csu.FunctionField) {
    36         if (1 in field.Field) {
    37             var superclass = field.Field[1].Type.Name;
    38             var subclass = field.Field[1].FieldCSU.Type.Name;
    39             assert(subclass == csuName);
    40             addClassEntry(subclasses, superclass, subclass);
    41             addClassEntry(superclasses, subclass, superclass);
    42         }
    43         if ("Variable" in field) {
    44             // Note: not dealing with overloading correctly.
    45             var name = field.Variable.Name[0];
    46             var key = csuName + ":" + field.Field[0].Name[0];
    47             if (!(key in classFunctions))
    48                 classFunctions[key] = [];
    49             classFunctions[key].push(name);
    50         }
    51     }
    52 }
    54 function findVirtualFunctions(initialCSU, field, suppressed)
    55 {
    56     var worklist = [initialCSU];
    58     // Virtual call targets on subclasses of nsISupports may be incomplete,
    59     // if the interface is scriptable. Just treat all indirect calls on
    60     // nsISupports objects as potentially GC'ing, except AddRef/Release
    61     // which should never enter the JS engine (even when calling dtors).
    62     while (worklist.length) {
    63         var csu = worklist.pop();
    64         if (csu == "nsISupports" && (field == "AddRef" || field == "Release")) {
    65             suppressed[0] = true;
    66             return [];
    67         }
    68         if (isOverridableField(initialCSU, csu, field))
    69             return null;
    71         if (csu in superclasses) {
    72             for (var superclass of superclasses[csu])
    73                 worklist.push(superclass);
    74         }
    75     }
    77     var functions = [];
    78     var worklist = [csu];
    80     while (worklist.length) {
    81         var csu = worklist.pop();
    82         var key = csu + ":" + field;
    84         if (key in classFunctions) {
    85             for (var name of classFunctions[key])
    86                 functions.push(name);
    87         }
    89         if (csu in subclasses) {
    90             for (var subclass of subclasses[csu])
    91                 worklist.push(subclass);
    92         }
    93     }
    95     return functions;
    96 }
    98 var memoized = {};
    99 var memoizedCount = 0;
   101 function memo(name)
   102 {
   103     if (!(name in memoized)) {
   104         memoizedCount++;
   105         memoized[name] = "" + memoizedCount;
   106         print("#" + memoizedCount + " " + name);
   107     }
   108     return memoized[name];
   109 }
   111 var seenCallees = null;
   112 var seenSuppressedCallees = null;
   114 // Return a list of all callees that the given edge might be a call to. Each
   115 // one is represented by an object with a 'kind' field that is one of
   116 // ('direct', 'field', 'indirect', 'unknown').
   117 function getCallees(edge)
   118 {
   119     if (edge.Kind != "Call")
   120         return [];
   122     var callee = edge.Exp[0];
   123     var callees = [];
   124     if (callee.Kind == "Var") {
   125         assert(callee.Variable.Kind == "Func");
   126         callees.push({'kind': 'direct', 'name': callee.Variable.Name[0]});
   127     } else {
   128         assert(callee.Kind == "Drf");
   129         if (callee.Exp[0].Kind == "Fld") {
   130             var field = callee.Exp[0].Field;
   131             var fieldName = field.Name[0];
   132             var csuName = field.FieldCSU.Type.Name;
   133             var functions = null;
   134             if ("FieldInstanceFunction" in field) {
   135                 var suppressed = [ false ];
   136                 functions = findVirtualFunctions(csuName, fieldName, suppressed);
   137                 if (suppressed[0]) {
   138                     // Field call known to not GC; mark it as suppressed so
   139                     // direct invocations will be ignored
   140                     callees.push({'kind': "field", 'csu': csuName, 'field': fieldName,
   141                                   'suppressed': true});
   142                 }
   143             }
   144             if (functions) {
   145                 // Known set of virtual call targets. Treat them as direct
   146                 // calls to all possible resolved types, but also record edges
   147                 // from this field call to each final callee. When the analysis
   148                 // is checking whether an edge can GC and it sees an unrooted
   149                 // pointer held live across this field call, it will know
   150                 // whether any of the direct callees can GC or not.
   151                 var targets = [];
   152                 for (var name of functions) {
   153                     callees.push({'kind': "direct", 'name': name});
   154                     targets.push({'kind': "direct", 'name': name});
   155                 }
   156                 callees.push({'kind': "resolved-field", 'csu': csuName, 'field': fieldName, 'callees': targets});
   157             } else {
   158                 // Unknown set of call targets. Non-virtual field call,
   159                 // or virtual call on an nsISupports object.
   160                 callees.push({'kind': "field", 'csu': csuName, 'field': fieldName});
   161             }
   162         } else if (callee.Exp[0].Kind == "Var") {
   163             // indirect call through a variable.
   164             callees.push({'kind': "indirect", 'variable': callee.Exp[0].Variable.Name[0]});
   165         } else {
   166             // unknown call target.
   167             callees.push({'kind': "unknown"});
   168         }
   169     }
   171     return callees;
   172 }
   174 var lastline;
   175 function printOnce(line)
   176 {
   177     if (line != lastline) {
   178         print(line);
   179         lastline = line;
   180     }
   181 }
   183 function processBody(caller, body)
   184 {
   185     if (!('PEdge' in body))
   186         return;
   188     lastline = null;
   189     for (var edge of body.PEdge) {
   190         if (edge.Kind != "Call")
   191             continue;
   192         var edgeSuppressed = false;
   193         var seen = seenCallees;
   194         if (edge.Index[0] in body.suppressed) {
   195             edgeSuppressed = true;
   196             seen = seenSuppressedCallees;
   197         }
   198         for (var callee of getCallees(edge)) {
   199             var prologue = (edgeSuppressed || callee.suppressed) ? "SUPPRESS_GC " : "";
   200             prologue += memo(caller) + " ";
   201             if (callee.kind == 'direct') {
   202                 if (!(callee.name in seen)) {
   203                     seen[name] = true;
   204                     printOnce("D " + prologue + memo(callee.name));
   205                 }
   206             } else if (callee.kind == 'field') {
   207                 var { csu, field } = callee;
   208                 printOnce("F " + prologue + "CLASS " + csu + " FIELD " + field);
   209             } else if (callee.kind == 'resolved-field') {
   210                 // Fully-resolved field call (usually a virtual method). Record
   211                 // the callgraph edges. Do not consider suppression, since it
   212                 // is local to this callsite and we are writing out a global
   213                 // record here.
   214                 //
   215                 // Any field call that does *not* have an R entry must be
   216                 // assumed to call anything.
   217                 var { csu, field, callees } = callee;
   218                 var fullFieldName = csu + "." + field;
   219                 if (!(fullFieldName in fieldCallSeen)) {
   220                     fieldCallSeen[fullFieldName] = true;
   221                     for (var target of callees)
   222                         printOnce("R " + memo(fullFieldName) + " " + memo(target.name));
   223                 }
   224             } else if (callee.kind == 'indirect') {
   225                 printOnce("I " + prologue + "VARIABLE " + callee.variable);
   226             } else if (callee.kind == 'unknown') {
   227                 printOnce("I " + prologue + "VARIABLE UNKNOWN");
   228             } else {
   229                 printErr("invalid " + callee.kind + " callee");
   230                 debugger;
   231             }
   232         }
   233     }
   234 }
   236 var callgraph = {};
   238 var xdb = xdbLibrary();
   239 xdb.open("src_comp.xdb");
   241 var minStream = xdb.min_data_stream();
   242 var maxStream = xdb.max_data_stream();
   244 for (var csuIndex = minStream; csuIndex <= maxStream; csuIndex++) {
   245     var csu = xdb.read_key(csuIndex);
   246     var data = xdb.read_entry(csu);
   247     var json = JSON.parse(data.readString());
   248     processCSU(csu.readString(), json[0]);
   250     xdb.free_string(csu);
   251     xdb.free_string(data);
   252 }
   254 xdb.open("src_body.xdb");
   256 printErr("Finished loading data structures");
   258 var minStream = xdb.min_data_stream();
   259 var maxStream = xdb.max_data_stream();
   261 for (var nameIndex = minStream; nameIndex <= maxStream; nameIndex++) {
   262     var name = xdb.read_key(nameIndex);
   263     var data = xdb.read_entry(name);
   264     functionBodies = JSON.parse(data.readString());
   265     for (var body of functionBodies)
   266         body.suppressed = [];
   267     for (var body of functionBodies) {
   268         for (var [pbody, id] of allRAIIGuardedCallPoints(body, isSuppressConstructor))
   269             pbody.suppressed[id] = true;
   270     }
   272     seenCallees = {};
   273     seenSuppressedCallees = {};
   275     var functionName = name.readString();
   276     for (var body of functionBodies)
   277         processBody(functionName, body);
   279     xdb.free_string(name);
   280     xdb.free_string(data);
   281 }

mercurial