/* 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) + "*"; else if (Kind == 'CSU') return Name; else if (Kind == 'Array') 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 "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 "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 ("Kind" in unknown) { if ("BlockId" in unknown) return str_Variable(unknown); return str_value(unknown); } else if ("Type" in unknown) { return str_Type(unknown); } return "unknown"; } function jdump(x) { print(JSON.stringify(x, null, 4)); quit(0); }