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.
michael@0 | 1 | /* -*- Mode: Javascript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
michael@0 | 2 | |
michael@0 | 3 | "use strict"; |
michael@0 | 4 | |
michael@0 | 5 | loadRelativeToScript('utility.js'); |
michael@0 | 6 | loadRelativeToScript('annotations.js'); |
michael@0 | 7 | loadRelativeToScript('CFG.js'); |
michael@0 | 8 | |
michael@0 | 9 | var sourceRoot = (environment['SOURCE'] || '') + '/' |
michael@0 | 10 | |
michael@0 | 11 | var functionBodies; |
michael@0 | 12 | |
michael@0 | 13 | if (typeof scriptArgs[0] != 'string' || typeof scriptArgs[1] != 'string') |
michael@0 | 14 | throw "Usage: analyzeRoots.js <gcFunctions.lst> <gcEdges.txt> <suppressedFunctions.lst> <gcTypes.txt> [start end [tmpfile]]"; |
michael@0 | 15 | |
michael@0 | 16 | var gcFunctionsFile = scriptArgs[0]; |
michael@0 | 17 | var gcEdgesFile = scriptArgs[1]; |
michael@0 | 18 | var suppressedFunctionsFile = scriptArgs[2]; |
michael@0 | 19 | var gcTypesFile = scriptArgs[3]; |
michael@0 | 20 | var batch = (scriptArgs[4]|0) || 1; |
michael@0 | 21 | var numBatches = (scriptArgs[5]|0) || 1; |
michael@0 | 22 | var tmpfile = scriptArgs[6] || "tmp.txt"; |
michael@0 | 23 | |
michael@0 | 24 | var gcFunctions = {}; |
michael@0 | 25 | var text = snarf("gcFunctions.lst").split("\n"); |
michael@0 | 26 | assert(text.pop().length == 0); |
michael@0 | 27 | for (var line of text) |
michael@0 | 28 | gcFunctions[mangled(line)] = true; |
michael@0 | 29 | |
michael@0 | 30 | var suppressedFunctions = {}; |
michael@0 | 31 | var text = snarf(suppressedFunctionsFile).split("\n"); |
michael@0 | 32 | assert(text.pop().length == 0); |
michael@0 | 33 | for (var line of text) { |
michael@0 | 34 | suppressedFunctions[line] = true; |
michael@0 | 35 | } |
michael@0 | 36 | text = null; |
michael@0 | 37 | |
michael@0 | 38 | var gcEdges = {}; |
michael@0 | 39 | text = snarf(gcEdgesFile).split('\n'); |
michael@0 | 40 | assert(text.pop().length == 0); |
michael@0 | 41 | for (var line of text) { |
michael@0 | 42 | var [ block, edge, func ] = line.split(" || "); |
michael@0 | 43 | if (!(block in gcEdges)) |
michael@0 | 44 | gcEdges[block] = {} |
michael@0 | 45 | gcEdges[block][edge] = func; |
michael@0 | 46 | } |
michael@0 | 47 | text = null; |
michael@0 | 48 | |
michael@0 | 49 | var match; |
michael@0 | 50 | var gcThings = {}; |
michael@0 | 51 | var gcPointers = {}; |
michael@0 | 52 | |
michael@0 | 53 | text = snarf(gcTypesFile).split("\n"); |
michael@0 | 54 | for (var line of text) { |
michael@0 | 55 | if (match = /^GCThing: (.*)/.exec(line)) |
michael@0 | 56 | gcThings[match[1]] = true; |
michael@0 | 57 | if (match = /^GCPointer: (.*)/.exec(line)) |
michael@0 | 58 | gcPointers[match[1]] = true; |
michael@0 | 59 | } |
michael@0 | 60 | text = null; |
michael@0 | 61 | |
michael@0 | 62 | function isUnrootedType(type) |
michael@0 | 63 | { |
michael@0 | 64 | if (type.Kind == "Pointer") { |
michael@0 | 65 | var target = type.Type; |
michael@0 | 66 | if (target.Kind == "CSU") |
michael@0 | 67 | return target.Name in gcThings; |
michael@0 | 68 | return false; |
michael@0 | 69 | } |
michael@0 | 70 | if (type.Kind == "CSU") |
michael@0 | 71 | return type.Name in gcPointers; |
michael@0 | 72 | return false; |
michael@0 | 73 | } |
michael@0 | 74 | |
michael@0 | 75 | function expressionUsesVariable(exp, variable) |
michael@0 | 76 | { |
michael@0 | 77 | if (exp.Kind == "Var" && sameVariable(exp.Variable, variable)) |
michael@0 | 78 | return true; |
michael@0 | 79 | if (!("Exp" in exp)) |
michael@0 | 80 | return false; |
michael@0 | 81 | for (var childExp of exp.Exp) { |
michael@0 | 82 | if (expressionUsesVariable(childExp, variable)) |
michael@0 | 83 | return true; |
michael@0 | 84 | } |
michael@0 | 85 | return false; |
michael@0 | 86 | } |
michael@0 | 87 | |
michael@0 | 88 | function edgeUsesVariable(edge, variable) |
michael@0 | 89 | { |
michael@0 | 90 | if (ignoreEdgeUse(edge, variable)) |
michael@0 | 91 | return false; |
michael@0 | 92 | switch (edge.Kind) { |
michael@0 | 93 | |
michael@0 | 94 | case "Assign": |
michael@0 | 95 | if (expressionUsesVariable(edge.Exp[0], variable)) |
michael@0 | 96 | return true; |
michael@0 | 97 | return expressionUsesVariable(edge.Exp[1], variable); |
michael@0 | 98 | |
michael@0 | 99 | case "Assume": |
michael@0 | 100 | return expressionUsesVariable(edge.Exp[0], variable); |
michael@0 | 101 | |
michael@0 | 102 | case "Call": |
michael@0 | 103 | if (expressionUsesVariable(edge.Exp[0], variable)) |
michael@0 | 104 | return true; |
michael@0 | 105 | if (1 in edge.Exp && expressionUsesVariable(edge.Exp[1], variable)) |
michael@0 | 106 | return true; |
michael@0 | 107 | if ("PEdgeCallInstance" in edge) { |
michael@0 | 108 | if (expressionUsesVariable(edge.PEdgeCallInstance.Exp, variable)) |
michael@0 | 109 | return true; |
michael@0 | 110 | } |
michael@0 | 111 | if ("PEdgeCallArguments" in edge) { |
michael@0 | 112 | for (var exp of edge.PEdgeCallArguments.Exp) { |
michael@0 | 113 | if (expressionUsesVariable(exp, variable)) |
michael@0 | 114 | return true; |
michael@0 | 115 | } |
michael@0 | 116 | } |
michael@0 | 117 | return false; |
michael@0 | 118 | |
michael@0 | 119 | case "Loop": |
michael@0 | 120 | return false; |
michael@0 | 121 | |
michael@0 | 122 | default: |
michael@0 | 123 | assert(false); |
michael@0 | 124 | } |
michael@0 | 125 | } |
michael@0 | 126 | |
michael@0 | 127 | function expressionIsVariableAddress(exp, variable) |
michael@0 | 128 | { |
michael@0 | 129 | while (exp.Kind == "Fld") |
michael@0 | 130 | exp = exp.Exp[0]; |
michael@0 | 131 | return exp.Kind == "Var" && sameVariable(exp.Variable, variable); |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | function edgeTakesVariableAddress(edge, variable) |
michael@0 | 135 | { |
michael@0 | 136 | if (ignoreEdgeUse(edge, variable)) |
michael@0 | 137 | return false; |
michael@0 | 138 | if (ignoreEdgeAddressTaken(edge)) |
michael@0 | 139 | return false; |
michael@0 | 140 | switch (edge.Kind) { |
michael@0 | 141 | case "Assign": |
michael@0 | 142 | return expressionIsVariableAddress(edge.Exp[1], variable); |
michael@0 | 143 | case "Call": |
michael@0 | 144 | if ("PEdgeCallArguments" in edge) { |
michael@0 | 145 | for (var exp of edge.PEdgeCallArguments.Exp) { |
michael@0 | 146 | if (expressionIsVariableAddress(exp, variable)) |
michael@0 | 147 | return true; |
michael@0 | 148 | } |
michael@0 | 149 | } |
michael@0 | 150 | return false; |
michael@0 | 151 | default: |
michael@0 | 152 | return false; |
michael@0 | 153 | } |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | function edgeKillsVariable(edge, variable) |
michael@0 | 157 | { |
michael@0 | 158 | // Direct assignments kill their lhs. |
michael@0 | 159 | if (edge.Kind == "Assign") { |
michael@0 | 160 | var lhs = edge.Exp[0]; |
michael@0 | 161 | if (lhs.Kind == "Var" && sameVariable(lhs.Variable, variable)) |
michael@0 | 162 | return true; |
michael@0 | 163 | } |
michael@0 | 164 | |
michael@0 | 165 | if (edge.Kind != "Call") |
michael@0 | 166 | return false; |
michael@0 | 167 | |
michael@0 | 168 | // Assignments of call results kill their lhs. |
michael@0 | 169 | if (1 in edge.Exp) { |
michael@0 | 170 | var lhs = edge.Exp[1]; |
michael@0 | 171 | if (lhs.Kind == "Var" && sameVariable(lhs.Variable, variable)) |
michael@0 | 172 | return true; |
michael@0 | 173 | } |
michael@0 | 174 | |
michael@0 | 175 | // Constructor calls kill their 'this' value. |
michael@0 | 176 | if ("PEdgeCallInstance" in edge) { |
michael@0 | 177 | do { |
michael@0 | 178 | var instance = edge.PEdgeCallInstance.Exp; |
michael@0 | 179 | |
michael@0 | 180 | // Kludge around incorrect dereference on some constructor calls. |
michael@0 | 181 | if (instance.Kind == "Drf") |
michael@0 | 182 | instance = instance.Exp[0]; |
michael@0 | 183 | |
michael@0 | 184 | if (instance.Kind != "Var" || !sameVariable(instance.Variable, variable)) |
michael@0 | 185 | break; |
michael@0 | 186 | |
michael@0 | 187 | var callee = edge.Exp[0]; |
michael@0 | 188 | if (callee.Kind != "Var") |
michael@0 | 189 | break; |
michael@0 | 190 | |
michael@0 | 191 | assert(callee.Variable.Kind == "Func"); |
michael@0 | 192 | var calleeName = readable(callee.Variable.Name[0]); |
michael@0 | 193 | |
michael@0 | 194 | // Constructor calls include the text 'Name::Name(' or 'Name<...>::Name('. |
michael@0 | 195 | var openParen = calleeName.indexOf('('); |
michael@0 | 196 | if (openParen < 0) |
michael@0 | 197 | break; |
michael@0 | 198 | calleeName = calleeName.substring(0, openParen); |
michael@0 | 199 | |
michael@0 | 200 | var lastColon = calleeName.lastIndexOf('::'); |
michael@0 | 201 | if (lastColon < 0) |
michael@0 | 202 | break; |
michael@0 | 203 | var constructorName = calleeName.substr(lastColon + 2); |
michael@0 | 204 | calleeName = calleeName.substr(0, lastColon); |
michael@0 | 205 | |
michael@0 | 206 | var lastTemplateOpen = calleeName.lastIndexOf('<'); |
michael@0 | 207 | if (lastTemplateOpen >= 0) |
michael@0 | 208 | calleeName = calleeName.substr(0, lastTemplateOpen); |
michael@0 | 209 | |
michael@0 | 210 | if (calleeName.endsWith(constructorName)) |
michael@0 | 211 | return true; |
michael@0 | 212 | } while (false); |
michael@0 | 213 | } |
michael@0 | 214 | |
michael@0 | 215 | return false; |
michael@0 | 216 | } |
michael@0 | 217 | |
michael@0 | 218 | function edgeCanGC(edge) |
michael@0 | 219 | { |
michael@0 | 220 | if (edge.Kind != "Call") |
michael@0 | 221 | return false; |
michael@0 | 222 | var callee = edge.Exp[0]; |
michael@0 | 223 | if (callee.Kind == "Var") { |
michael@0 | 224 | var variable = callee.Variable; |
michael@0 | 225 | assert(variable.Kind == "Func"); |
michael@0 | 226 | var callee = mangled(variable.Name[0]); |
michael@0 | 227 | if (callee in gcFunctions) |
michael@0 | 228 | return "'" + variable.Name[0] + "'"; |
michael@0 | 229 | return null; |
michael@0 | 230 | } |
michael@0 | 231 | assert(callee.Kind == "Drf"); |
michael@0 | 232 | if (callee.Exp[0].Kind == "Fld") { |
michael@0 | 233 | var field = callee.Exp[0].Field; |
michael@0 | 234 | var csuName = field.FieldCSU.Type.Name; |
michael@0 | 235 | var fullFieldName = csuName + "." + field.Name[0]; |
michael@0 | 236 | if (fieldCallCannotGC(csuName, fullFieldName)) |
michael@0 | 237 | return null; |
michael@0 | 238 | return (fullFieldName in suppressedFunctions) ? null : fullFieldName; |
michael@0 | 239 | } |
michael@0 | 240 | assert(callee.Exp[0].Kind == "Var"); |
michael@0 | 241 | var varName = callee.Exp[0].Variable.Name[0]; |
michael@0 | 242 | return indirectCallCannotGC(functionName, varName) ? null : "*" + varName; |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | function variableUseFollowsGC(suppressed, variable, worklist) |
michael@0 | 246 | { |
michael@0 | 247 | // Scan through all edges following an unrooted variable use, using an |
michael@0 | 248 | // explicit worklist. A worklist contains a following edge together with a |
michael@0 | 249 | // description of where one of its predecessors GC'd (if any). |
michael@0 | 250 | |
michael@0 | 251 | while (worklist.length) { |
michael@0 | 252 | var entry = worklist.pop(); |
michael@0 | 253 | var body = entry.body, ppoint = entry.ppoint; |
michael@0 | 254 | |
michael@0 | 255 | if (body.seen) { |
michael@0 | 256 | if (ppoint in body.seen) { |
michael@0 | 257 | var seenEntry = body.seen[ppoint]; |
michael@0 | 258 | if (!entry.gcInfo || seenEntry.gcInfo) |
michael@0 | 259 | continue; |
michael@0 | 260 | } |
michael@0 | 261 | } else { |
michael@0 | 262 | body.seen = []; |
michael@0 | 263 | } |
michael@0 | 264 | body.seen[ppoint] = {body:body, gcInfo:entry.gcInfo}; |
michael@0 | 265 | |
michael@0 | 266 | if (ppoint == body.Index[0]) { |
michael@0 | 267 | if (body.BlockId.Kind == "Loop") { |
michael@0 | 268 | // propagate to parents that enter the loop body. |
michael@0 | 269 | if ("BlockPPoint" in body) { |
michael@0 | 270 | for (var parent of body.BlockPPoint) { |
michael@0 | 271 | var found = false; |
michael@0 | 272 | for (var xbody of functionBodies) { |
michael@0 | 273 | if (sameBlockId(xbody.BlockId, parent.BlockId)) { |
michael@0 | 274 | assert(!found); |
michael@0 | 275 | found = true; |
michael@0 | 276 | worklist.push({body:xbody, ppoint:parent.Index, |
michael@0 | 277 | gcInfo:entry.gcInfo, why:entry}); |
michael@0 | 278 | } |
michael@0 | 279 | } |
michael@0 | 280 | assert(found); |
michael@0 | 281 | } |
michael@0 | 282 | } |
michael@0 | 283 | } else if (variable.Kind == "Arg" && entry.gcInfo) { |
michael@0 | 284 | return {gcInfo:entry.gcInfo, why:entry}; |
michael@0 | 285 | } |
michael@0 | 286 | } |
michael@0 | 287 | |
michael@0 | 288 | var predecessors = getPredecessors(body); |
michael@0 | 289 | if (!(ppoint in predecessors)) |
michael@0 | 290 | continue; |
michael@0 | 291 | |
michael@0 | 292 | for (var edge of predecessors[ppoint]) { |
michael@0 | 293 | var source = edge.Index[0]; |
michael@0 | 294 | |
michael@0 | 295 | if (edgeKillsVariable(edge, variable)) { |
michael@0 | 296 | if (entry.gcInfo) |
michael@0 | 297 | return {gcInfo:entry.gcInfo, why:entry}; |
michael@0 | 298 | if (!body.minimumUse || source < body.minimumUse) |
michael@0 | 299 | body.minimumUse = source; |
michael@0 | 300 | continue; |
michael@0 | 301 | } |
michael@0 | 302 | |
michael@0 | 303 | var gcInfo = entry.gcInfo; |
michael@0 | 304 | if (!gcInfo && !(source in body.suppressed) && !suppressed) { |
michael@0 | 305 | var gcName = edgeCanGC(edge, body); |
michael@0 | 306 | if (gcName) |
michael@0 | 307 | gcInfo = {name:gcName, body:body, ppoint:source}; |
michael@0 | 308 | } |
michael@0 | 309 | |
michael@0 | 310 | if (edgeUsesVariable(edge, variable)) { |
michael@0 | 311 | if (gcInfo) |
michael@0 | 312 | return {gcInfo:gcInfo, why:entry}; |
michael@0 | 313 | if (!body.minimumUse || source < body.minimumUse) |
michael@0 | 314 | body.minimumUse = source; |
michael@0 | 315 | } |
michael@0 | 316 | |
michael@0 | 317 | if (edge.Kind == "Loop") { |
michael@0 | 318 | // propagate to exit points of the loop body, in addition to the |
michael@0 | 319 | // predecessor of the loop edge itself. |
michael@0 | 320 | var found = false; |
michael@0 | 321 | for (var xbody of functionBodies) { |
michael@0 | 322 | if (sameBlockId(xbody.BlockId, edge.BlockId)) { |
michael@0 | 323 | assert(!found); |
michael@0 | 324 | found = true; |
michael@0 | 325 | worklist.push({body:xbody, ppoint:xbody.Index[1], |
michael@0 | 326 | gcInfo:gcInfo, why:entry}); |
michael@0 | 327 | } |
michael@0 | 328 | } |
michael@0 | 329 | assert(found); |
michael@0 | 330 | break; |
michael@0 | 331 | } |
michael@0 | 332 | worklist.push({body:body, ppoint:source, gcInfo:gcInfo, why:entry}); |
michael@0 | 333 | } |
michael@0 | 334 | } |
michael@0 | 335 | |
michael@0 | 336 | return null; |
michael@0 | 337 | } |
michael@0 | 338 | |
michael@0 | 339 | function variableLiveAcrossGC(suppressed, variable) |
michael@0 | 340 | { |
michael@0 | 341 | // A variable is live across a GC if (1) it is used by an edge, and (2) it |
michael@0 | 342 | // is used after a GC in a successor edge. |
michael@0 | 343 | |
michael@0 | 344 | for (var body of functionBodies) { |
michael@0 | 345 | body.seen = null; |
michael@0 | 346 | body.minimumUse = 0; |
michael@0 | 347 | } |
michael@0 | 348 | |
michael@0 | 349 | for (var body of functionBodies) { |
michael@0 | 350 | if (!("PEdge" in body)) |
michael@0 | 351 | continue; |
michael@0 | 352 | for (var edge of body.PEdge) { |
michael@0 | 353 | if (edgeUsesVariable(edge, variable) && !edgeKillsVariable(edge, variable)) { |
michael@0 | 354 | var worklist = [{body:body, ppoint:edge.Index[0], gcInfo:null, why:null}]; |
michael@0 | 355 | var call = variableUseFollowsGC(suppressed, variable, worklist); |
michael@0 | 356 | if (call) |
michael@0 | 357 | return call; |
michael@0 | 358 | } |
michael@0 | 359 | } |
michael@0 | 360 | } |
michael@0 | 361 | return null; |
michael@0 | 362 | } |
michael@0 | 363 | |
michael@0 | 364 | // An unrooted variable has its address stored in another variable via |
michael@0 | 365 | // assignment, or passed into a function that can GC. If the address is |
michael@0 | 366 | // assigned into some other variable, we can't track it to see if it is held |
michael@0 | 367 | // live across a GC. If it is passed into a function that can GC, then it's |
michael@0 | 368 | // sort of like a Handle to an unrooted location, and the callee could GC |
michael@0 | 369 | // before overwriting it or rooting it. |
michael@0 | 370 | function unsafeVariableAddressTaken(suppressed, variable) |
michael@0 | 371 | { |
michael@0 | 372 | for (var body of functionBodies) { |
michael@0 | 373 | if (!("PEdge" in body)) |
michael@0 | 374 | continue; |
michael@0 | 375 | for (var edge of body.PEdge) { |
michael@0 | 376 | if (edgeTakesVariableAddress(edge, variable)) { |
michael@0 | 377 | if (edge.Kind == "Assign" || (!suppressed && edgeCanGC(edge))) |
michael@0 | 378 | return {body:body, ppoint:edge.Index[0]}; |
michael@0 | 379 | } |
michael@0 | 380 | } |
michael@0 | 381 | } |
michael@0 | 382 | return null; |
michael@0 | 383 | } |
michael@0 | 384 | |
michael@0 | 385 | function computePrintedLines(functionName) |
michael@0 | 386 | { |
michael@0 | 387 | assert(!system("xdbfind src_body.xdb '" + functionName + "' > " + tmpfile)); |
michael@0 | 388 | var lines = snarf(tmpfile).split('\n'); |
michael@0 | 389 | |
michael@0 | 390 | for (var body of functionBodies) |
michael@0 | 391 | body.lines = []; |
michael@0 | 392 | |
michael@0 | 393 | // Distribute lines of output to the block they originate from. |
michael@0 | 394 | var currentBody = null; |
michael@0 | 395 | for (var i = 0; i < lines.length; i++) { |
michael@0 | 396 | var line = lines[i]; |
michael@0 | 397 | if (/^block:/.test(line)) { |
michael@0 | 398 | if (match = /:(loop#[\d#]+)/.exec(line)) { |
michael@0 | 399 | var loop = match[1]; |
michael@0 | 400 | var found = false; |
michael@0 | 401 | for (var body of functionBodies) { |
michael@0 | 402 | if (body.BlockId.Kind == "Loop" && body.BlockId.Loop == loop) { |
michael@0 | 403 | assert(!found); |
michael@0 | 404 | found = true; |
michael@0 | 405 | currentBody = body; |
michael@0 | 406 | } |
michael@0 | 407 | } |
michael@0 | 408 | assert(found); |
michael@0 | 409 | } else { |
michael@0 | 410 | for (var body of functionBodies) { |
michael@0 | 411 | if (body.BlockId.Kind == "Function") |
michael@0 | 412 | currentBody = body; |
michael@0 | 413 | } |
michael@0 | 414 | } |
michael@0 | 415 | } |
michael@0 | 416 | if (currentBody) |
michael@0 | 417 | currentBody.lines.push(line); |
michael@0 | 418 | } |
michael@0 | 419 | } |
michael@0 | 420 | |
michael@0 | 421 | function findLocation(body, ppoint) |
michael@0 | 422 | { |
michael@0 | 423 | var location = body.PPoint[ppoint - 1].Location; |
michael@0 | 424 | var text = location.CacheString + ":" + location.Line; |
michael@0 | 425 | if (text.indexOf(sourceRoot) == 0) |
michael@0 | 426 | return text.substring(sourceRoot.length); |
michael@0 | 427 | return text; |
michael@0 | 428 | } |
michael@0 | 429 | |
michael@0 | 430 | function locationLine(text) |
michael@0 | 431 | { |
michael@0 | 432 | if (match = /:(\d+)$/.exec(text)) |
michael@0 | 433 | return match[1]; |
michael@0 | 434 | return 0; |
michael@0 | 435 | } |
michael@0 | 436 | |
michael@0 | 437 | function printEntryTrace(functionName, entry) |
michael@0 | 438 | { |
michael@0 | 439 | if (!functionBodies[0].lines) |
michael@0 | 440 | computePrintedLines(functionName); |
michael@0 | 441 | |
michael@0 | 442 | while (entry) { |
michael@0 | 443 | var ppoint = entry.ppoint; |
michael@0 | 444 | var lineText = findLocation(entry.body, ppoint); |
michael@0 | 445 | |
michael@0 | 446 | var edgeText = null; |
michael@0 | 447 | if (entry.why && entry.why.body == entry.body) { |
michael@0 | 448 | // If the next point in the trace is in the same block, look for an edge between them. |
michael@0 | 449 | var next = entry.why.ppoint; |
michael@0 | 450 | for (var line of entry.body.lines) { |
michael@0 | 451 | if (match = /\((\d+),(\d+),/.exec(line)) { |
michael@0 | 452 | if (match[1] == ppoint && match[2] == next) |
michael@0 | 453 | edgeText = line; // May be multiple |
michael@0 | 454 | } |
michael@0 | 455 | } |
michael@0 | 456 | assert(edgeText); |
michael@0 | 457 | } else { |
michael@0 | 458 | // Look for any outgoing edge from the chosen point. |
michael@0 | 459 | for (var line of entry.body.lines) { |
michael@0 | 460 | if (match = /\((\d+),/.exec(line)) { |
michael@0 | 461 | if (match[1] == ppoint) { |
michael@0 | 462 | edgeText = line; |
michael@0 | 463 | break; |
michael@0 | 464 | } |
michael@0 | 465 | } |
michael@0 | 466 | } |
michael@0 | 467 | } |
michael@0 | 468 | |
michael@0 | 469 | print(" " + lineText + (edgeText ? ": " + edgeText : "")); |
michael@0 | 470 | entry = entry.why; |
michael@0 | 471 | } |
michael@0 | 472 | } |
michael@0 | 473 | |
michael@0 | 474 | function isRootedType(type) |
michael@0 | 475 | { |
michael@0 | 476 | return type.Kind == "CSU" && isRootedTypeName(type.Name); |
michael@0 | 477 | } |
michael@0 | 478 | |
michael@0 | 479 | function typeDesc(type) |
michael@0 | 480 | { |
michael@0 | 481 | if (type.Kind == "CSU") { |
michael@0 | 482 | return type.Name; |
michael@0 | 483 | } else if ('Type' in type) { |
michael@0 | 484 | var inner = typeDesc(type.Type); |
michael@0 | 485 | if (type.Kind == 'Pointer') |
michael@0 | 486 | return inner + '*'; |
michael@0 | 487 | else if (type.Kind == 'Array') |
michael@0 | 488 | return inner + '[]'; |
michael@0 | 489 | else |
michael@0 | 490 | return inner + '?'; |
michael@0 | 491 | } else { |
michael@0 | 492 | return '???'; |
michael@0 | 493 | } |
michael@0 | 494 | } |
michael@0 | 495 | |
michael@0 | 496 | function processBodies(functionName) |
michael@0 | 497 | { |
michael@0 | 498 | if (!("DefineVariable" in functionBodies[0])) |
michael@0 | 499 | return; |
michael@0 | 500 | var suppressed = (mangled(functionName) in suppressedFunctions); |
michael@0 | 501 | for (var variable of functionBodies[0].DefineVariable) { |
michael@0 | 502 | if (variable.Variable.Kind == "Return") |
michael@0 | 503 | continue; |
michael@0 | 504 | var name; |
michael@0 | 505 | if (variable.Variable.Kind == "This") |
michael@0 | 506 | name = "this"; |
michael@0 | 507 | else |
michael@0 | 508 | name = variable.Variable.Name[0]; |
michael@0 | 509 | if (isRootedType(variable.Type)) { |
michael@0 | 510 | if (!variableLiveAcrossGC(suppressed, variable.Variable)) { |
michael@0 | 511 | // The earliest use of the variable should be its constructor. |
michael@0 | 512 | var lineText; |
michael@0 | 513 | for (var body of functionBodies) { |
michael@0 | 514 | if (body.minimumUse) { |
michael@0 | 515 | var text = findLocation(body, body.minimumUse); |
michael@0 | 516 | if (!lineText || locationLine(lineText) > locationLine(text)) |
michael@0 | 517 | lineText = text; |
michael@0 | 518 | } |
michael@0 | 519 | } |
michael@0 | 520 | print("\nFunction '" + functionName + "'" + |
michael@0 | 521 | " has unnecessary root '" + name + "' at " + lineText); |
michael@0 | 522 | } |
michael@0 | 523 | } else if (isUnrootedType(variable.Type)) { |
michael@0 | 524 | var result = variableLiveAcrossGC(suppressed, variable.Variable); |
michael@0 | 525 | if (result) { |
michael@0 | 526 | var lineText = findLocation(result.gcInfo.body, result.gcInfo.ppoint); |
michael@0 | 527 | print("\nFunction '" + functionName + "'" + |
michael@0 | 528 | " has unrooted '" + name + "'" + |
michael@0 | 529 | " of type '" + typeDesc(variable.Type) + "'" + |
michael@0 | 530 | " live across GC call " + result.gcInfo.name + |
michael@0 | 531 | " at " + lineText); |
michael@0 | 532 | printEntryTrace(functionName, result.why); |
michael@0 | 533 | } |
michael@0 | 534 | result = unsafeVariableAddressTaken(suppressed, variable.Variable); |
michael@0 | 535 | if (result) { |
michael@0 | 536 | var lineText = findLocation(result.body, result.ppoint); |
michael@0 | 537 | print("\nFunction '" + functionName + "'" + |
michael@0 | 538 | " takes unsafe address of unrooted '" + name + "'" + |
michael@0 | 539 | " at " + lineText); |
michael@0 | 540 | printEntryTrace(functionName, {body:result.body, ppoint:result.ppoint}); |
michael@0 | 541 | } |
michael@0 | 542 | } |
michael@0 | 543 | } |
michael@0 | 544 | } |
michael@0 | 545 | |
michael@0 | 546 | if (batch == 1) |
michael@0 | 547 | print("Time: " + new Date); |
michael@0 | 548 | |
michael@0 | 549 | var xdb = xdbLibrary(); |
michael@0 | 550 | xdb.open("src_body.xdb"); |
michael@0 | 551 | |
michael@0 | 552 | var minStream = xdb.min_data_stream()|0; |
michael@0 | 553 | var maxStream = xdb.max_data_stream()|0; |
michael@0 | 554 | |
michael@0 | 555 | var N = (maxStream - minStream) + 1; |
michael@0 | 556 | var each = Math.floor(N/numBatches); |
michael@0 | 557 | var start = minStream + each * (batch - 1); |
michael@0 | 558 | var end = Math.min(minStream + each * batch - 1, maxStream); |
michael@0 | 559 | |
michael@0 | 560 | for (var nameIndex = start; nameIndex <= end; nameIndex++) { |
michael@0 | 561 | var name = xdb.read_key(nameIndex); |
michael@0 | 562 | var functionName = name.readString(); |
michael@0 | 563 | var data = xdb.read_entry(name); |
michael@0 | 564 | xdb.free_string(name); |
michael@0 | 565 | var json = data.readString(); |
michael@0 | 566 | xdb.free_string(data); |
michael@0 | 567 | functionBodies = JSON.parse(json); |
michael@0 | 568 | |
michael@0 | 569 | for (var body of functionBodies) |
michael@0 | 570 | body.suppressed = []; |
michael@0 | 571 | for (var body of functionBodies) { |
michael@0 | 572 | for (var [pbody, id] of allRAIIGuardedCallPoints(body, isSuppressConstructor)) |
michael@0 | 573 | pbody.suppressed[id] = true; |
michael@0 | 574 | } |
michael@0 | 575 | processBodies(functionName); |
michael@0 | 576 | } |