/* 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 '';
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 '';
}
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);
}