Source code

Revision control

Copy as Markdown

Other Tools

/* 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/. */
// const cfg = loadCFG(scriptArgs[0]);
// dump_CFG(cfg);
function loadCFG(filename) {
const data = os.file.readFile(filename);
return JSON.parse(data);
}
function dump_CFG(cfg) {
for (const body of cfg)
dump_body(body);
}
function dump_body(body, src, dst) {
const {BlockId,Command,DefineVariable,Index,Location,PEdge,PPoint,Version} = body;
const [mangled, unmangled] = splitFunction(BlockId.Variable.Name[0]);
print(`${unmangled} at ${Location[0].CacheString}:${Location[0].Line}`);
if (src === undefined) {
for (const def of DefineVariable)
print(str_definition(def));
print("");
}
for (const edge of PEdge) {
if (src === undefined || edge.Index[0] == src) {
if (dst == undefined || edge.Index[1] == dst)
print(str_edge(edge, body));
}
}
}
function str_definition(def) {
const {Type, Variable} = def;
return `define ${str_Variable(Variable)} : ${str_Type(Type)}`;
}
function badFormat(what, val) {
printErr("Bad format of " + what + ": " + JSON.stringify(val, null, 4));
printErr((new Error).stack);
}
function str_Variable(variable) {
if (variable.Kind == 'Return')
return '<returnval>';
else if (variable.Kind == 'This')
return 'this';
try {
return variable.Name[1];
} catch(e) {
badFormat("variable", variable);
}
}
function str_Type(type) {
try {
const {Kind, Type, Name, TypeFunctionArguments} = type;
if (Kind == 'Pointer')
return str_Type(Type) + ["*", "&", "&&"][type.Reference];
else if (Kind == 'CSU')
return Name;
else if (Kind == 'Array')
return str_Type(Type) + "[]";
else if (Kind == 'Function')
return str_Type(Type) + "()";
return Kind;
} catch(e) {
badFormat("type", type);
}
}
var OpCodeNames = {
'LessEqual': ['<=', '>'],
'LessThan': ['<', '>='],
'GreaterEqual': ['>=', '<'],
'Greater': ['>', '<='],
'Plus': '+',
'Minus': '-',
};
function opcode_name(opcode, invert) {
if (opcode in OpCodeNames) {
const name = OpCodeNames[opcode];
if (invert === undefined)
return name;
return name[invert ? 1 : 0];
} else {
if (invert === undefined)
return opcode;
return (invert ? '!' : '') + opcode;
}
}
function str_value(val, env, options) {
const {Kind, Variable, String, Exp} = val;
if (Kind == 'Var')
return str_Variable(Variable);
else if (Kind == 'Drf') {
// Suppress the vtable lookup dereference
if (Exp[0].Kind == 'Fld' && "FieldInstanceFunction" in Exp[0].Field)
return str_value(Exp[0], env);
const exp = str_value(Exp[0], env);
if (options && options.noderef)
return exp;
return "*" + exp;
} else if (Kind == 'Fld') {
const {Exp, Field} = val;
const name = Field.Name[0];
if ("FieldInstanceFunction" in Field) {
return Field.FieldCSU.Type.Name + "." + name;
}
const container = str_value(Exp[0]);
if (container.startsWith("*"))
return container.substring(1) + "->" + name;
return container + "." + name;
} else if (Kind == 'Empty') {
return '<unknown>';
} else if (Kind == 'Binop') {
const {OpCode} = val;
const op = opcode_name(OpCode);
return `${str_value(Exp[0], env)} ${op} ${str_value(Exp[1], env)}`;
} else if (Kind == 'Unop') {
const exp = str_value(Exp[0], env);
const {OpCode} = val;
if (OpCode == 'LogicalNot')
return `not ${exp}`;
return `${OpCode}(${exp})`;
} else if (Kind == 'Index') {
const index = str_value(Exp[1], env);
if (Exp[0].Kind == 'Drf')
return `${str_value(Exp[0], env, {noderef:true})}[${index}]`;
else
return `&${str_value(Exp[0], env)}[${index}]`;
} else if (Kind == 'NullTest') {
return `nullptr == ${str_value(Exp[0], env)}`;
} else if (Kind == "String") {
return '"' + String + '"';
} else if (String !== undefined) {
return String;
}
badFormat("value", val);
}
function str_thiscall_Exp(exp) {
return exp.Kind == 'Drf' ? str_value(exp.Exp[0]) + "->" : str_value(exp) + ".";
}
function stripcsu(s) {
return s.replace("class ", "").replace("struct ", "").replace("union ");
}
function str_call(prefix, edge, env) {
const {Exp, Type, PEdgeCallArguments, PEdgeCallInstance} = edge;
const {Kind, Type:cType, TypeFunctionArguments, TypeFunctionCSU} = Type;
if (Kind == 'Function') {
const params = PEdgeCallArguments ? PEdgeCallArguments.Exp : [];
const strParams = params.map(str_value);
let func;
let comment = "";
let assign_exp;
if (PEdgeCallInstance) {
const csu = TypeFunctionCSU.Type.Name;
const method = str_value(Exp[0], env);
// Heuristic to only display the csu for constructors
if (csu.includes(method)) {
func = stripcsu(csu) + "::" + method;
} else {
func = method;
comment = "# " + csu + "::" + method + "\n";
}
const {Exp: thisExp} = PEdgeCallInstance;
func = str_thiscall_Exp(thisExp) + func;
} else {
func = str_value(Exp[0]);
}
assign_exp = Exp[1];
let assign = "";
if (assign_exp) {
assign = str_value(assign_exp) + " := ";
}
return `${comment}${prefix} Call ${assign}${func}(${strParams.join(", ")})`;
}
print(JSON.stringify(edge, null, 4));
throw new Error("unhandled format error");
}
function str_assign(prefix, edge) {
const {Exp} = edge;
const [lhs, rhs] = Exp;
return `${prefix} Assign ${str_value(lhs)} := ${str_value(rhs)}`;
}
function str_loop(prefix, edge) {
const {BlockId: {Loop}} = edge;
return `${prefix} Loop ${Loop}`;
}
function str_assume(prefix, edge) {
const {Exp, PEdgeAssumeNonZero} = edge;
const cmp = PEdgeAssumeNonZero ? "" : "!";
const {Exp: aExp, Kind, OpCode} = Exp[0];
if (Kind == 'Binop') {
const [lhs, rhs] = aExp;
const op = opcode_name(OpCode, !PEdgeAssumeNonZero);
return `${prefix} Assume ${str_value(lhs)} ${op} ${str_value(rhs)}`;
} else if (Kind == 'Unop') {
return `${prefix} Assume ${cmp}${OpCode} ${str_value(aExp[0])}`;
} else if (Kind == 'NullTest') {
return `${prefix} Assume nullptr ${cmp}== ${str_value(aExp[0])}`;
} else if (Kind == 'Drf') {
return `${prefix} Assume ${cmp}${str_value(Exp[0])}`;
}
print(JSON.stringify(edge, null, 4));
throw new Error("unhandled format error");
}
function str_edge(edge, env) {
const {Index, Kind} = edge;
const [src, dst] = Index;
const prefix = `[${src},${dst}]`;
if (Kind == "Call")
return str_call(prefix, edge, env);
if (Kind == 'Assign')
return str_assign(prefix, edge);
if (Kind == 'Assume')
return str_assume(prefix, edge);
if (Kind == 'Loop')
return str_loop(prefix, edge);
print(JSON.stringify(edge, null, 4));
throw "unhandled edge type";
}
function str(unknown) {
if ("Name" in unknown) {
return str_Variable(unknown);
} else if ("Index" in unknown) {
// Note: Variable also has .Index, with a different meaning.
return str_edge(unknown);
} else if ("Type" in unknown) {
if ("Variable" in unknown) {
return str_definition(unknown);
} else {
return str_Type(unknown);
}
} else if ("Kind" in unknown) {
if ("BlockId" in unknown)
return str_Variable(unknown);
return str_value(unknown);
}
return "unknown";
}
function jdump(x) {
print(JSON.stringify(x, null, 4));
quit(0);
}