#!/usr/bin/python3 # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this file, # You can obtain one at http://mozilla.org/MPL/2.0/.
import argparse import json import pathlib import re from html import escape
parser = argparse.ArgumentParser(
description="Convert the JSON output of the hazard analysis into various text files describing the results.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument("--verbose", type=bool, default=False, help="verbose output")
inputs = parser.add_argument_group("Input")
inputs.add_argument( "rootingHazards",
nargs="?",
default="rootingHazards.json",
help="JSON input file describing the output of the hazard analysis",
)
outputs = parser.add_argument_group("Output")
outputs.add_argument( "gcFunctions",
nargs="?",
default="gcFunctions.txt",
help="file containing a list of functions that can GC",
)
outputs.add_argument( "hazards",
nargs="?",
default="hazards.txt",
help="file containing the rooting hazards found",
)
outputs.add_argument( "extra",
nargs="?",
default="unnecessary.txt",
help="file containing unnecessary roots",
)
outputs.add_argument( "refs",
nargs="?",
default="refs.txt",
help="file containing a list of unsafe references to unrooted values",
)
outputs.add_argument( "html",
nargs="?",
default="hazards.html",
help="HTML-formatted file with the hazards found",
)
def parse_function(readable): """Do a very simple parse of a full function declaration, splitting it into
a prefix, the function name, and a suffix."""
s = readable
# Remove all template parameter lists, recursively. whileTrue:
s, count = re.subn(r"<[^<]*>", "", s) if count == 0: break
if m := re.search(r"((?:~?\w+|operator[^(]+))\(", s): # Grab the function name just before the first open paren.
funcName = m.group(1)
# Expand the function name to include any namespaces (and class names, # as long as they aren't templatized).
name_start = readable.index(funcName)
name_end = name_start + len(funcName)
s = readable[: name_end + 1]
m = re.search(r"((?:[\w:]+|operator[^(]+))\(", s) if m:
funcName = m.group(1) elif m := re.search(r"\:\d+$", s): # Field call, like "IDL_foo:0"
funcName = s elif m := re.search(r"[^:]:(\w+)$", s): # <filename>:<cfunc>
funcName = m.group(1) elif m := re.match(r"\w+$", s): # <cfunc>
funcName = s else: # There is a wide variety of messy other formats for function names. We # are unlikely to run across them in this code, but fall back to the # full string.
print(f"FAILED function_parse on {s}", file=sys.stderr)
funcName = s
try: with open(args.rootingHazards) as rootingHazards, open(
args.hazards, "w"
) as hazards, open(args.extra, "w") as extra, open(args.refs, "w") as refs, open(
args.html, "w"
) as html:
current_gcFunction = None
when = min((r for r in results if r["record"] == "time"), key=lambda r: r["t"])[ "iso"
]
line = f"Time: {when}"
print(line, file=hazards)
print(line, file=extra)
print(line, file=refs)
checkboxCounter = 0
hazard_results = []
missing = []
seen_time = False for result in results: if result["record"] == "unrooted":
hazard_results.append(result)
gccall_mangled, _ = splitfunc(result["gccall"])
hazardousGCFunctions.add(gccall_mangled) ifnot result.get("expected"):
num_hazards += 1
elif result["record"] == "unnecessary":
print( "\nFunction '{mangled}' has unnecessary root '{variable}' of type {type} at {loc}".format(
**result
),
file=extra,
)
elif result["record"] == "missing":
print( "\nFunction '{functionName}' expected hazard(s) but none were found at {loc}".format(
**result
),
file=hazards,
)
missing.append(result)
num_missing += 1
readable2mangled = {} with open(args.gcFunctions) as gcFunctions:
gcExplanations = {} # gcFunction => stack showing why it can GC
current_func = None
explanation = [] for line in gcFunctions: if m := re.match(r"^GC Function: (.*)", line): if current_func:
gcExplanations[splitfunc(current_func)[0]] = explanation
functionName = m.group(1)
mangled, readable = splitfunc(functionName) if mangled notin hazardousGCFunctions:
current_func = None continue
current_func = functionName if readable != mangled:
readable2mangled[readable] = mangled # TODO: store the mangled name here, and change # gcFunctions.txt -> gcFunctions.json and key off of the mangled name.
explanation = [readable] elif current_func:
explanation.append(line.strip()) if current_func:
gcExplanations[splitfunc(current_func)[0]] = explanation
for result in missing:
print( "<li>MISSING expected hazard in <a href='{loc_url}'><tt>{call_short}</tt></a> at {loc}".format(
loc_url=sourcelink(range=result["range"], loc=result["loc"]),
call_short=brief_function(result["readable"]),
loc=result["loc"],
),
file=html,
)
# Put expected results last, so they can be hidden by default.
expected_banner_shown = False for result in sorted(hazard_results, key=lambda r: r.get("expected")):
(result["gccall_mangled"], result["gccall_readable"]) = splitfunc(
result["gccall"]
) # Attempt to extract out the function name. Won't handle `Foo<int, Bar<int>>::Foo()`. if m := re.search(r"((?:\w|:|<[^>]*?>)+)\(", result["gccall_readable"]):
result["gccall_short"] = m.group(1) + "()" else:
result["gccall_short"] = result["gccall_readable"] if result.get("expected"):
print("\nThis is expected, but ", end="", file=hazards) else:
print("\nFunction ", end="", file=hazards)
print( "'{readable}' has unrooted '{variable}'" " of type '{type}' live across GC call '{gccall_readable}' at {loc}".format(
**result
),
file=hazards,
) for edge in result["trace"]:
print(" {lineText}: {edgeText}".format(**edge), file=hazards)
explanation = gcExplanations.get(result["gccall_mangled"])
explanation = explanation or gcExplanations.get(
readable2mangled.get(
result["gccall_readable"], result["gccall_readable"]
),
[],
) if explanation:
print("GC Function: " + explanation[0], file=hazards) for func in explanation[1:]:
print(" " + func, file=hazards)
print(file=hazards)
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.