Sat, 03 Jan 2015 20:18:00 +0100
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');
7 // Functions come out of sixgill in the form "mangled|readable". The mangled
8 // name is Truth. One mangled name might correspond to multiple readable names,
9 // for multiple reasons, including (1) sixgill/gcc doesn't always qualify types
10 // the same way or de-typedef the same amount; (2) sixgill's output treats
11 // references and pointers the same, and so doesn't distinguish them, but C++
12 // treats them as separate for overloading and linking; (3) (identical)
13 // destructors sometimes have an int32 parameter, sometimes not.
14 //
15 // The readable names are useful because they're far more meaningful to the
16 // user, and are what should show up in reports and questions to mrgiggles. At
17 // least in most cases, it's fine to have the extra mangled name tacked onto
18 // the beginning for these.
19 //
20 // The strategy used is to separate out the pieces whenever they are read in,
21 // create a table mapping mangled names to (one of the) readable names, and
22 // use the mangled names in all computation.
23 //
24 // Note that callgraph.txt uses a compressed representation -- each name is
25 // mapped to an integer, and those integers are what is recorded in the edges.
26 // But the integers depend on the full name, whereas the true edge should only
27 // consider the mangled name. And some of the names encoded in callgraph.txt
28 // are FieldCalls, not just function names.
30 var readableNames = {}; // map from mangled name => list of readable names
31 var mangledName = {}; // map from demangled names => mangled names. Could be eliminated.
32 var calleeGraph = {}; // map from mangled => list of tuples of {'callee':mangled, 'suppressed':bool}
33 var callerGraph = {}; // map from mangled => list of tuples of {'caller':mangled, 'suppressed':bool}
34 var gcFunctions = {}; // map from mangled callee => reason
35 var suppressedFunctions = {}; // set of mangled names (map from mangled name => true)
36 var gcEdges = {};
38 function addGCFunction(caller, reason)
39 {
40 if (caller in suppressedFunctions)
41 return false;
43 if (ignoreGCFunction(caller))
44 return false;
46 if (!(caller in gcFunctions)) {
47 gcFunctions[caller] = reason;
48 return true;
49 }
51 return false;
52 }
54 function addCallEdge(caller, callee, suppressed)
55 {
56 if (!(caller in calleeGraph))
57 calleeGraph[caller] = [];
58 calleeGraph[caller].push({callee:callee, suppressed:suppressed});
60 if (!(callee in callerGraph))
61 callerGraph[callee] = [];
62 callerGraph[callee].push({caller:caller, suppressed:suppressed});
63 }
65 // Map from identifier to full "mangled|readable" name. Or sometimes to a
66 // Class.Field name.
67 var functionNames = [""];
69 // Map from identifier to mangled name (or to a Class.Field)
70 var idToMangled = [""];
72 function loadCallgraph(file)
73 {
74 var suppressedFieldCalls = {};
75 var resolvedFunctions = {};
77 var textLines = snarf(file).split('\n');
78 for (var line of textLines) {
79 var match;
80 if (match = line.charAt(0) == "#" && /^\#(\d+) (.*)/.exec(line)) {
81 assert(functionNames.length == match[1]);
82 functionNames.push(match[2]);
83 var [ mangled, readable ] = splitFunction(match[2]);
84 if (mangled in readableNames)
85 readableNames[mangled].push(readable);
86 else
87 readableNames[mangled] = [ readable ];
88 mangledName[readable] = mangled;
89 idToMangled.push(mangled);
90 continue;
91 }
92 var suppressed = false;
93 if (line.indexOf("SUPPRESS_GC") != -1) {
94 match = /^(..)SUPPRESS_GC (.*)/.exec(line);
95 line = match[1] + match[2];
96 suppressed = true;
97 }
98 var tag = line.charAt(0);
99 if (match = tag == 'I' && /^I (\d+) VARIABLE ([^\,]*)/.exec(line)) {
100 var mangledCaller = idToMangled[match[1]];
101 var name = match[2];
102 if (!indirectCallCannotGC(functionNames[match[1]], name) && !suppressed)
103 addGCFunction(mangledCaller, "IndirectCall: " + name);
104 } else if (match = tag == 'F' && /^F (\d+) CLASS (.*?) FIELD (.*)/.exec(line)) {
105 var caller = idToMangled[match[1]];
106 var csu = match[2];
107 var fullfield = csu + "." + match[3];
108 if (suppressed)
109 suppressedFieldCalls[fullfield] = true;
110 else if (!fieldCallCannotGC(csu, fullfield))
111 addGCFunction(caller, "FieldCall: " + fullfield);
112 } else if (match = tag == 'D' && /^D (\d+) (\d+)/.exec(line)) {
113 var caller = idToMangled[match[1]];
114 var callee = idToMangled[match[2]];
115 addCallEdge(caller, callee, suppressed);
116 } else if (match = tag == 'R' && /^R (\d+) (\d+)/.exec(line)) {
117 var callerField = idToMangled[match[1]];
118 var callee = idToMangled[match[2]];
119 addCallEdge(callerField, callee, false);
120 resolvedFunctions[callerField] = true;
121 }
122 }
124 // Initialize suppressedFunctions to the set of all functions, and the
125 // worklist to all toplevel callers.
126 var worklist = [];
127 for (var callee in callerGraph)
128 suppressedFunctions[callee] = true;
129 for (var caller in calleeGraph) {
130 if (!(caller in callerGraph)) {
131 suppressedFunctions[caller] = true;
132 worklist.push(caller);
133 }
134 }
136 // Find all functions reachable via an unsuppressed call chain, and remove
137 // them from the suppressedFunctions set. Everything remaining is only
138 // reachable when GC is suppressed.
139 var top = worklist.length;
140 while (top > 0) {
141 name = worklist[--top];
142 if (!(name in suppressedFunctions))
143 continue;
144 delete suppressedFunctions[name];
145 if (!(name in calleeGraph))
146 continue;
147 for (var entry of calleeGraph[name]) {
148 if (!entry.suppressed)
149 worklist[top++] = entry.callee;
150 }
151 }
153 // Such functions are known to not GC.
154 for (var name in gcFunctions) {
155 if (name in suppressedFunctions)
156 delete gcFunctions[name];
157 }
159 for (var name in suppressedFieldCalls) {
160 suppressedFunctions[name] = true;
161 }
163 for (var gcName of [ 'jsgc.cpp:void Collect(JSRuntime*, uint8, int64, uint32, uint32)',
164 'void js::MinorGC(JSRuntime*, uint32)' ])
165 {
166 assert(gcName in mangledName);
167 addGCFunction(mangledName[gcName], "GC");
168 }
170 // Initialize the worklist to all known gcFunctions.
171 var worklist = [];
172 for (var name in gcFunctions)
173 worklist.push(name);
175 // Recursively find all callers and add them to the set of gcFunctions.
176 while (worklist.length) {
177 name = worklist.shift();
178 assert(name in gcFunctions);
179 if (!(name in callerGraph))
180 continue;
181 for (var entry of callerGraph[name]) {
182 if (!entry.suppressed && addGCFunction(entry.caller, name))
183 worklist.push(entry.caller);
184 }
185 }
187 // Any field call that has been resolved to all possible callees can be
188 // trusted to not GC if all of those callees are known to not GC.
189 for (var name in resolvedFunctions) {
190 if (!(name in gcFunctions))
191 suppressedFunctions[name] = true;
192 }
193 }