1/*********************************************************************** 2 3 A JavaScript tokenizer / parser / beautifier / compressor. 4 https://github.com/mishoo/UglifyJS2 5 6 -------------------------------- (C) --------------------------------- 7 8 Author: Mihai Bazon 9 <mihai.bazon@gmail.com> 10 http://mihai.bazon.net/blog 11 12 Distributed under the BSD license: 13 14 Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> 15 16 Redistribution and use in source and binary forms, with or without 17 modification, are permitted provided that the following conditions 18 are met: 19 20 * Redistributions of source code must retain the above 21 copyright notice, this list of conditions and the following 22 disclaimer. 23 24 * Redistributions in binary form must reproduce the above 25 copyright notice, this list of conditions and the following 26 disclaimer in the documentation and/or other materials 27 provided with the distribution. 28 29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY 30 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 33 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 34 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 35 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 36 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 38 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 39 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 SUCH DAMAGE. 41 42 ***********************************************************************/ 43 44"use strict"; 45 46(function(){ 47 48 var normalize_directives = function(body) { 49 var in_directive = true; 50 51 for (var i = 0; i < body.length; i++) { 52 if (in_directive && body[i] instanceof AST_Statement && body[i].body instanceof AST_String) { 53 body[i] = new AST_Directive({ 54 start: body[i].start, 55 end: body[i].end, 56 value: body[i].body.value 57 }); 58 } else if (in_directive && !(body[i] instanceof AST_Statement && body[i].body instanceof AST_String)) { 59 in_directive = false; 60 } 61 } 62 63 return body; 64 }; 65 66 var MOZ_TO_ME = { 67 Program: function(M) { 68 return new AST_Toplevel({ 69 start: my_start_token(M), 70 end: my_end_token(M), 71 body: normalize_directives(M.body.map(from_moz)) 72 }); 73 }, 74 FunctionDeclaration: function(M) { 75 return new AST_Defun({ 76 start: my_start_token(M), 77 end: my_end_token(M), 78 name: from_moz(M.id), 79 argnames: M.params.map(from_moz), 80 body: normalize_directives(from_moz(M.body).body) 81 }); 82 }, 83 FunctionExpression: function(M) { 84 return new AST_Function({ 85 start: my_start_token(M), 86 end: my_end_token(M), 87 name: from_moz(M.id), 88 argnames: M.params.map(from_moz), 89 body: normalize_directives(from_moz(M.body).body) 90 }); 91 }, 92 ExpressionStatement: function(M) { 93 return new AST_SimpleStatement({ 94 start: my_start_token(M), 95 end: my_end_token(M), 96 body: from_moz(M.expression) 97 }); 98 }, 99 TryStatement: function(M) { 100 var handlers = M.handlers || [M.handler]; 101 if (handlers.length > 1 || M.guardedHandlers && M.guardedHandlers.length) { 102 throw new Error("Multiple catch clauses are not supported."); 103 } 104 return new AST_Try({ 105 start : my_start_token(M), 106 end : my_end_token(M), 107 body : from_moz(M.block).body, 108 bcatch : from_moz(handlers[0]), 109 bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null 110 }); 111 }, 112 Property: function(M) { 113 var key = M.key; 114 var args = { 115 start : my_start_token(key), 116 end : my_end_token(M.value), 117 key : key.type == "Identifier" ? key.name : key.value, 118 value : from_moz(M.value) 119 }; 120 if (M.kind == "init") return new AST_ObjectKeyVal(args); 121 args.key = new AST_SymbolAccessor({ 122 name: args.key 123 }); 124 args.value = new AST_Accessor(args.value); 125 if (M.kind == "get") return new AST_ObjectGetter(args); 126 if (M.kind == "set") return new AST_ObjectSetter(args); 127 }, 128 ArrayExpression: function(M) { 129 return new AST_Array({ 130 start : my_start_token(M), 131 end : my_end_token(M), 132 elements : M.elements.map(function(elem){ 133 return elem === null ? new AST_Hole() : from_moz(elem); 134 }) 135 }); 136 }, 137 ObjectExpression: function(M) { 138 return new AST_Object({ 139 start : my_start_token(M), 140 end : my_end_token(M), 141 properties : M.properties.map(function(prop){ 142 prop.type = "Property"; 143 return from_moz(prop) 144 }) 145 }); 146 }, 147 SequenceExpression: function(M) { 148 return AST_Seq.from_array(M.expressions.map(from_moz)); 149 }, 150 MemberExpression: function(M) { 151 return new (M.computed ? AST_Sub : AST_Dot)({ 152 start : my_start_token(M), 153 end : my_end_token(M), 154 property : M.computed ? from_moz(M.property) : M.property.name, 155 expression : from_moz(M.object) 156 }); 157 }, 158 SwitchCase: function(M) { 159 return new (M.test ? AST_Case : AST_Default)({ 160 start : my_start_token(M), 161 end : my_end_token(M), 162 expression : from_moz(M.test), 163 body : M.consequent.map(from_moz) 164 }); 165 }, 166 VariableDeclaration: function(M) { 167 return new (M.kind === "const" ? AST_Const : AST_Var)({ 168 start : my_start_token(M), 169 end : my_end_token(M), 170 definitions : M.declarations.map(from_moz) 171 }); 172 }, 173 Literal: function(M) { 174 var val = M.value, args = { 175 start : my_start_token(M), 176 end : my_end_token(M) 177 }; 178 if (val === null) return new AST_Null(args); 179 switch (typeof val) { 180 case "string": 181 args.value = val; 182 return new AST_String(args); 183 case "number": 184 args.value = val; 185 return new AST_Number(args); 186 case "boolean": 187 return new (val ? AST_True : AST_False)(args); 188 default: 189 var rx = M.regex; 190 if (rx && rx.pattern) { 191 // RegExpLiteral as per ESTree AST spec 192 args.value = new RegExp(rx.pattern, rx.flags).toString(); 193 } else { 194 // support legacy RegExp 195 args.value = M.regex && M.raw ? M.raw : val; 196 } 197 return new AST_RegExp(args); 198 } 199 }, 200 Identifier: function(M) { 201 var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2]; 202 return new ( p.type == "LabeledStatement" ? AST_Label 203 : p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar) 204 : p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg) 205 : p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg) 206 : p.type == "CatchClause" ? AST_SymbolCatch 207 : p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef 208 : AST_SymbolRef)({ 209 start : my_start_token(M), 210 end : my_end_token(M), 211 name : M.name 212 }); 213 } 214 }; 215 216 MOZ_TO_ME.UpdateExpression = 217 MOZ_TO_ME.UnaryExpression = function To_Moz_Unary(M) { 218 var prefix = "prefix" in M ? M.prefix 219 : M.type == "UnaryExpression" ? true : false; 220 return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({ 221 start : my_start_token(M), 222 end : my_end_token(M), 223 operator : M.operator, 224 expression : from_moz(M.argument) 225 }); 226 }; 227 228 map("EmptyStatement", AST_EmptyStatement); 229 map("BlockStatement", AST_BlockStatement, "body@body"); 230 map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative"); 231 map("LabeledStatement", AST_LabeledStatement, "label>label, body>body"); 232 map("BreakStatement", AST_Break, "label>label"); 233 map("ContinueStatement", AST_Continue, "label>label"); 234 map("WithStatement", AST_With, "object>expression, body>body"); 235 map("SwitchStatement", AST_Switch, "discriminant>expression, cases@body"); 236 map("ReturnStatement", AST_Return, "argument>value"); 237 map("ThrowStatement", AST_Throw, "argument>value"); 238 map("WhileStatement", AST_While, "test>condition, body>body"); 239 map("DoWhileStatement", AST_Do, "test>condition, body>body"); 240 map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body"); 241 map("ForInStatement", AST_ForIn, "left>init, right>object, body>body"); 242 map("DebuggerStatement", AST_Debugger); 243 map("VariableDeclarator", AST_VarDef, "id>name, init>value"); 244 map("CatchClause", AST_Catch, "param>argname, body%body"); 245 246 map("ThisExpression", AST_This); 247 map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right"); 248 map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right"); 249 map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right"); 250 map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative"); 251 map("NewExpression", AST_New, "callee>expression, arguments@args"); 252 map("CallExpression", AST_Call, "callee>expression, arguments@args"); 253 254 def_to_moz(AST_Toplevel, function To_Moz_Program(M) { 255 return to_moz_scope("Program", M); 256 }); 257 258 def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) { 259 return { 260 type: "FunctionDeclaration", 261 id: to_moz(M.name), 262 params: M.argnames.map(to_moz), 263 body: to_moz_scope("BlockStatement", M) 264 } 265 }); 266 267 def_to_moz(AST_Function, function To_Moz_FunctionExpression(M) { 268 return { 269 type: "FunctionExpression", 270 id: to_moz(M.name), 271 params: M.argnames.map(to_moz), 272 body: to_moz_scope("BlockStatement", M) 273 } 274 }); 275 276 def_to_moz(AST_Directive, function To_Moz_Directive(M) { 277 return { 278 type: "ExpressionStatement", 279 expression: { 280 type: "Literal", 281 value: M.value 282 } 283 }; 284 }); 285 286 def_to_moz(AST_SimpleStatement, function To_Moz_ExpressionStatement(M) { 287 return { 288 type: "ExpressionStatement", 289 expression: to_moz(M.body) 290 }; 291 }); 292 293 def_to_moz(AST_SwitchBranch, function To_Moz_SwitchCase(M) { 294 return { 295 type: "SwitchCase", 296 test: to_moz(M.expression), 297 consequent: M.body.map(to_moz) 298 }; 299 }); 300 301 def_to_moz(AST_Try, function To_Moz_TryStatement(M) { 302 return { 303 type: "TryStatement", 304 block: to_moz_block(M), 305 handler: to_moz(M.bcatch), 306 guardedHandlers: [], 307 finalizer: to_moz(M.bfinally) 308 }; 309 }); 310 311 def_to_moz(AST_Catch, function To_Moz_CatchClause(M) { 312 return { 313 type: "CatchClause", 314 param: to_moz(M.argname), 315 guard: null, 316 body: to_moz_block(M) 317 }; 318 }); 319 320 def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) { 321 return { 322 type: "VariableDeclaration", 323 kind: M instanceof AST_Const ? "const" : "var", 324 declarations: M.definitions.map(to_moz) 325 }; 326 }); 327 328 def_to_moz(AST_Seq, function To_Moz_SequenceExpression(M) { 329 return { 330 type: "SequenceExpression", 331 expressions: M.to_array().map(to_moz) 332 }; 333 }); 334 335 def_to_moz(AST_PropAccess, function To_Moz_MemberExpression(M) { 336 var isComputed = M instanceof AST_Sub; 337 return { 338 type: "MemberExpression", 339 object: to_moz(M.expression), 340 computed: isComputed, 341 property: isComputed ? to_moz(M.property) : {type: "Identifier", name: M.property} 342 }; 343 }); 344 345 def_to_moz(AST_Unary, function To_Moz_Unary(M) { 346 return { 347 type: M.operator == "++" || M.operator == "--" ? "UpdateExpression" : "UnaryExpression", 348 operator: M.operator, 349 prefix: M instanceof AST_UnaryPrefix, 350 argument: to_moz(M.expression) 351 }; 352 }); 353 354 def_to_moz(AST_Binary, function To_Moz_BinaryExpression(M) { 355 return { 356 type: M.operator == "&&" || M.operator == "||" ? "LogicalExpression" : "BinaryExpression", 357 left: to_moz(M.left), 358 operator: M.operator, 359 right: to_moz(M.right) 360 }; 361 }); 362 363 def_to_moz(AST_Array, function To_Moz_ArrayExpression(M) { 364 return { 365 type: "ArrayExpression", 366 elements: M.elements.map(to_moz) 367 }; 368 }); 369 370 def_to_moz(AST_Object, function To_Moz_ObjectExpression(M) { 371 return { 372 type: "ObjectExpression", 373 properties: M.properties.map(to_moz) 374 }; 375 }); 376 377 def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) { 378 var key = { 379 type: "Literal", 380 value: M.key instanceof AST_SymbolAccessor ? M.key.name : M.key 381 }; 382 var kind; 383 if (M instanceof AST_ObjectKeyVal) { 384 kind = "init"; 385 } else 386 if (M instanceof AST_ObjectGetter) { 387 kind = "get"; 388 } else 389 if (M instanceof AST_ObjectSetter) { 390 kind = "set"; 391 } 392 return { 393 type: "Property", 394 kind: kind, 395 key: key, 396 value: to_moz(M.value) 397 }; 398 }); 399 400 def_to_moz(AST_Symbol, function To_Moz_Identifier(M) { 401 var def = M.definition(); 402 return { 403 type: "Identifier", 404 name: def ? def.mangled_name || def.name : M.name 405 }; 406 }); 407 408 def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) { 409 var value = M.value; 410 return { 411 type: "Literal", 412 value: value, 413 raw: value.toString(), 414 regex: { 415 pattern: value.source, 416 flags: value.toString().match(/[gimuy]*$/)[0] 417 } 418 }; 419 }); 420 421 def_to_moz(AST_Constant, function To_Moz_Literal(M) { 422 var value = M.value; 423 if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) { 424 return { 425 type: "UnaryExpression", 426 operator: "-", 427 prefix: true, 428 argument: { 429 type: "Literal", 430 value: -value, 431 raw: M.start.raw 432 } 433 }; 434 } 435 return { 436 type: "Literal", 437 value: value, 438 raw: M.start.raw 439 }; 440 }); 441 442 def_to_moz(AST_Atom, function To_Moz_Atom(M) { 443 return { 444 type: "Identifier", 445 name: String(M.value) 446 }; 447 }); 448 449 AST_Boolean.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast); 450 AST_Null.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast); 451 AST_Hole.DEFMETHOD("to_mozilla_ast", function To_Moz_ArrayHole() { return null }); 452 453 AST_Block.DEFMETHOD("to_mozilla_ast", AST_BlockStatement.prototype.to_mozilla_ast); 454 AST_Lambda.DEFMETHOD("to_mozilla_ast", AST_Function.prototype.to_mozilla_ast); 455 456 /* -----[ tools ]----- */ 457 458 function raw_token(moznode) { 459 if (moznode.type == "Literal") { 460 return moznode.raw != null ? moznode.raw : moznode.value + ""; 461 } 462 } 463 464 function my_start_token(moznode) { 465 var loc = moznode.loc, start = loc && loc.start; 466 var range = moznode.range; 467 return new AST_Token({ 468 file : loc && loc.source, 469 line : start && start.line, 470 col : start && start.column, 471 pos : range ? range[0] : moznode.start, 472 endline : start && start.line, 473 endcol : start && start.column, 474 endpos : range ? range[0] : moznode.start, 475 raw : raw_token(moznode), 476 }); 477 }; 478 479 function my_end_token(moznode) { 480 var loc = moznode.loc, end = loc && loc.end; 481 var range = moznode.range; 482 return new AST_Token({ 483 file : loc && loc.source, 484 line : end && end.line, 485 col : end && end.column, 486 pos : range ? range[1] : moznode.end, 487 endline : end && end.line, 488 endcol : end && end.column, 489 endpos : range ? range[1] : moznode.end, 490 raw : raw_token(moznode), 491 }); 492 }; 493 494 function map(moztype, mytype, propmap) { 495 var moz_to_me = "function From_Moz_" + moztype + "(M){\n"; 496 moz_to_me += "return new U2." + mytype.name + "({\n" + 497 "start: my_start_token(M),\n" + 498 "end: my_end_token(M)"; 499 500 var me_to_moz = "function To_Moz_" + moztype + "(M){\n"; 501 me_to_moz += "return {\n" + 502 "type: " + JSON.stringify(moztype); 503 504 if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){ 505 var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop); 506 if (!m) throw new Error("Can't understand property map: " + prop); 507 var moz = m[1], how = m[2], my = m[3]; 508 moz_to_me += ",\n" + my + ": "; 509 me_to_moz += ",\n" + moz + ": "; 510 switch (how) { 511 case "@": 512 moz_to_me += "M." + moz + ".map(from_moz)"; 513 me_to_moz += "M." + my + ".map(to_moz)"; 514 break; 515 case ">": 516 moz_to_me += "from_moz(M." + moz + ")"; 517 me_to_moz += "to_moz(M." + my + ")"; 518 break; 519 case "=": 520 moz_to_me += "M." + moz; 521 me_to_moz += "M." + my; 522 break; 523 case "%": 524 moz_to_me += "from_moz(M." + moz + ").body"; 525 me_to_moz += "to_moz_block(M)"; 526 break; 527 default: 528 throw new Error("Can't understand operator in propmap: " + prop); 529 } 530 }); 531 532 moz_to_me += "\n})\n}"; 533 me_to_moz += "\n}\n}"; 534 535 //moz_to_me = parse(moz_to_me).print_to_string({ beautify: true }); 536 //me_to_moz = parse(me_to_moz).print_to_string({ beautify: true }); 537 //console.log(moz_to_me); 538 539 moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")( 540 exports, my_start_token, my_end_token, from_moz 541 ); 542 me_to_moz = new Function("to_moz", "to_moz_block", "to_moz_scope", "return(" + me_to_moz + ")")( 543 to_moz, to_moz_block, to_moz_scope 544 ); 545 MOZ_TO_ME[moztype] = moz_to_me; 546 def_to_moz(mytype, me_to_moz); 547 }; 548 549 var FROM_MOZ_STACK = null; 550 551 function from_moz(node) { 552 FROM_MOZ_STACK.push(node); 553 var ret = node != null ? MOZ_TO_ME[node.type](node) : null; 554 FROM_MOZ_STACK.pop(); 555 return ret; 556 }; 557 558 AST_Node.from_mozilla_ast = function(node){ 559 var save_stack = FROM_MOZ_STACK; 560 FROM_MOZ_STACK = []; 561 var ast = from_moz(node); 562 FROM_MOZ_STACK = save_stack; 563 return ast; 564 }; 565 566 function set_moz_loc(mynode, moznode, myparent) { 567 var start = mynode.start; 568 var end = mynode.end; 569 if (start.pos != null && end.endpos != null) { 570 moznode.range = [start.pos, end.endpos]; 571 } 572 if (start.line) { 573 moznode.loc = { 574 start: {line: start.line, column: start.col}, 575 end: end.endline ? {line: end.endline, column: end.endcol} : null 576 }; 577 if (start.file) { 578 moznode.loc.source = start.file; 579 } 580 } 581 return moznode; 582 }; 583 584 function def_to_moz(mytype, handler) { 585 mytype.DEFMETHOD("to_mozilla_ast", function() { 586 return set_moz_loc(this, handler(this)); 587 }); 588 }; 589 590 function to_moz(node) { 591 return node != null ? node.to_mozilla_ast() : null; 592 }; 593 594 function to_moz_block(node) { 595 return { 596 type: "BlockStatement", 597 body: node.body.map(to_moz) 598 }; 599 }; 600 601 function to_moz_scope(type, node) { 602 var body = node.body.map(to_moz); 603 if (node.body[0] instanceof AST_SimpleStatement && node.body[0].body instanceof AST_String) { 604 body.unshift(to_moz(new AST_EmptyStatement(node.body[0]))); 605 } 606 return { 607 type: type, 608 body: body 609 }; 610 }; 611})(); 612