| OLD | NEW |
| (Empty) |
| 1 // Decompile a JS file. This will be painful. | |
| 2 include("../utils/dumpast.js"); | |
| 3 include("../utils/astml.js"); | |
| 4 | |
| 5 let visitor = { | |
| 6 _visitArray: function (arr, pre, post, comma) { | |
| 7 if (pre === undefined) pre = '('; | |
| 8 if (post === undefined) post = ')'; | |
| 9 if (comma === undefined) comma = ', '; | |
| 10 output(pre); | |
| 11 for each (let arg in arr) { | |
| 12 arg.visit(this); | |
| 13 output(comma); | |
| 14 } | |
| 15 if (arr.length > 0) | |
| 16 unwrite(comma.length); | |
| 17 output(post); | |
| 18 }, | |
| 19 _visitMaybeBlock: function (body) { | |
| 20 if (body.type == "BlockStatement") { | |
| 21 output(" "); | |
| 22 body.visit(this); | |
| 23 } else { | |
| 24 flush().indent(); | |
| 25 body.visit(this); | |
| 26 unindent(); | |
| 27 } | |
| 28 }, | |
| 29 _visitNeedBlock: function (stmt, noFlush) { | |
| 30 if (stmt.type == "EmptyStatement") { | |
| 31 output("{}") | |
| 32 if (!noFlush) | |
| 33 flush(); | |
| 34 } | |
| 35 else if (stmt.type == "ReturnStatement") { | |
| 36 output("{").flush().indent(); | |
| 37 stmt.visit(this); | |
| 38 unindent().output("}"); | |
| 39 if (!noFlush) | |
| 40 flush(); | |
| 41 } | |
| 42 else | |
| 43 stmt.visit(this); | |
| 44 }, | |
| 45 visitProgram: function (program) {}, | |
| 46 visitFunctionDeclaration: function (func) { | |
| 47 output("function "); | |
| 48 if (func.name) | |
| 49 output(func.name); | |
| 50 this._visitArray(func.arguments, '(', ') '); | |
| 51 this._visitNeedBlock(func.body, true); | |
| 52 return true; | |
| 53 }, | |
| 54 visitParameter: function (p) { | |
| 55 output(p.name); | |
| 56 }, | |
| 57 visitBlockStatement: function (stmt) { | |
| 58 output("{").flush().indent(); | |
| 59 }, | |
| 60 postvisitBlockStatement: function (stmt) { | |
| 61 output("}").unindent().flush(); | |
| 62 }, | |
| 63 visitVarStatement: function (stmt) { | |
| 64 output(stmt.vartype).output(" "); | |
| 65 this._visitArray(stmt.variables, '', ''); | |
| 66 if (!this._noFlush) | |
| 67 output(';').flush(); | |
| 68 this._noFlush = false; | |
| 69 return true; | |
| 70 }, | |
| 71 visitVarDeclaration: function (decl) { | |
| 72 output(decl.name); | |
| 73 if ("initializer" in decl) | |
| 74 output(" = "); | |
| 75 }, | |
| 76 visitLetStatement: function (stmt) { | |
| 77 output("let "); | |
| 78 this._visitArray(stmt.variables, '(', ')'); | |
| 79 if (!this._noFlush) | |
| 80 output(';').flush(); | |
| 81 this._noFlush = false; | |
| 82 return true; | |
| 83 }, | |
| 84 visitExpressionStatement: function (stmt) {}, | |
| 85 postvisitExpressionStatement: function (stmt) { | |
| 86 output(";").flush(); | |
| 87 }, | |
| 88 visitEmptyStatement: function (stmt) { output(";").flush(); }, | |
| 89 visitIfStatement: function (stmt) { | |
| 90 output("if ("); | |
| 91 stmt.cond.visit(this); | |
| 92 output(")"); | |
| 93 this._visitMaybeBlock(stmt.body); | |
| 94 if (stmt.elsebody) { | |
| 95 output(" else"); | |
| 96 this._visitMaybeBlock(stmt.elsebody); | |
| 97 } | |
| 98 return true; | |
| 99 }, | |
| 100 visitDoWhileStatement: function (stmt) { | |
| 101 output("do"); | |
| 102 this._visitMaybeBlock(stmt.body); | |
| 103 output("while ("); | |
| 104 stmt.cond.visit(this); | |
| 105 output(");").flush(); | |
| 106 return true; | |
| 107 }, | |
| 108 visitWhileStatement: function (stmt) { | |
| 109 output("while ("); | |
| 110 stmt.cond.visit(this); | |
| 111 output(")"); | |
| 112 this._visitMaybeBlock(stmt.body); | |
| 113 return true; | |
| 114 }, | |
| 115 visitForStatement: function (stmt) { | |
| 116 output("for ("); | |
| 117 stmt.init.visit(this); | |
| 118 stmt.cond.visit(this); | |
| 119 if (stmt.cond.type != "EmptyStatement") | |
| 120 output("; "); | |
| 121 stmt.inc.visit(this); | |
| 122 output(")"); | |
| 123 this._visitMaybeBlock(stmt.body); | |
| 124 return true; | |
| 125 }, | |
| 126 visitForInStatement: function (stmt) { | |
| 127 output(stmt.itertype).output(" ("); | |
| 128 this._noFlush = true; | |
| 129 stmt.itervar.visit(this); | |
| 130 output(" in "); | |
| 131 stmt.iterrange.visit(this); | |
| 132 output(")"); | |
| 133 this._visitMaybeBlock(stmt.body); | |
| 134 return true; | |
| 135 }, | |
| 136 visitContinueStatement: function (stmt) { | |
| 137 output("continue"); | |
| 138 if ("label" in stmt) | |
| 139 output(" ").output(stmt.label); | |
| 140 output(";").flush(); | |
| 141 }, | |
| 142 visitBreakStatement: function (stmt) { | |
| 143 output("break"); | |
| 144 if ("label" in stmt) | |
| 145 output(" ").output(stmt.label); | |
| 146 output(";").flush(); | |
| 147 }, | |
| 148 visitReturnStatement: function (stmt) { output("return "); }, | |
| 149 postvisitReturnStatement: function (stmt) { output(";").flush(); }, | |
| 150 visitWithStatement: function (stmt) { | |
| 151 output("with ("); | |
| 152 stmt.variable.visit(this); | |
| 153 output(")"); | |
| 154 this._visitMaybeBlock(stmt.body); | |
| 155 return true; | |
| 156 }, | |
| 157 visitLabeledStatement: function (stmt) { output(stmt.label).output(": "); }, | |
| 158 visitSwitchStatement: function (stmt) { | |
| 159 output("switch ("); | |
| 160 stmt.expr.visit(this); | |
| 161 output(") {").flush().indent(); | |
| 162 this._visitArray(stmt.cases, '', '', ''); | |
| 163 output("}").unindent().flush(); | |
| 164 return true; | |
| 165 }, | |
| 166 visitSwitchCase: function (stmt) { | |
| 167 if ("expr" in stmt) { | |
| 168 output("case "); | |
| 169 stmt.expr.visit(this); | |
| 170 output(": "); | |
| 171 } else | |
| 172 output("default: "); | |
| 173 stmt.body.visit(this); | |
| 174 return true; | |
| 175 }, | |
| 176 visitThrowStatement: function (stmt) { output("throw "); }, | |
| 177 postvisitThrowStatement: function (stmt) { output(";").flush(); }, | |
| 178 visitTryStatement: function (stmt) { | |
| 179 output("try "); | |
| 180 this._visitNeedBlock(stmt.body); | |
| 181 this._visitArray(stmt.catchers, '', '', '\n' + indentStr); | |
| 182 if (stmt.fin) { | |
| 183 output("finally "); | |
| 184 this._visitNeedBlock(stmt.fin); | |
| 185 } | |
| 186 return true; | |
| 187 }, | |
| 188 visitCatchStatement: function (stmt) { | |
| 189 output("catch ("); | |
| 190 stmt.variable.visit(this); | |
| 191 if ("cond" in stmt) { | |
| 192 output(" if "); | |
| 193 stmt.cond.visit(this); | |
| 194 } | |
| 195 output(")"); | |
| 196 this._visitNeedBlock(stmt.body); | |
| 197 return true; | |
| 198 }, | |
| 199 visitDebuggerStatement: function (stmt) { output("debugger;").flush(); }, | |
| 200 | |
| 201 visitThisExpression: function (expr) { output("this"); }, | |
| 202 visitMemberExpression: function (expr) { | |
| 203 let needparen = expr.precedence + 1 < expr.container.precedence; | |
| 204 if (needparen) | |
| 205 output("("); | |
| 206 expr.container.visit(this); | |
| 207 if (needparen) | |
| 208 output(")"); | |
| 209 | |
| 210 if ("constmember" in expr && /^[_a-zA-Z]\w*$/.test(expr.constmember)) | |
| 211 output(".").output(expr.constmember); | |
| 212 else { | |
| 213 output("["); | |
| 214 expr.member.visit(this); | |
| 215 output("]"); | |
| 216 } | |
| 217 return true; | |
| 218 }, | |
| 219 visitNewExpression: function (expr) { | |
| 220 let needparen = expr.precedence < expr.constructor.precedence; | |
| 221 output("new "); | |
| 222 if (needparen) | |
| 223 output("("); | |
| 224 expr.constructor.visit(this); | |
| 225 if (needparen) | |
| 226 output(")"); | |
| 227 this._visitArray(expr.arguments); | |
| 228 return true; | |
| 229 }, | |
| 230 visitCallExpression: function (expr) { | |
| 231 let needparen = expr.precedence < expr.func.precedence; | |
| 232 if (needparen) | |
| 233 output("("); | |
| 234 expr.func.visit(this); | |
| 235 if (needparen) | |
| 236 output(")"); | |
| 237 this._visitArray(expr.arguments); | |
| 238 return true; | |
| 239 }, | |
| 240 visitLiteralExpression: function (expr) { | |
| 241 switch (expr.objtype) { | |
| 242 case "string": | |
| 243 output('"').output(sanitize(expr.value, '"')).output('"'); | |
| 244 break; | |
| 245 case "number": | |
| 246 case "boolean": | |
| 247 case "regex": | |
| 248 output(expr.value.toString()); | |
| 249 break; | |
| 250 case "null": | |
| 251 output("null"); | |
| 252 break; | |
| 253 default: | |
| 254 throw "Unknown literal " + expr.objtype; | |
| 255 }; | |
| 256 }, | |
| 257 visitObjectLiteral: function (obj) { | |
| 258 output('{').flush().indent(); | |
| 259 this._visitArray(obj.setters, '', '', ',\n' + indentStr); | |
| 260 flush().output('}').unindent(); | |
| 261 return true; | |
| 262 }, | |
| 263 visitPropertyLiteral: function (prop) { | |
| 264 if ("proptype" in prop) { | |
| 265 if (prop.value.type == "LiteralExpression") { | |
| 266 prop.property.visit(this); | |
| 267 output(" ").output(prop.proptype).output(": "); | |
| 268 prop.value.visit(this); | |
| 269 return true; | |
| 270 } | |
| 271 if (prop.proptype == "getter") | |
| 272 output("get "); | |
| 273 else if (prop.proptype == "setter") | |
| 274 output("set "); | |
| 275 else | |
| 276 throw "Unknown type: " + prop.proptype; | |
| 277 prop.property.visit(this); | |
| 278 if (prop.value.type != "FunctionDeclaration") | |
| 279 throw "Expection function, found: " + prop.value.type; | |
| 280 if (prop.value.name) { | |
| 281 output(" ").output(prop.value.name); | |
| 282 } | |
| 283 this._visitArray(prop.value.arguments, '(', ') '); | |
| 284 this._visitNeedBlock(prop.value.body, true); | |
| 285 return true; | |
| 286 } | |
| 287 prop.property.visit(this); | |
| 288 output(": "); | |
| 289 prop.value.visit(this); | |
| 290 return true; | |
| 291 }, | |
| 292 visitArrayLiteral: function (arr) { | |
| 293 this._visitArray(arr.members, '[', ']', ', '); | |
| 294 return true; | |
| 295 }, | |
| 296 visitArrayComprehensionExpression: function (arrcomp) { | |
| 297 output('['); | |
| 298 let enp = arrcomp.element.precedence > 16; | |
| 299 if (enp) | |
| 300 output("("); | |
| 301 arrcomp.element.visit(this); | |
| 302 if (enp) | |
| 303 output(")"); | |
| 304 output(" ").output(arrcomp.itertype).output("("); | |
| 305 arrcomp.itervar.visit(this); | |
| 306 output(" in "); | |
| 307 arrcomp.iterrange.visit(this); | |
| 308 output(")"); | |
| 309 if ("iterif" in arrcomp) { | |
| 310 output(" if ("); | |
| 311 arrcomp.iterif.visit(this); | |
| 312 output(")"); | |
| 313 } | |
| 314 output("]"); | |
| 315 return true; | |
| 316 }, | |
| 317 visitIdentifierExpression: function (expr) { | |
| 318 output(expr.name); | |
| 319 if ("initializer" in expr) { | |
| 320 output(" = "); | |
| 321 expr.initializer.visit(this); | |
| 322 return true; | |
| 323 } | |
| 324 }, | |
| 325 visitPostfixExpression: function (expr) {}, | |
| 326 postvisitPostfixExpression: function (expr) { | |
| 327 output(expr.operator); | |
| 328 }, | |
| 329 visitUnaryExpression: function (expr) { | |
| 330 if (expr.operator != '()') { | |
| 331 output(expr.operator); | |
| 332 if (expr.operator.length > 1) | |
| 333 output(" "); | |
| 334 } | |
| 335 let np = expr.precedence < expr.operand.precedence; | |
| 336 if (expr.operator == '()' || np) { | |
| 337 output("("); | |
| 338 expr.operand.visit(this); | |
| 339 output(")"); | |
| 340 return true; | |
| 341 } | |
| 342 }, | |
| 343 visitBinaryExpression: function (expr) { | |
| 344 let lhp = expr.precedence < expr.lhs.precedence; | |
| 345 let rhp = expr.precedence < expr.rhs.precedence; | |
| 346 if (lhp) | |
| 347 output("("); | |
| 348 expr.lhs.visit(this); | |
| 349 if (lhp) | |
| 350 output(")"); | |
| 351 output(" ").output(expr.operator).output(" "); | |
| 352 if (rhp) | |
| 353 output("("); | |
| 354 expr.rhs.visit(this); | |
| 355 if (rhp) | |
| 356 output(")"); | |
| 357 return true; | |
| 358 }, | |
| 359 visitConditionalExpression: function (expr) { | |
| 360 let lhp = expr.precedence < expr.cond.precedence; | |
| 361 let mhp = expr.precedence < expr.iftrue.precedence; | |
| 362 let rhp = expr.precedence < expr.iffalse.precedence; | |
| 363 if (lhp) | |
| 364 output("("); | |
| 365 expr.cond.visit(this); | |
| 366 if (lhp) | |
| 367 output(")"); | |
| 368 output(" ? "); | |
| 369 if (mhp) | |
| 370 output("("); | |
| 371 expr.iftrue.visit(this); | |
| 372 if (mhp) | |
| 373 output(")"); | |
| 374 output(" : "); | |
| 375 if (rhp) | |
| 376 output("("); | |
| 377 expr.iffalse.visit(this); | |
| 378 if (rhp) | |
| 379 output(")"); | |
| 380 return true; | |
| 381 }, | |
| 382 visitAssignmentExpression: function (expr) { | |
| 383 let lhp = expr.precedence < expr.lhs.precedence; | |
| 384 let rhp = expr.precedence < expr.rhs.precedence; | |
| 385 if (lhp) | |
| 386 output("("); | |
| 387 expr.lhs.visit(this); | |
| 388 if (lhp) | |
| 389 output(")"); | |
| 390 output(" ").output(expr.operator).output("= "); | |
| 391 if (rhp) | |
| 392 output("("); | |
| 393 expr.rhs.visit(this); | |
| 394 if (rhp) | |
| 395 output(")"); | |
| 396 return true; | |
| 397 }, | |
| 398 }; | |
| 399 | |
| 400 /* Reminder */ | |
| 401 for (let f in structure) { | |
| 402 if (!("visit" + f in visitor)) | |
| 403 throw "Please visit " + f; | |
| 404 } | |
| 405 | |
| 406 function process_js(ast) { | |
| 407 if (!ast) | |
| 408 return; | |
| 409 ast = makeAST(ast); | |
| 410 walkAST(ast, visitor); | |
| 411 } | |
| 412 | |
| 413 /* Output functions */ | |
| 414 let buffer = "", indentStr = ""; | |
| 415 function output(str) { | |
| 416 buffer += str; | |
| 417 return global; | |
| 418 } | |
| 419 function unwrite(numChars) { | |
| 420 buffer = buffer.substring(0, buffer.length - numChars); | |
| 421 return global; | |
| 422 } | |
| 423 function flush() { | |
| 424 _print(buffer); | |
| 425 buffer = indentStr; | |
| 426 return global; | |
| 427 } | |
| 428 function indent() { | |
| 429 indentStr += " "; | |
| 430 buffer = " " + buffer; | |
| 431 return global; | |
| 432 } | |
| 433 function unindent() { | |
| 434 indentStr = indentStr.substring(2); | |
| 435 buffer = buffer.substring(2); | |
| 436 return global; | |
| 437 } | |
| 438 | |
| 439 function sanitize(str, q) { | |
| 440 function replace(x) { | |
| 441 if (x == q) return '\\' + q; | |
| 442 if (x == '\\') return '\\\\'; | |
| 443 if (x == '\b') return '\\b'; | |
| 444 if (x == '\f') return '\\f'; | |
| 445 if (x == '\n') return '\\n'; | |
| 446 if (x == '\r') return '\\r'; | |
| 447 if (x == '\t') return '\\t'; | |
| 448 if (x == '\v') return '\\v'; | |
| 449 let val = x.charCodeAt(0) | |
| 450 if (x < ' ') return '\\x' + (val - val % 16) / 16 + (val % 16); | |
| 451 return x; | |
| 452 } | |
| 453 return [replace(x) for each (x in str)].join(''); | |
| 454 } | |
| OLD | NEW |