dumpCFG.js (7561B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 // const cfg = loadCFG(scriptArgs[0]); 6 // dump_CFG(cfg); 7 8 function loadCFG(filename) { 9 const data = os.file.readFile(filename); 10 return JSON.parse(data); 11 } 12 13 function dump_CFG(cfg) { 14 for (const body of cfg) 15 dump_body(body); 16 } 17 18 function dump_body(body, src, dst) { 19 const {BlockId,Command,DefineVariable,Index,Location,PEdge,PPoint,Version} = body; 20 21 const [mangled, unmangled] = splitFunction(BlockId.Variable.Name[0]); 22 print(`${unmangled} at ${Location[0].CacheString}:${Location[0].Line}`); 23 24 if (src === undefined) { 25 for (const def of DefineVariable) 26 print(str_definition(def)); 27 print(""); 28 } 29 30 for (const edge of PEdge) { 31 if (src === undefined || edge.Index[0] == src) { 32 if (dst == undefined || edge.Index[1] == dst) 33 print(str_edge(edge, body)); 34 } 35 } 36 } 37 38 function str_definition(def) { 39 const {Type, Variable} = def; 40 return `define ${str_Variable(Variable)} : ${str_Type(Type)}`; 41 } 42 43 function badFormat(what, val) { 44 printErr("Bad format of " + what + ": " + JSON.stringify(val, null, 4)); 45 printErr((new Error).stack); 46 } 47 48 function str_Variable(variable) { 49 if (variable.Kind == 'Return') 50 return '<returnval>'; 51 else if (variable.Kind == 'This') 52 return 'this'; 53 54 try { 55 return variable.Name[1]; 56 } catch(e) { 57 badFormat("variable", variable); 58 } 59 } 60 61 function str_Type(type) { 62 try { 63 const {Kind, Type, Name, TypeFunctionArguments} = type; 64 if (Kind == 'Pointer') 65 return str_Type(Type) + ["*", "&", "&&"][type.Reference]; 66 else if (Kind == 'CSU') 67 return Name; 68 else if (Kind == 'Array') 69 return str_Type(Type) + "[]"; 70 else if (Kind == 'Function') 71 return str_Type(Type) + "()"; 72 73 return Kind; 74 } catch(e) { 75 badFormat("type", type); 76 } 77 } 78 79 var OpCodeNames = { 80 'LessEqual': ['<=', '>'], 81 'LessThan': ['<', '>='], 82 'GreaterEqual': ['>=', '<'], 83 'Greater': ['>', '<='], 84 'Plus': '+', 85 'Minus': '-', 86 }; 87 88 function opcode_name(opcode, invert) { 89 if (opcode in OpCodeNames) { 90 const name = OpCodeNames[opcode]; 91 if (invert === undefined) 92 return name; 93 return name[invert ? 1 : 0]; 94 } else { 95 if (invert === undefined) 96 return opcode; 97 return (invert ? '!' : '') + opcode; 98 } 99 } 100 101 function str_value(val, env, options) { 102 const {Kind, Variable, String, Exp} = val; 103 if (Kind == 'Var') 104 return str_Variable(Variable); 105 else if (Kind == 'Drf') { 106 // Suppress the vtable lookup dereference 107 if (Exp[0].Kind == 'Fld' && "FieldInstanceFunction" in Exp[0].Field) 108 return str_value(Exp[0], env); 109 const exp = str_value(Exp[0], env); 110 if (options && options.noderef) 111 return exp; 112 return "*" + exp; 113 } else if (Kind == 'Fld') { 114 const {Exp, Field} = val; 115 const name = Field.Name[0]; 116 if ("FieldInstanceFunction" in Field) { 117 return Field.FieldCSU.Type.Name + "." + name; 118 } 119 const container = str_value(Exp[0]); 120 if (container.startsWith("*")) 121 return container.substring(1) + "->" + name; 122 return container + "." + name; 123 } else if (Kind == 'Empty') { 124 return '<unknown>'; 125 } else if (Kind == 'Binop') { 126 const {OpCode} = val; 127 const op = opcode_name(OpCode); 128 return `${str_value(Exp[0], env)} ${op} ${str_value(Exp[1], env)}`; 129 } else if (Kind == 'Unop') { 130 const exp = str_value(Exp[0], env); 131 const {OpCode} = val; 132 if (OpCode == 'LogicalNot') 133 return `not ${exp}`; 134 return `${OpCode}(${exp})`; 135 } else if (Kind == 'Index') { 136 const index = str_value(Exp[1], env); 137 if (Exp[0].Kind == 'Drf') 138 return `${str_value(Exp[0], env, {noderef:true})}[${index}]`; 139 else 140 return `&${str_value(Exp[0], env)}[${index}]`; 141 } else if (Kind == 'NullTest') { 142 return `nullptr == ${str_value(Exp[0], env)}`; 143 } else if (Kind == "String") { 144 return '"' + String + '"'; 145 } else if (String !== undefined) { 146 return String; 147 } 148 badFormat("value", val); 149 } 150 151 function str_thiscall_Exp(exp) { 152 return exp.Kind == 'Drf' ? str_value(exp.Exp[0]) + "->" : str_value(exp) + "."; 153 } 154 155 function stripcsu(s) { 156 return s.replace("class ", "").replace("struct ", "").replace("union "); 157 } 158 159 function str_call(prefix, edge, env) { 160 const {Exp, Type, PEdgeCallArguments, PEdgeCallInstance} = edge; 161 const {Kind, Type:cType, TypeFunctionArguments, TypeFunctionCSU} = Type; 162 163 if (Kind == 'Function') { 164 const params = PEdgeCallArguments ? PEdgeCallArguments.Exp : []; 165 const strParams = params.map(str_value); 166 167 let func; 168 let comment = ""; 169 let assign_exp; 170 if (PEdgeCallInstance) { 171 const csu = TypeFunctionCSU.Type.Name; 172 const method = str_value(Exp[0], env); 173 174 // Heuristic to only display the csu for constructors 175 if (csu.includes(method)) { 176 func = stripcsu(csu) + "::" + method; 177 } else { 178 func = method; 179 comment = "# " + csu + "::" + method + "\n"; 180 } 181 182 const {Exp: thisExp} = PEdgeCallInstance; 183 func = str_thiscall_Exp(thisExp) + func; 184 } else { 185 func = str_value(Exp[0]); 186 } 187 assign_exp = Exp[1]; 188 189 let assign = ""; 190 if (assign_exp) { 191 assign = str_value(assign_exp) + " := "; 192 } 193 return `${comment}${prefix} Call ${assign}${func}(${strParams.join(", ")})`; 194 } 195 196 print(JSON.stringify(edge, null, 4)); 197 throw new Error("unhandled format error"); 198 } 199 200 function str_assign(prefix, edge) { 201 const {Exp} = edge; 202 const [lhs, rhs] = Exp; 203 return `${prefix} Assign ${str_value(lhs)} := ${str_value(rhs)}`; 204 } 205 206 function str_loop(prefix, edge) { 207 const {BlockId: {Loop}} = edge; 208 return `${prefix} Loop ${Loop}`; 209 } 210 211 function str_assume(prefix, edge) { 212 const {Exp, PEdgeAssumeNonZero} = edge; 213 const cmp = PEdgeAssumeNonZero ? "" : "!"; 214 215 const {Exp: aExp, Kind, OpCode} = Exp[0]; 216 if (Kind == 'Binop') { 217 const [lhs, rhs] = aExp; 218 const op = opcode_name(OpCode, !PEdgeAssumeNonZero); 219 return `${prefix} Assume ${str_value(lhs)} ${op} ${str_value(rhs)}`; 220 } else if (Kind == 'Unop') { 221 return `${prefix} Assume ${cmp}${OpCode} ${str_value(aExp[0])}`; 222 } else if (Kind == 'NullTest') { 223 return `${prefix} Assume nullptr ${cmp}== ${str_value(aExp[0])}`; 224 } else if (Kind == 'Drf') { 225 return `${prefix} Assume ${cmp}${str_value(Exp[0])}`; 226 } 227 228 print(JSON.stringify(edge, null, 4)); 229 throw new Error("unhandled format error"); 230 } 231 232 function str_edge(edge, env) { 233 const {Index, Kind} = edge; 234 const [src, dst] = Index; 235 const prefix = `[${src},${dst}]`; 236 237 if (Kind == "Call") 238 return str_call(prefix, edge, env); 239 if (Kind == 'Assign') 240 return str_assign(prefix, edge); 241 if (Kind == 'Assume') 242 return str_assume(prefix, edge); 243 if (Kind == 'Loop') 244 return str_loop(prefix, edge); 245 246 print(JSON.stringify(edge, null, 4)); 247 throw "unhandled edge type"; 248 } 249 250 function str(unknown) { 251 if ("Name" in unknown) { 252 return str_Variable(unknown); 253 } else if ("Index" in unknown) { 254 // Note: Variable also has .Index, with a different meaning. 255 return str_edge(unknown); 256 } else if ("Type" in unknown) { 257 if ("Variable" in unknown) { 258 return str_definition(unknown); 259 } else { 260 return str_Type(unknown); 261 } 262 } else if ("Kind" in unknown) { 263 if ("BlockId" in unknown) 264 return str_Variable(unknown); 265 return str_value(unknown); 266 } 267 return "unknown"; 268 } 269 270 function jdump(x) { 271 print(JSON.stringify(x, null, 4)); 272 quit(0); 273 }