output.js 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326
  1. /***********************************************************************
  2. A JavaScript tokenizer / parser / beautifier / compressor.
  3. https://github.com/mishoo/UglifyJS2
  4. -------------------------------- (C) ---------------------------------
  5. Author: Mihai Bazon
  6. <mihai.bazon@gmail.com>
  7. http://mihai.bazon.net/blog
  8. Distributed under the BSD license:
  9. Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
  10. Redistribution and use in source and binary forms, with or without
  11. modification, are permitted provided that the following conditions
  12. are met:
  13. * Redistributions of source code must retain the above
  14. copyright notice, this list of conditions and the following
  15. disclaimer.
  16. * Redistributions in binary form must reproduce the above
  17. copyright notice, this list of conditions and the following
  18. disclaimer in the documentation and/or other materials
  19. provided with the distribution.
  20. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
  21. EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  23. PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
  24. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  25. OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26. PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  27. PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  29. TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  30. THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. SUCH DAMAGE.
  32. ***********************************************************************/
  33. "use strict";
  34. function OutputStream(options) {
  35. options = defaults(options, {
  36. indent_start : 0,
  37. indent_level : 4,
  38. quote_keys : false,
  39. space_colon : true,
  40. ascii_only : false,
  41. unescape_regexps : false,
  42. inline_script : false,
  43. width : 80,
  44. max_line_len : 32000,
  45. beautify : false,
  46. source_map : null,
  47. bracketize : false,
  48. semicolons : true,
  49. comments : false,
  50. preserve_line : false,
  51. screw_ie8 : false,
  52. preamble : null,
  53. quote_style : 0
  54. }, true);
  55. var indentation = 0;
  56. var current_col = 0;
  57. var current_line = 1;
  58. var current_pos = 0;
  59. var OUTPUT = "";
  60. function to_ascii(str, identifier) {
  61. return str.replace(/[\u0080-\uffff]/g, function(ch) {
  62. var code = ch.charCodeAt(0).toString(16);
  63. if (code.length <= 2 && !identifier) {
  64. while (code.length < 2) code = "0" + code;
  65. return "\\x" + code;
  66. } else {
  67. while (code.length < 4) code = "0" + code;
  68. return "\\u" + code;
  69. }
  70. });
  71. };
  72. function make_string(str, quote) {
  73. var dq = 0, sq = 0;
  74. str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s){
  75. switch (s) {
  76. case "\\": return "\\\\";
  77. case "\b": return "\\b";
  78. case "\f": return "\\f";
  79. case "\n": return "\\n";
  80. case "\r": return "\\r";
  81. case "\u2028": return "\\u2028";
  82. case "\u2029": return "\\u2029";
  83. case '"': ++dq; return '"';
  84. case "'": ++sq; return "'";
  85. case "\0": return "\\x00";
  86. case "\ufeff": return "\\ufeff";
  87. }
  88. return s;
  89. });
  90. function quote_single() {
  91. return "'" + str.replace(/\x27/g, "\\'") + "'";
  92. }
  93. function quote_double() {
  94. return '"' + str.replace(/\x22/g, '\\"') + '"';
  95. }
  96. if (options.ascii_only) str = to_ascii(str);
  97. switch (options.quote_style) {
  98. case 1:
  99. return quote_single();
  100. case 2:
  101. return quote_double();
  102. case 3:
  103. return quote == "'" ? quote_single() : quote_double();
  104. default:
  105. return dq > sq ? quote_single() : quote_double();
  106. }
  107. };
  108. function encode_string(str, quote) {
  109. var ret = make_string(str, quote);
  110. if (options.inline_script)
  111. ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
  112. return ret;
  113. };
  114. function make_name(name) {
  115. name = name.toString();
  116. if (options.ascii_only)
  117. name = to_ascii(name, true);
  118. return name;
  119. };
  120. function make_indent(back) {
  121. return repeat_string(" ", options.indent_start + indentation - back * options.indent_level);
  122. };
  123. /* -----[ beautification/minification ]----- */
  124. var might_need_space = false;
  125. var might_need_semicolon = false;
  126. var last = null;
  127. function last_char() {
  128. return last.charAt(last.length - 1);
  129. };
  130. function maybe_newline() {
  131. if (options.max_line_len && current_col > options.max_line_len)
  132. print("\n");
  133. };
  134. var requireSemicolonChars = makePredicate("( [ + * / - , .");
  135. function print(str) {
  136. str = String(str);
  137. var ch = str.charAt(0);
  138. if (might_need_semicolon) {
  139. if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) {
  140. if (options.semicolons || requireSemicolonChars(ch)) {
  141. OUTPUT += ";";
  142. current_col++;
  143. current_pos++;
  144. } else {
  145. OUTPUT += "\n";
  146. current_pos++;
  147. current_line++;
  148. current_col = 0;
  149. }
  150. if (!options.beautify)
  151. might_need_space = false;
  152. }
  153. might_need_semicolon = false;
  154. }
  155. if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
  156. var target_line = stack[stack.length - 1].start.line;
  157. while (current_line < target_line) {
  158. OUTPUT += "\n";
  159. current_pos++;
  160. current_line++;
  161. current_col = 0;
  162. might_need_space = false;
  163. }
  164. }
  165. if (might_need_space) {
  166. var prev = last_char();
  167. if ((is_identifier_char(prev)
  168. && (is_identifier_char(ch) || ch == "\\"))
  169. || (/^[\+\-\/]$/.test(ch) && ch == prev))
  170. {
  171. OUTPUT += " ";
  172. current_col++;
  173. current_pos++;
  174. }
  175. might_need_space = false;
  176. }
  177. var a = str.split(/\r?\n/), n = a.length - 1;
  178. current_line += n;
  179. if (n == 0) {
  180. current_col += a[n].length;
  181. } else {
  182. current_col = a[n].length;
  183. }
  184. current_pos += str.length;
  185. last = str;
  186. OUTPUT += str;
  187. };
  188. var space = options.beautify ? function() {
  189. print(" ");
  190. } : function() {
  191. might_need_space = true;
  192. };
  193. var indent = options.beautify ? function(half) {
  194. if (options.beautify) {
  195. print(make_indent(half ? 0.5 : 0));
  196. }
  197. } : noop;
  198. var with_indent = options.beautify ? function(col, cont) {
  199. if (col === true) col = next_indent();
  200. var save_indentation = indentation;
  201. indentation = col;
  202. var ret = cont();
  203. indentation = save_indentation;
  204. return ret;
  205. } : function(col, cont) { return cont() };
  206. var newline = options.beautify ? function() {
  207. print("\n");
  208. } : maybe_newline;
  209. var semicolon = options.beautify ? function() {
  210. print(";");
  211. } : function() {
  212. might_need_semicolon = true;
  213. };
  214. function force_semicolon() {
  215. might_need_semicolon = false;
  216. print(";");
  217. };
  218. function next_indent() {
  219. return indentation + options.indent_level;
  220. };
  221. function with_block(cont) {
  222. var ret;
  223. print("{");
  224. newline();
  225. with_indent(next_indent(), function(){
  226. ret = cont();
  227. });
  228. indent();
  229. print("}");
  230. return ret;
  231. };
  232. function with_parens(cont) {
  233. print("(");
  234. //XXX: still nice to have that for argument lists
  235. //var ret = with_indent(current_col, cont);
  236. var ret = cont();
  237. print(")");
  238. return ret;
  239. };
  240. function with_square(cont) {
  241. print("[");
  242. //var ret = with_indent(current_col, cont);
  243. var ret = cont();
  244. print("]");
  245. return ret;
  246. };
  247. function comma() {
  248. print(",");
  249. space();
  250. };
  251. function colon() {
  252. print(":");
  253. if (options.space_colon) space();
  254. };
  255. var add_mapping = options.source_map ? function(token, name) {
  256. try {
  257. if (token) options.source_map.add(
  258. token.file || "?",
  259. current_line, current_col,
  260. token.line, token.col,
  261. (!name && token.type == "name") ? token.value : name
  262. );
  263. } catch(ex) {
  264. AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
  265. file: token.file,
  266. line: token.line,
  267. col: token.col,
  268. cline: current_line,
  269. ccol: current_col,
  270. name: name || ""
  271. })
  272. }
  273. } : noop;
  274. function get() {
  275. return OUTPUT;
  276. };
  277. if (options.preamble) {
  278. print(options.preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n"));
  279. }
  280. var stack = [];
  281. return {
  282. get : get,
  283. toString : get,
  284. indent : indent,
  285. indentation : function() { return indentation },
  286. current_width : function() { return current_col - indentation },
  287. should_break : function() { return options.width && this.current_width() >= options.width },
  288. newline : newline,
  289. print : print,
  290. space : space,
  291. comma : comma,
  292. colon : colon,
  293. last : function() { return last },
  294. semicolon : semicolon,
  295. force_semicolon : force_semicolon,
  296. to_ascii : to_ascii,
  297. print_name : function(name) { print(make_name(name)) },
  298. print_string : function(str, quote) { print(encode_string(str, quote)) },
  299. next_indent : next_indent,
  300. with_indent : with_indent,
  301. with_block : with_block,
  302. with_parens : with_parens,
  303. with_square : with_square,
  304. add_mapping : add_mapping,
  305. option : function(opt) { return options[opt] },
  306. line : function() { return current_line },
  307. col : function() { return current_col },
  308. pos : function() { return current_pos },
  309. push_node : function(node) { stack.push(node) },
  310. pop_node : function() { return stack.pop() },
  311. stack : function() { return stack },
  312. parent : function(n) {
  313. return stack[stack.length - 2 - (n || 0)];
  314. }
  315. };
  316. };
  317. /* -----[ code generators ]----- */
  318. (function(){
  319. /* -----[ utils ]----- */
  320. function DEFPRINT(nodetype, generator) {
  321. nodetype.DEFMETHOD("_codegen", generator);
  322. };
  323. AST_Node.DEFMETHOD("print", function(stream, force_parens){
  324. var self = this, generator = self._codegen;
  325. function doit() {
  326. self.add_comments(stream);
  327. self.add_source_map(stream);
  328. generator(self, stream);
  329. }
  330. stream.push_node(self);
  331. if (force_parens || self.needs_parens(stream)) {
  332. stream.with_parens(doit);
  333. } else {
  334. doit();
  335. }
  336. stream.pop_node();
  337. });
  338. AST_Node.DEFMETHOD("print_to_string", function(options){
  339. var s = OutputStream(options);
  340. this.print(s);
  341. return s.get();
  342. });
  343. /* -----[ comments ]----- */
  344. AST_Node.DEFMETHOD("add_comments", function(output){
  345. var c = output.option("comments"), self = this;
  346. if (c) {
  347. var start = self.start;
  348. if (start && !start._comments_dumped) {
  349. start._comments_dumped = true;
  350. var comments = start.comments_before || [];
  351. // XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112
  352. // and https://github.com/mishoo/UglifyJS2/issues/372
  353. if (self instanceof AST_Exit && self.value) {
  354. self.value.walk(new TreeWalker(function(node){
  355. if (node.start && node.start.comments_before) {
  356. comments = comments.concat(node.start.comments_before);
  357. node.start.comments_before = [];
  358. }
  359. if (node instanceof AST_Function ||
  360. node instanceof AST_Array ||
  361. node instanceof AST_Object)
  362. {
  363. return true; // don't go inside.
  364. }
  365. }));
  366. }
  367. if (c.test) {
  368. comments = comments.filter(function(comment){
  369. return c.test(comment.value);
  370. });
  371. } else if (typeof c == "function") {
  372. comments = comments.filter(function(comment){
  373. return c(self, comment);
  374. });
  375. }
  376. // Keep single line comments after nlb, after nlb
  377. if (!output.option("beautify") && comments.length > 0 &&
  378. /comment[134]/.test(comments[0].type) &&
  379. output.col() !== 0 && comments[0].nlb)
  380. {
  381. output.print("\n");
  382. }
  383. comments.forEach(function(c){
  384. if (/comment[134]/.test(c.type)) {
  385. output.print("//" + c.value + "\n");
  386. output.indent();
  387. }
  388. else if (c.type == "comment2") {
  389. output.print("/*" + c.value + "*/");
  390. if (start.nlb) {
  391. output.print("\n");
  392. output.indent();
  393. } else {
  394. output.space();
  395. }
  396. }
  397. });
  398. }
  399. }
  400. });
  401. /* -----[ PARENTHESES ]----- */
  402. function PARENS(nodetype, func) {
  403. if (Array.isArray(nodetype)) {
  404. nodetype.forEach(function(nodetype){
  405. PARENS(nodetype, func);
  406. });
  407. } else {
  408. nodetype.DEFMETHOD("needs_parens", func);
  409. }
  410. };
  411. PARENS(AST_Node, function(){
  412. return false;
  413. });
  414. // a function expression needs parens around it when it's provably
  415. // the first token to appear in a statement.
  416. PARENS(AST_Function, function(output){
  417. return first_in_statement(output);
  418. });
  419. // same goes for an object literal, because otherwise it would be
  420. // interpreted as a block of code.
  421. PARENS(AST_Object, function(output){
  422. return first_in_statement(output);
  423. });
  424. PARENS([ AST_Unary, AST_Undefined ], function(output){
  425. var p = output.parent();
  426. return p instanceof AST_PropAccess && p.expression === this;
  427. });
  428. PARENS(AST_Seq, function(output){
  429. var p = output.parent();
  430. return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
  431. || p instanceof AST_Unary // !(foo, bar, baz)
  432. || p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 8
  433. || p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4
  434. || p instanceof AST_PropAccess // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
  435. || p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
  436. || p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2
  437. || p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30)
  438. * ==> 20 (side effect, set a := 10 and b := 20) */
  439. ;
  440. });
  441. PARENS(AST_Binary, function(output){
  442. var p = output.parent();
  443. // (foo && bar)()
  444. if (p instanceof AST_Call && p.expression === this)
  445. return true;
  446. // typeof (foo && bar)
  447. if (p instanceof AST_Unary)
  448. return true;
  449. // (foo && bar)["prop"], (foo && bar).prop
  450. if (p instanceof AST_PropAccess && p.expression === this)
  451. return true;
  452. // this deals with precedence: 3 * (2 + 1)
  453. if (p instanceof AST_Binary) {
  454. var po = p.operator, pp = PRECEDENCE[po];
  455. var so = this.operator, sp = PRECEDENCE[so];
  456. if (pp > sp
  457. || (pp == sp
  458. && this === p.right)) {
  459. return true;
  460. }
  461. }
  462. });
  463. PARENS(AST_PropAccess, function(output){
  464. var p = output.parent();
  465. if (p instanceof AST_New && p.expression === this) {
  466. // i.e. new (foo.bar().baz)
  467. //
  468. // if there's one call into this subtree, then we need
  469. // parens around it too, otherwise the call will be
  470. // interpreted as passing the arguments to the upper New
  471. // expression.
  472. try {
  473. this.walk(new TreeWalker(function(node){
  474. if (node instanceof AST_Call) throw p;
  475. }));
  476. } catch(ex) {
  477. if (ex !== p) throw ex;
  478. return true;
  479. }
  480. }
  481. });
  482. PARENS(AST_Call, function(output){
  483. var p = output.parent(), p1;
  484. if (p instanceof AST_New && p.expression === this)
  485. return true;
  486. // workaround for Safari bug.
  487. // https://bugs.webkit.org/show_bug.cgi?id=123506
  488. return this.expression instanceof AST_Function
  489. && p instanceof AST_PropAccess
  490. && p.expression === this
  491. && (p1 = output.parent(1)) instanceof AST_Assign
  492. && p1.left === p;
  493. });
  494. PARENS(AST_New, function(output){
  495. var p = output.parent();
  496. if (no_constructor_parens(this, output)
  497. && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
  498. || p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
  499. return true;
  500. });
  501. PARENS(AST_Number, function(output){
  502. var p = output.parent();
  503. if (this.getValue() < 0 && p instanceof AST_PropAccess && p.expression === this)
  504. return true;
  505. });
  506. PARENS([ AST_Assign, AST_Conditional ], function (output){
  507. var p = output.parent();
  508. // !(a = false) → true
  509. if (p instanceof AST_Unary)
  510. return true;
  511. // 1 + (a = 2) + 3 → 6, side effect setting a = 2
  512. if (p instanceof AST_Binary && !(p instanceof AST_Assign))
  513. return true;
  514. // (a = func)() —or— new (a = Object)()
  515. if (p instanceof AST_Call && p.expression === this)
  516. return true;
  517. // (a = foo) ? bar : baz
  518. if (p instanceof AST_Conditional && p.condition === this)
  519. return true;
  520. // (a = foo)["prop"] —or— (a = foo).prop
  521. if (p instanceof AST_PropAccess && p.expression === this)
  522. return true;
  523. });
  524. /* -----[ PRINTERS ]----- */
  525. DEFPRINT(AST_Directive, function(self, output){
  526. output.print_string(self.value, self.quote);
  527. output.semicolon();
  528. });
  529. DEFPRINT(AST_Debugger, function(self, output){
  530. output.print("debugger");
  531. output.semicolon();
  532. });
  533. /* -----[ statements ]----- */
  534. function display_body(body, is_toplevel, output) {
  535. var last = body.length - 1;
  536. body.forEach(function(stmt, i){
  537. if (!(stmt instanceof AST_EmptyStatement)) {
  538. output.indent();
  539. stmt.print(output);
  540. if (!(i == last && is_toplevel)) {
  541. output.newline();
  542. if (is_toplevel) output.newline();
  543. }
  544. }
  545. });
  546. };
  547. AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output){
  548. force_statement(this.body, output);
  549. });
  550. DEFPRINT(AST_Statement, function(self, output){
  551. self.body.print(output);
  552. output.semicolon();
  553. });
  554. DEFPRINT(AST_Toplevel, function(self, output){
  555. display_body(self.body, true, output);
  556. output.print("");
  557. });
  558. DEFPRINT(AST_LabeledStatement, function(self, output){
  559. self.label.print(output);
  560. output.colon();
  561. self.body.print(output);
  562. });
  563. DEFPRINT(AST_SimpleStatement, function(self, output){
  564. self.body.print(output);
  565. output.semicolon();
  566. });
  567. function print_bracketed(body, output) {
  568. if (body.length > 0) output.with_block(function(){
  569. display_body(body, false, output);
  570. });
  571. else output.print("{}");
  572. };
  573. DEFPRINT(AST_BlockStatement, function(self, output){
  574. print_bracketed(self.body, output);
  575. });
  576. DEFPRINT(AST_EmptyStatement, function(self, output){
  577. output.semicolon();
  578. });
  579. DEFPRINT(AST_Do, function(self, output){
  580. output.print("do");
  581. output.space();
  582. self._do_print_body(output);
  583. output.space();
  584. output.print("while");
  585. output.space();
  586. output.with_parens(function(){
  587. self.condition.print(output);
  588. });
  589. output.semicolon();
  590. });
  591. DEFPRINT(AST_While, function(self, output){
  592. output.print("while");
  593. output.space();
  594. output.with_parens(function(){
  595. self.condition.print(output);
  596. });
  597. output.space();
  598. self._do_print_body(output);
  599. });
  600. DEFPRINT(AST_For, function(self, output){
  601. output.print("for");
  602. output.space();
  603. output.with_parens(function(){
  604. if (self.init && !(self.init instanceof AST_EmptyStatement)) {
  605. if (self.init instanceof AST_Definitions) {
  606. self.init.print(output);
  607. } else {
  608. parenthesize_for_noin(self.init, output, true);
  609. }
  610. output.print(";");
  611. output.space();
  612. } else {
  613. output.print(";");
  614. }
  615. if (self.condition) {
  616. self.condition.print(output);
  617. output.print(";");
  618. output.space();
  619. } else {
  620. output.print(";");
  621. }
  622. if (self.step) {
  623. self.step.print(output);
  624. }
  625. });
  626. output.space();
  627. self._do_print_body(output);
  628. });
  629. DEFPRINT(AST_ForIn, function(self, output){
  630. output.print("for");
  631. output.space();
  632. output.with_parens(function(){
  633. self.init.print(output);
  634. output.space();
  635. output.print("in");
  636. output.space();
  637. self.object.print(output);
  638. });
  639. output.space();
  640. self._do_print_body(output);
  641. });
  642. DEFPRINT(AST_With, function(self, output){
  643. output.print("with");
  644. output.space();
  645. output.with_parens(function(){
  646. self.expression.print(output);
  647. });
  648. output.space();
  649. self._do_print_body(output);
  650. });
  651. /* -----[ functions ]----- */
  652. AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){
  653. var self = this;
  654. if (!nokeyword) {
  655. output.print("function");
  656. }
  657. if (self.name) {
  658. output.space();
  659. self.name.print(output);
  660. }
  661. output.with_parens(function(){
  662. self.argnames.forEach(function(arg, i){
  663. if (i) output.comma();
  664. arg.print(output);
  665. });
  666. });
  667. output.space();
  668. print_bracketed(self.body, output);
  669. });
  670. DEFPRINT(AST_Lambda, function(self, output){
  671. self._do_print(output);
  672. });
  673. /* -----[ exits ]----- */
  674. AST_Exit.DEFMETHOD("_do_print", function(output, kind){
  675. output.print(kind);
  676. if (this.value) {
  677. output.space();
  678. this.value.print(output);
  679. }
  680. output.semicolon();
  681. });
  682. DEFPRINT(AST_Return, function(self, output){
  683. self._do_print(output, "return");
  684. });
  685. DEFPRINT(AST_Throw, function(self, output){
  686. self._do_print(output, "throw");
  687. });
  688. /* -----[ loop control ]----- */
  689. AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
  690. output.print(kind);
  691. if (this.label) {
  692. output.space();
  693. this.label.print(output);
  694. }
  695. output.semicolon();
  696. });
  697. DEFPRINT(AST_Break, function(self, output){
  698. self._do_print(output, "break");
  699. });
  700. DEFPRINT(AST_Continue, function(self, output){
  701. self._do_print(output, "continue");
  702. });
  703. /* -----[ if ]----- */
  704. function make_then(self, output) {
  705. if (output.option("bracketize")) {
  706. make_block(self.body, output);
  707. return;
  708. }
  709. // The squeezer replaces "block"-s that contain only a single
  710. // statement with the statement itself; technically, the AST
  711. // is correct, but this can create problems when we output an
  712. // IF having an ELSE clause where the THEN clause ends in an
  713. // IF *without* an ELSE block (then the outer ELSE would refer
  714. // to the inner IF). This function checks for this case and
  715. // adds the block brackets if needed.
  716. if (!self.body)
  717. return output.force_semicolon();
  718. if (self.body instanceof AST_Do
  719. && !output.option("screw_ie8")) {
  720. // https://github.com/mishoo/UglifyJS/issues/#issue/57 IE
  721. // croaks with "syntax error" on code like this: if (foo)
  722. // do ... while(cond); else ... we need block brackets
  723. // around do/while
  724. make_block(self.body, output);
  725. return;
  726. }
  727. var b = self.body;
  728. while (true) {
  729. if (b instanceof AST_If) {
  730. if (!b.alternative) {
  731. make_block(self.body, output);
  732. return;
  733. }
  734. b = b.alternative;
  735. }
  736. else if (b instanceof AST_StatementWithBody) {
  737. b = b.body;
  738. }
  739. else break;
  740. }
  741. force_statement(self.body, output);
  742. };
  743. DEFPRINT(AST_If, function(self, output){
  744. output.print("if");
  745. output.space();
  746. output.with_parens(function(){
  747. self.condition.print(output);
  748. });
  749. output.space();
  750. if (self.alternative) {
  751. make_then(self, output);
  752. output.space();
  753. output.print("else");
  754. output.space();
  755. force_statement(self.alternative, output);
  756. } else {
  757. self._do_print_body(output);
  758. }
  759. });
  760. /* -----[ switch ]----- */
  761. DEFPRINT(AST_Switch, function(self, output){
  762. output.print("switch");
  763. output.space();
  764. output.with_parens(function(){
  765. self.expression.print(output);
  766. });
  767. output.space();
  768. if (self.body.length > 0) output.with_block(function(){
  769. self.body.forEach(function(stmt, i){
  770. if (i) output.newline();
  771. output.indent(true);
  772. stmt.print(output);
  773. });
  774. });
  775. else output.print("{}");
  776. });
  777. AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){
  778. if (this.body.length > 0) {
  779. output.newline();
  780. this.body.forEach(function(stmt){
  781. output.indent();
  782. stmt.print(output);
  783. output.newline();
  784. });
  785. }
  786. });
  787. DEFPRINT(AST_Default, function(self, output){
  788. output.print("default:");
  789. self._do_print_body(output);
  790. });
  791. DEFPRINT(AST_Case, function(self, output){
  792. output.print("case");
  793. output.space();
  794. self.expression.print(output);
  795. output.print(":");
  796. self._do_print_body(output);
  797. });
  798. /* -----[ exceptions ]----- */
  799. DEFPRINT(AST_Try, function(self, output){
  800. output.print("try");
  801. output.space();
  802. print_bracketed(self.body, output);
  803. if (self.bcatch) {
  804. output.space();
  805. self.bcatch.print(output);
  806. }
  807. if (self.bfinally) {
  808. output.space();
  809. self.bfinally.print(output);
  810. }
  811. });
  812. DEFPRINT(AST_Catch, function(self, output){
  813. output.print("catch");
  814. output.space();
  815. output.with_parens(function(){
  816. self.argname.print(output);
  817. });
  818. output.space();
  819. print_bracketed(self.body, output);
  820. });
  821. DEFPRINT(AST_Finally, function(self, output){
  822. output.print("finally");
  823. output.space();
  824. print_bracketed(self.body, output);
  825. });
  826. /* -----[ var/const ]----- */
  827. AST_Definitions.DEFMETHOD("_do_print", function(output, kind){
  828. output.print(kind);
  829. output.space();
  830. this.definitions.forEach(function(def, i){
  831. if (i) output.comma();
  832. def.print(output);
  833. });
  834. var p = output.parent();
  835. var in_for = p instanceof AST_For || p instanceof AST_ForIn;
  836. var avoid_semicolon = in_for && p.init === this;
  837. if (!avoid_semicolon)
  838. output.semicolon();
  839. });
  840. DEFPRINT(AST_Var, function(self, output){
  841. self._do_print(output, "var");
  842. });
  843. DEFPRINT(AST_Const, function(self, output){
  844. self._do_print(output, "const");
  845. });
  846. function parenthesize_for_noin(node, output, noin) {
  847. if (!noin) node.print(output);
  848. else try {
  849. // need to take some precautions here:
  850. // https://github.com/mishoo/UglifyJS2/issues/60
  851. node.walk(new TreeWalker(function(node){
  852. if (node instanceof AST_Binary && node.operator == "in")
  853. throw output;
  854. }));
  855. node.print(output);
  856. } catch(ex) {
  857. if (ex !== output) throw ex;
  858. node.print(output, true);
  859. }
  860. };
  861. DEFPRINT(AST_VarDef, function(self, output){
  862. self.name.print(output);
  863. if (self.value) {
  864. output.space();
  865. output.print("=");
  866. output.space();
  867. var p = output.parent(1);
  868. var noin = p instanceof AST_For || p instanceof AST_ForIn;
  869. parenthesize_for_noin(self.value, output, noin);
  870. }
  871. });
  872. /* -----[ other expressions ]----- */
  873. DEFPRINT(AST_Call, function(self, output){
  874. self.expression.print(output);
  875. if (self instanceof AST_New && no_constructor_parens(self, output))
  876. return;
  877. output.with_parens(function(){
  878. self.args.forEach(function(expr, i){
  879. if (i) output.comma();
  880. expr.print(output);
  881. });
  882. });
  883. });
  884. DEFPRINT(AST_New, function(self, output){
  885. output.print("new");
  886. output.space();
  887. AST_Call.prototype._codegen(self, output);
  888. });
  889. AST_Seq.DEFMETHOD("_do_print", function(output){
  890. this.car.print(output);
  891. if (this.cdr) {
  892. output.comma();
  893. if (output.should_break()) {
  894. output.newline();
  895. output.indent();
  896. }
  897. this.cdr.print(output);
  898. }
  899. });
  900. DEFPRINT(AST_Seq, function(self, output){
  901. self._do_print(output);
  902. // var p = output.parent();
  903. // if (p instanceof AST_Statement) {
  904. // output.with_indent(output.next_indent(), function(){
  905. // self._do_print(output);
  906. // });
  907. // } else {
  908. // self._do_print(output);
  909. // }
  910. });
  911. DEFPRINT(AST_Dot, function(self, output){
  912. var expr = self.expression;
  913. expr.print(output);
  914. if (expr instanceof AST_Number && expr.getValue() >= 0) {
  915. if (!/[xa-f.]/i.test(output.last())) {
  916. output.print(".");
  917. }
  918. }
  919. output.print(".");
  920. // the name after dot would be mapped about here.
  921. output.add_mapping(self.end);
  922. output.print_name(self.property);
  923. });
  924. DEFPRINT(AST_Sub, function(self, output){
  925. self.expression.print(output);
  926. output.print("[");
  927. self.property.print(output);
  928. output.print("]");
  929. });
  930. DEFPRINT(AST_UnaryPrefix, function(self, output){
  931. var op = self.operator;
  932. output.print(op);
  933. if (/^[a-z]/i.test(op)
  934. || (/[+-]$/.test(op)
  935. && self.expression instanceof AST_UnaryPrefix
  936. && /^[+-]/.test(self.expression.operator))) {
  937. output.space();
  938. }
  939. self.expression.print(output);
  940. });
  941. DEFPRINT(AST_UnaryPostfix, function(self, output){
  942. self.expression.print(output);
  943. output.print(self.operator);
  944. });
  945. DEFPRINT(AST_Binary, function(self, output){
  946. self.left.print(output);
  947. output.space();
  948. output.print(self.operator);
  949. if (self.operator == "<"
  950. && self.right instanceof AST_UnaryPrefix
  951. && self.right.operator == "!"
  952. && self.right.expression instanceof AST_UnaryPrefix
  953. && self.right.expression.operator == "--") {
  954. // space is mandatory to avoid outputting <!--
  955. // http://javascript.spec.whatwg.org/#comment-syntax
  956. output.print(" ");
  957. } else {
  958. // the space is optional depending on "beautify"
  959. output.space();
  960. }
  961. self.right.print(output);
  962. });
  963. DEFPRINT(AST_Conditional, function(self, output){
  964. self.condition.print(output);
  965. output.space();
  966. output.print("?");
  967. output.space();
  968. self.consequent.print(output);
  969. output.space();
  970. output.colon();
  971. self.alternative.print(output);
  972. });
  973. /* -----[ literals ]----- */
  974. DEFPRINT(AST_Array, function(self, output){
  975. output.with_square(function(){
  976. var a = self.elements, len = a.length;
  977. if (len > 0) output.space();
  978. a.forEach(function(exp, i){
  979. if (i) output.comma();
  980. exp.print(output);
  981. // If the final element is a hole, we need to make sure it
  982. // doesn't look like a trailing comma, by inserting an actual
  983. // trailing comma.
  984. if (i === len - 1 && exp instanceof AST_Hole)
  985. output.comma();
  986. });
  987. if (len > 0) output.space();
  988. });
  989. });
  990. DEFPRINT(AST_Object, function(self, output){
  991. if (self.properties.length > 0) output.with_block(function(){
  992. self.properties.forEach(function(prop, i){
  993. if (i) {
  994. output.print(",");
  995. output.newline();
  996. }
  997. output.indent();
  998. prop.print(output);
  999. });
  1000. output.newline();
  1001. });
  1002. else output.print("{}");
  1003. });
  1004. DEFPRINT(AST_ObjectKeyVal, function(self, output){
  1005. var key = self.key;
  1006. var quote = self.quote;
  1007. if (output.option("quote_keys")) {
  1008. output.print_string(key + "");
  1009. } else if ((typeof key == "number"
  1010. || !output.option("beautify")
  1011. && +key + "" == key)
  1012. && parseFloat(key) >= 0) {
  1013. output.print(make_num(key));
  1014. } else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
  1015. output.print_name(key);
  1016. } else {
  1017. output.print_string(key, quote);
  1018. }
  1019. output.colon();
  1020. self.value.print(output);
  1021. });
  1022. DEFPRINT(AST_ObjectSetter, function(self, output){
  1023. output.print("set");
  1024. output.space();
  1025. self.key.print(output);
  1026. self.value._do_print(output, true);
  1027. });
  1028. DEFPRINT(AST_ObjectGetter, function(self, output){
  1029. output.print("get");
  1030. output.space();
  1031. self.key.print(output);
  1032. self.value._do_print(output, true);
  1033. });
  1034. DEFPRINT(AST_Symbol, function(self, output){
  1035. var def = self.definition();
  1036. output.print_name(def ? def.mangled_name || def.name : self.name);
  1037. });
  1038. DEFPRINT(AST_Undefined, function(self, output){
  1039. output.print("void 0");
  1040. });
  1041. DEFPRINT(AST_Hole, noop);
  1042. DEFPRINT(AST_Infinity, function(self, output){
  1043. output.print("Infinity");
  1044. });
  1045. DEFPRINT(AST_NaN, function(self, output){
  1046. output.print("NaN");
  1047. });
  1048. DEFPRINT(AST_This, function(self, output){
  1049. output.print("this");
  1050. });
  1051. DEFPRINT(AST_Constant, function(self, output){
  1052. output.print(self.getValue());
  1053. });
  1054. DEFPRINT(AST_String, function(self, output){
  1055. output.print_string(self.getValue(), self.quote);
  1056. });
  1057. DEFPRINT(AST_Number, function(self, output){
  1058. output.print(make_num(self.getValue()));
  1059. });
  1060. function regexp_safe_literal(code) {
  1061. return [
  1062. 0x5c , // \
  1063. 0x2f , // /
  1064. 0x2e , // .
  1065. 0x2b , // +
  1066. 0x2a , // *
  1067. 0x3f , // ?
  1068. 0x28 , // (
  1069. 0x29 , // )
  1070. 0x5b , // [
  1071. 0x5d , // ]
  1072. 0x7b , // {
  1073. 0x7d , // }
  1074. 0x24 , // $
  1075. 0x5e , // ^
  1076. 0x3a , // :
  1077. 0x7c , // |
  1078. 0x21 , // !
  1079. 0x0a , // \n
  1080. 0x0d , // \r
  1081. 0x00 , // \0
  1082. 0xfeff , // Unicode BOM
  1083. 0x2028 , // unicode "line separator"
  1084. 0x2029 , // unicode "paragraph separator"
  1085. ].indexOf(code) < 0;
  1086. };
  1087. DEFPRINT(AST_RegExp, function(self, output){
  1088. var str = self.getValue().toString();
  1089. if (output.option("ascii_only")) {
  1090. str = output.to_ascii(str);
  1091. } else if (output.option("unescape_regexps")) {
  1092. str = str.split("\\\\").map(function(str){
  1093. return str.replace(/\\u[0-9a-fA-F]{4}|\\x[0-9a-fA-F]{2}/g, function(s){
  1094. var code = parseInt(s.substr(2), 16);
  1095. return regexp_safe_literal(code) ? String.fromCharCode(code) : s;
  1096. });
  1097. }).join("\\\\");
  1098. }
  1099. output.print(str);
  1100. var p = output.parent();
  1101. if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
  1102. output.print(" ");
  1103. });
  1104. function force_statement(stat, output) {
  1105. if (output.option("bracketize")) {
  1106. if (!stat || stat instanceof AST_EmptyStatement)
  1107. output.print("{}");
  1108. else if (stat instanceof AST_BlockStatement)
  1109. stat.print(output);
  1110. else output.with_block(function(){
  1111. output.indent();
  1112. stat.print(output);
  1113. output.newline();
  1114. });
  1115. } else {
  1116. if (!stat || stat instanceof AST_EmptyStatement)
  1117. output.force_semicolon();
  1118. else
  1119. stat.print(output);
  1120. }
  1121. };
  1122. // return true if the node at the top of the stack (that means the
  1123. // innermost node in the current output) is lexically the first in
  1124. // a statement.
  1125. function first_in_statement(output) {
  1126. var a = output.stack(), i = a.length, node = a[--i], p = a[--i];
  1127. while (i > 0) {
  1128. if (p instanceof AST_Statement && p.body === node)
  1129. return true;
  1130. if ((p instanceof AST_Seq && p.car === node ) ||
  1131. (p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) ||
  1132. (p instanceof AST_Dot && p.expression === node ) ||
  1133. (p instanceof AST_Sub && p.expression === node ) ||
  1134. (p instanceof AST_Conditional && p.condition === node ) ||
  1135. (p instanceof AST_Binary && p.left === node ) ||
  1136. (p instanceof AST_UnaryPostfix && p.expression === node ))
  1137. {
  1138. node = p;
  1139. p = a[--i];
  1140. } else {
  1141. return false;
  1142. }
  1143. }
  1144. };
  1145. // self should be AST_New. decide if we want to show parens or not.
  1146. function no_constructor_parens(self, output) {
  1147. return self.args.length == 0 && !output.option("beautify");
  1148. };
  1149. function best_of(a) {
  1150. var best = a[0], len = best.length;
  1151. for (var i = 1; i < a.length; ++i) {
  1152. if (a[i].length < len) {
  1153. best = a[i];
  1154. len = best.length;
  1155. }
  1156. }
  1157. return best;
  1158. };
  1159. function make_num(num) {
  1160. var str = num.toString(10), a = [ str.replace(/^0\./, ".").replace('e+', 'e') ], m;
  1161. if (Math.floor(num) === num) {
  1162. if (num >= 0) {
  1163. a.push("0x" + num.toString(16).toLowerCase(), // probably pointless
  1164. "0" + num.toString(8)); // same.
  1165. } else {
  1166. a.push("-0x" + (-num).toString(16).toLowerCase(), // probably pointless
  1167. "-0" + (-num).toString(8)); // same.
  1168. }
  1169. if ((m = /^(.*?)(0+)$/.exec(num))) {
  1170. a.push(m[1] + "e" + m[2].length);
  1171. }
  1172. } else if ((m = /^0?\.(0+)(.*)$/.exec(num))) {
  1173. a.push(m[2] + "e-" + (m[1].length + m[2].length),
  1174. str.substr(str.indexOf(".")));
  1175. }
  1176. return best_of(a);
  1177. };
  1178. function make_block(stmt, output) {
  1179. if (stmt instanceof AST_BlockStatement) {
  1180. stmt.print(output);
  1181. return;
  1182. }
  1183. output.with_block(function(){
  1184. output.indent();
  1185. stmt.print(output);
  1186. output.newline();
  1187. });
  1188. };
  1189. /* -----[ source map generators ]----- */
  1190. function DEFMAP(nodetype, generator) {
  1191. nodetype.DEFMETHOD("add_source_map", function(stream){
  1192. generator(this, stream);
  1193. });
  1194. };
  1195. // We could easily add info for ALL nodes, but it seems to me that
  1196. // would be quite wasteful, hence this noop in the base class.
  1197. DEFMAP(AST_Node, noop);
  1198. function basic_sourcemap_gen(self, output) {
  1199. output.add_mapping(self.start);
  1200. };
  1201. // XXX: I'm not exactly sure if we need it for all of these nodes,
  1202. // or if we should add even more.
  1203. DEFMAP(AST_Directive, basic_sourcemap_gen);
  1204. DEFMAP(AST_Debugger, basic_sourcemap_gen);
  1205. DEFMAP(AST_Symbol, basic_sourcemap_gen);
  1206. DEFMAP(AST_Jump, basic_sourcemap_gen);
  1207. DEFMAP(AST_StatementWithBody, basic_sourcemap_gen);
  1208. DEFMAP(AST_LabeledStatement, noop); // since the label symbol will mark it
  1209. DEFMAP(AST_Lambda, basic_sourcemap_gen);
  1210. DEFMAP(AST_Switch, basic_sourcemap_gen);
  1211. DEFMAP(AST_SwitchBranch, basic_sourcemap_gen);
  1212. DEFMAP(AST_BlockStatement, basic_sourcemap_gen);
  1213. DEFMAP(AST_Toplevel, noop);
  1214. DEFMAP(AST_New, basic_sourcemap_gen);
  1215. DEFMAP(AST_Try, basic_sourcemap_gen);
  1216. DEFMAP(AST_Catch, basic_sourcemap_gen);
  1217. DEFMAP(AST_Finally, basic_sourcemap_gen);
  1218. DEFMAP(AST_Definitions, basic_sourcemap_gen);
  1219. DEFMAP(AST_Constant, basic_sourcemap_gen);
  1220. DEFMAP(AST_ObjectProperty, function(self, output){
  1221. output.add_mapping(self.start, self.key);
  1222. });
  1223. })();