You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

output.js 49KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508
  1. /***********************************************************************
  2. A JavaScript tokenizer / parser / beautifier / compressor.
  3. https://github.com/mishoo/UglifyJS
  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 is_some_comments(comment) {
  35. // multiline comment
  36. return comment.type == "comment2" && /@preserve|@license|@cc_on/i.test(comment.value);
  37. }
  38. function OutputStream(options) {
  39. var readonly = !options;
  40. options = defaults(options, {
  41. ascii_only : false,
  42. beautify : false,
  43. braces : false,
  44. comments : false,
  45. ie8 : false,
  46. indent_level : 4,
  47. indent_start : 0,
  48. inline_script : true,
  49. keep_quoted_props: false,
  50. max_line_len : false,
  51. preamble : null,
  52. preserve_line : false,
  53. quote_keys : false,
  54. quote_style : 0,
  55. semicolons : true,
  56. shebang : true,
  57. source_map : null,
  58. webkit : false,
  59. width : 80,
  60. wrap_iife : false,
  61. }, true);
  62. // Convert comment option to RegExp if neccessary and set up comments filter
  63. var comment_filter = return_false; // Default case, throw all comments away
  64. if (options.comments) {
  65. var comments = options.comments;
  66. if (typeof options.comments === "string" && /^\/.*\/[a-zA-Z]*$/.test(options.comments)) {
  67. var regex_pos = options.comments.lastIndexOf("/");
  68. comments = new RegExp(
  69. options.comments.substr(1, regex_pos - 1),
  70. options.comments.substr(regex_pos + 1)
  71. );
  72. }
  73. if (comments instanceof RegExp) {
  74. comment_filter = function(comment) {
  75. return comment.type != "comment5" && comments.test(comment.value);
  76. };
  77. } else if (typeof comments === "function") {
  78. comment_filter = function(comment) {
  79. return comment.type != "comment5" && comments(this, comment);
  80. };
  81. } else if (comments === "some") {
  82. comment_filter = is_some_comments;
  83. } else { // NOTE includes "all" option
  84. comment_filter = return_true;
  85. }
  86. }
  87. var indentation = 0;
  88. var current_col = 0;
  89. var current_line = 1;
  90. var current_pos = 0;
  91. var OUTPUT = "";
  92. var to_utf8 = options.ascii_only ? function(str, identifier) {
  93. return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) {
  94. var code = ch.charCodeAt(0).toString(16);
  95. if (code.length <= 2 && !identifier) {
  96. while (code.length < 2) code = "0" + code;
  97. return "\\x" + code;
  98. } else {
  99. while (code.length < 4) code = "0" + code;
  100. return "\\u" + code;
  101. }
  102. });
  103. } : function(str) {
  104. var s = "";
  105. for (var i = 0, j = 0; i < str.length; i++) {
  106. var code = str.charCodeAt(i);
  107. if (is_surrogate_pair_head(code)) {
  108. if (is_surrogate_pair_tail(str.charCodeAt(i + 1))) {
  109. i++;
  110. continue;
  111. }
  112. } else if (!is_surrogate_pair_tail(code)) {
  113. continue;
  114. }
  115. s += str.slice(j, i) + "\\u" + code.toString(16);
  116. j = i + 1;
  117. }
  118. return j == 0 ? str : s + str.slice(j);
  119. };
  120. function make_string(str, quote) {
  121. var dq = 0, sq = 0;
  122. str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s, i) {
  123. switch (s) {
  124. case '"': ++dq; return '"';
  125. case "'": ++sq; return "'";
  126. case "\\": return "\\\\";
  127. case "\n": return "\\n";
  128. case "\r": return "\\r";
  129. case "\t": return "\\t";
  130. case "\b": return "\\b";
  131. case "\f": return "\\f";
  132. case "\x0B": return options.ie8 ? "\\x0B" : "\\v";
  133. case "\u2028": return "\\u2028";
  134. case "\u2029": return "\\u2029";
  135. case "\ufeff": return "\\ufeff";
  136. case "\0":
  137. return /[0-9]/.test(str.charAt(i+1)) ? "\\x00" : "\\0";
  138. }
  139. return s;
  140. });
  141. function quote_single() {
  142. return "'" + str.replace(/\x27/g, "\\'") + "'";
  143. }
  144. function quote_double() {
  145. return '"' + str.replace(/\x22/g, '\\"') + '"';
  146. }
  147. str = to_utf8(str);
  148. switch (options.quote_style) {
  149. case 1:
  150. return quote_single();
  151. case 2:
  152. return quote_double();
  153. case 3:
  154. return quote == "'" ? quote_single() : quote_double();
  155. default:
  156. return dq > sq ? quote_single() : quote_double();
  157. }
  158. }
  159. function encode_string(str, quote) {
  160. var ret = make_string(str, quote);
  161. if (options.inline_script) {
  162. ret = ret.replace(/<\x2f(script)([>\/\t\n\f\r ])/gi, "<\\/$1$2");
  163. ret = ret.replace(/\x3c!--/g, "\\x3c!--");
  164. ret = ret.replace(/--\x3e/g, "--\\x3e");
  165. }
  166. return ret;
  167. }
  168. function make_name(name) {
  169. name = name.toString();
  170. name = to_utf8(name, true);
  171. return name;
  172. }
  173. function make_indent(back) {
  174. return repeat_string(" ", options.indent_start + indentation - back * options.indent_level);
  175. }
  176. /* -----[ beautification/minification ]----- */
  177. var has_parens = false;
  178. var line_end = 0;
  179. var line_fixed = true;
  180. var might_need_space = false;
  181. var might_need_semicolon = false;
  182. var need_newline_indented = false;
  183. var need_space = false;
  184. var newline_insert = -1;
  185. var last = "";
  186. var mapping_token, mapping_name, mappings = options.source_map && [];
  187. var adjust_mappings = mappings ? function(line, col) {
  188. mappings.forEach(function(mapping) {
  189. mapping.line += line;
  190. mapping.col += col;
  191. });
  192. } : noop;
  193. var flush_mappings = mappings ? function() {
  194. mappings.forEach(function(mapping) {
  195. options.source_map.add(
  196. mapping.token.file,
  197. mapping.line, mapping.col,
  198. mapping.token.line, mapping.token.col,
  199. !mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name
  200. );
  201. });
  202. mappings = [];
  203. } : noop;
  204. function insert_newlines(count) {
  205. var index = OUTPUT.lastIndexOf("\n");
  206. if (line_end < index) line_end = index;
  207. var left = OUTPUT.slice(0, line_end);
  208. var right = OUTPUT.slice(line_end);
  209. adjust_mappings(count, right.length - current_col);
  210. current_line += count;
  211. current_pos += count;
  212. current_col = right.length;
  213. OUTPUT = left;
  214. while (count--) OUTPUT += "\n";
  215. OUTPUT += right;
  216. }
  217. var fix_line = options.max_line_len ? function() {
  218. if (line_fixed) {
  219. if (current_col > options.max_line_len) {
  220. AST_Node.warn("Output exceeds {max_line_len} characters", options);
  221. }
  222. return;
  223. }
  224. if (current_col > options.max_line_len) insert_newlines(1);
  225. line_fixed = true;
  226. flush_mappings();
  227. } : noop;
  228. var requireSemicolonChars = makePredicate("( [ + * / - , .");
  229. function print(str) {
  230. str = String(str);
  231. var ch = str.charAt(0);
  232. if (need_newline_indented && ch) {
  233. need_newline_indented = false;
  234. if (ch != "\n") {
  235. print("\n");
  236. indent();
  237. }
  238. }
  239. if (need_space && ch) {
  240. need_space = false;
  241. if (!/[\s;})]/.test(ch)) {
  242. space();
  243. }
  244. }
  245. newline_insert = -1;
  246. var prev = last.slice(-1);
  247. if (might_need_semicolon) {
  248. might_need_semicolon = false;
  249. if (prev == ":" && ch == "}" || (!ch || ";}".indexOf(ch) < 0) && prev != ";") {
  250. if (options.semicolons || requireSemicolonChars[ch]) {
  251. OUTPUT += ";";
  252. current_col++;
  253. current_pos++;
  254. } else {
  255. fix_line();
  256. OUTPUT += "\n";
  257. current_pos++;
  258. current_line++;
  259. current_col = 0;
  260. if (/^\s+$/.test(str)) {
  261. // reset the semicolon flag, since we didn't print one
  262. // now and might still have to later
  263. might_need_semicolon = true;
  264. }
  265. }
  266. if (!options.beautify)
  267. might_need_space = false;
  268. }
  269. }
  270. if (might_need_space) {
  271. if (is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\")
  272. || (ch == "/" && ch == prev)
  273. || ((ch == "+" || ch == "-") && ch == last)
  274. || str == "--" && last == "!"
  275. || last == "--" && ch == ">") {
  276. OUTPUT += " ";
  277. current_col++;
  278. current_pos++;
  279. }
  280. if (prev != "<" || str != "!") might_need_space = false;
  281. }
  282. if (mapping_token) {
  283. mappings.push({
  284. token: mapping_token,
  285. name: mapping_name,
  286. line: current_line,
  287. col: current_col
  288. });
  289. mapping_token = false;
  290. if (line_fixed) flush_mappings();
  291. }
  292. OUTPUT += str;
  293. has_parens = str.slice(-1) == "(";
  294. current_pos += str.length;
  295. var a = str.split(/\r?\n/), n = a.length - 1;
  296. current_line += n;
  297. current_col += a[0].length;
  298. if (n > 0) {
  299. fix_line();
  300. current_col = a[n].length;
  301. }
  302. last = str;
  303. }
  304. var space = options.beautify ? function() {
  305. print(" ");
  306. } : function() {
  307. might_need_space = true;
  308. };
  309. var indent = options.beautify ? function(half) {
  310. if (options.beautify) {
  311. print(make_indent(half ? 0.5 : 0));
  312. }
  313. } : noop;
  314. var with_indent = options.beautify ? function(col, cont) {
  315. if (col === true) col = next_indent();
  316. var save_indentation = indentation;
  317. indentation = col;
  318. var ret = cont();
  319. indentation = save_indentation;
  320. return ret;
  321. } : function(col, cont) { return cont() };
  322. var may_add_newline = options.max_line_len || options.preserve_line ? function() {
  323. fix_line();
  324. line_end = OUTPUT.length;
  325. line_fixed = false;
  326. } : noop;
  327. var newline = options.beautify ? function() {
  328. if (newline_insert < 0) return print("\n");
  329. if (OUTPUT[newline_insert] != "\n") {
  330. OUTPUT = OUTPUT.slice(0, newline_insert) + "\n" + OUTPUT.slice(newline_insert);
  331. current_pos++;
  332. current_line++;
  333. }
  334. newline_insert++;
  335. } : may_add_newline;
  336. var semicolon = options.beautify ? function() {
  337. print(";");
  338. } : function() {
  339. might_need_semicolon = true;
  340. };
  341. function force_semicolon() {
  342. if (might_need_semicolon) print(";");
  343. print(";");
  344. }
  345. function next_indent() {
  346. return indentation + options.indent_level;
  347. }
  348. function with_block(cont) {
  349. var ret;
  350. print("{");
  351. newline();
  352. with_indent(next_indent(), function() {
  353. ret = cont();
  354. });
  355. indent();
  356. print("}");
  357. return ret;
  358. }
  359. function with_parens(cont) {
  360. print("(");
  361. may_add_newline();
  362. //XXX: still nice to have that for argument lists
  363. //var ret = with_indent(current_col, cont);
  364. var ret = cont();
  365. may_add_newline();
  366. print(")");
  367. return ret;
  368. }
  369. function with_square(cont) {
  370. print("[");
  371. may_add_newline();
  372. //var ret = with_indent(current_col, cont);
  373. var ret = cont();
  374. may_add_newline();
  375. print("]");
  376. return ret;
  377. }
  378. function comma() {
  379. may_add_newline();
  380. print(",");
  381. may_add_newline();
  382. space();
  383. }
  384. function colon() {
  385. print(":");
  386. space();
  387. }
  388. var add_mapping = mappings ? function(token, name) {
  389. mapping_token = token;
  390. mapping_name = name;
  391. } : noop;
  392. function get() {
  393. if (!line_fixed) fix_line();
  394. return OUTPUT;
  395. }
  396. function has_nlb() {
  397. var index = OUTPUT.lastIndexOf("\n");
  398. return /^ *$/.test(OUTPUT.slice(index + 1));
  399. }
  400. function prepend_comments(node) {
  401. var self = this;
  402. var scan = node instanceof AST_Exit && node.value;
  403. var comments = dump(node);
  404. if (!comments) comments = [];
  405. if (scan) {
  406. var tw = new TreeWalker(function(node) {
  407. var parent = tw.parent();
  408. if (parent instanceof AST_Exit
  409. || parent instanceof AST_Binary && parent.left === node
  410. || parent.TYPE == "Call" && parent.expression === node
  411. || parent instanceof AST_Conditional && parent.condition === node
  412. || parent instanceof AST_Dot && parent.expression === node
  413. || parent instanceof AST_Sequence && parent.expressions[0] === node
  414. || parent instanceof AST_Sub && parent.expression === node
  415. || parent instanceof AST_UnaryPostfix) {
  416. var before = dump(node);
  417. if (before) comments = comments.concat(before);
  418. } else {
  419. return true;
  420. }
  421. });
  422. tw.push(node);
  423. node.value.walk(tw);
  424. }
  425. if (current_pos == 0) {
  426. if (comments.length > 0 && options.shebang && comments[0].type == "comment5") {
  427. print("#!" + comments.shift().value + "\n");
  428. indent();
  429. }
  430. var preamble = options.preamble;
  431. if (preamble) {
  432. print(preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n"));
  433. }
  434. }
  435. comments = comments.filter(comment_filter, node);
  436. if (comments.length == 0) return;
  437. var last_nlb = has_nlb();
  438. comments.forEach(function(c, i) {
  439. if (!last_nlb) {
  440. if (c.nlb) {
  441. print("\n");
  442. indent();
  443. last_nlb = true;
  444. } else if (i > 0) {
  445. space();
  446. }
  447. }
  448. if (/comment[134]/.test(c.type)) {
  449. print("//" + c.value.replace(/[@#]__PURE__/g, ' ') + "\n");
  450. indent();
  451. last_nlb = true;
  452. } else if (c.type == "comment2") {
  453. print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/");
  454. last_nlb = false;
  455. }
  456. });
  457. if (!last_nlb) {
  458. if (node.start.nlb) {
  459. print("\n");
  460. indent();
  461. } else {
  462. space();
  463. }
  464. }
  465. function dump(node) {
  466. var token = node.start;
  467. if (!token) {
  468. if (!scan) return;
  469. node.start = token = new AST_Token();
  470. }
  471. var comments = token.comments_before;
  472. if (!comments) {
  473. if (!scan) return;
  474. token.comments_before = comments = [];
  475. }
  476. if (comments._dumped === self) return;
  477. comments._dumped = self;
  478. return comments;
  479. }
  480. }
  481. function append_comments(node, tail) {
  482. var self = this;
  483. var token = node.end;
  484. if (!token) return;
  485. var comments = token[tail ? "comments_before" : "comments_after"];
  486. if (!comments || comments._dumped === self) return;
  487. if (!(node instanceof AST_Statement || all(comments, function(c) {
  488. return !/comment[134]/.test(c.type);
  489. }))) return;
  490. comments._dumped = self;
  491. var insert = OUTPUT.length;
  492. comments.filter(comment_filter, node).forEach(function(c, i) {
  493. need_space = false;
  494. if (need_newline_indented) {
  495. print("\n");
  496. indent();
  497. need_newline_indented = false;
  498. } else if (c.nlb && (i > 0 || !has_nlb())) {
  499. print("\n");
  500. indent();
  501. } else if (i > 0 || !tail) {
  502. space();
  503. }
  504. if (/comment[134]/.test(c.type)) {
  505. print("//" + c.value.replace(/[@#]__PURE__/g, ' '));
  506. need_newline_indented = true;
  507. } else if (c.type == "comment2") {
  508. print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/");
  509. need_space = true;
  510. }
  511. });
  512. if (OUTPUT.length > insert) newline_insert = insert;
  513. }
  514. var stack = [];
  515. return {
  516. get : get,
  517. toString : get,
  518. indent : indent,
  519. indentation : function() { return indentation },
  520. current_width : function() { return current_col - indentation },
  521. should_break : function() { return options.width && this.current_width() >= options.width },
  522. has_parens : function() { return has_parens },
  523. newline : newline,
  524. print : print,
  525. space : space,
  526. comma : comma,
  527. colon : colon,
  528. last : function() { return last },
  529. semicolon : semicolon,
  530. force_semicolon : force_semicolon,
  531. to_utf8 : to_utf8,
  532. print_name : function(name) { print(make_name(name)) },
  533. print_string : function(str, quote) { print(encode_string(str, quote)) },
  534. next_indent : next_indent,
  535. with_indent : with_indent,
  536. with_block : with_block,
  537. with_parens : with_parens,
  538. with_square : with_square,
  539. add_mapping : add_mapping,
  540. option : function(opt) { return options[opt] },
  541. prepend_comments: readonly ? noop : prepend_comments,
  542. append_comments : readonly || comment_filter === return_false ? noop : append_comments,
  543. line : function() { return current_line },
  544. col : function() { return current_col },
  545. pos : function() { return current_pos },
  546. push_node : function(node) { stack.push(node) },
  547. pop_node : options.preserve_line ? function() {
  548. var node = stack.pop();
  549. if (node.start && node.start.line > current_line) {
  550. insert_newlines(node.start.line - current_line);
  551. }
  552. } : function() {
  553. stack.pop();
  554. },
  555. parent : function(n) {
  556. return stack[stack.length - 2 - (n || 0)];
  557. }
  558. };
  559. }
  560. /* -----[ code generators ]----- */
  561. (function() {
  562. /* -----[ utils ]----- */
  563. function DEFPRINT(nodetype, generator) {
  564. nodetype.DEFMETHOD("_codegen", generator);
  565. }
  566. var use_asm = false;
  567. AST_Node.DEFMETHOD("print", function(stream, force_parens) {
  568. var self = this, generator = self._codegen;
  569. function doit() {
  570. stream.prepend_comments(self);
  571. self.add_source_map(stream);
  572. generator(self, stream);
  573. stream.append_comments(self);
  574. }
  575. stream.push_node(self);
  576. if (force_parens || self.needs_parens(stream)) {
  577. stream.with_parens(doit);
  578. } else {
  579. doit();
  580. }
  581. stream.pop_node();
  582. });
  583. AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);
  584. AST_Node.DEFMETHOD("print_to_string", function(options) {
  585. var s = OutputStream(options);
  586. this.print(s);
  587. return s.get();
  588. });
  589. /* -----[ PARENTHESES ]----- */
  590. function PARENS(nodetype, func) {
  591. if (Array.isArray(nodetype)) {
  592. nodetype.forEach(function(nodetype) {
  593. PARENS(nodetype, func);
  594. });
  595. } else {
  596. nodetype.DEFMETHOD("needs_parens", func);
  597. }
  598. }
  599. PARENS(AST_Node, return_false);
  600. // a function expression needs parens around it when it's provably
  601. // the first token to appear in a statement.
  602. PARENS(AST_Function, function(output) {
  603. if (!output.has_parens() && first_in_statement(output)) return true;
  604. if (output.option('webkit')) {
  605. var p = output.parent();
  606. if (p instanceof AST_PropAccess && p.expression === this) return true;
  607. }
  608. if (output.option('wrap_iife')) {
  609. var p = output.parent();
  610. if (p instanceof AST_Call && p.expression === this) return true;
  611. }
  612. });
  613. // same goes for an object literal, because otherwise it would be
  614. // interpreted as a block of code.
  615. PARENS(AST_Object, function(output) {
  616. return !output.has_parens() && first_in_statement(output);
  617. });
  618. PARENS(AST_Unary, function(output) {
  619. var p = output.parent();
  620. return p instanceof AST_PropAccess && p.expression === this
  621. || p instanceof AST_Call && p.expression === this;
  622. });
  623. PARENS(AST_Sequence, function(output) {
  624. var p = output.parent();
  625. // (foo, bar)() or foo(1, (2, 3), 4)
  626. return p instanceof AST_Call
  627. // !(foo, bar, baz)
  628. || p instanceof AST_Unary
  629. // 1 + (2, 3) + 4 ==> 8
  630. || p instanceof AST_Binary
  631. // var a = (1, 2), b = a + a; ==> b == 4
  632. || p instanceof AST_VarDef
  633. // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
  634. || p instanceof AST_PropAccess && p.expression === this
  635. // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
  636. || p instanceof AST_Array
  637. // { foo: (1, 2) }.foo ==> 2
  638. || p instanceof AST_ObjectProperty
  639. // (false, true) ? (a = 10, b = 20) : (c = 30)
  640. // ==> 20 (side effect, set a := 10 and b := 20)
  641. || p instanceof AST_Conditional;
  642. });
  643. PARENS(AST_Binary, function(output) {
  644. var p = output.parent();
  645. // (foo && bar)()
  646. if (p instanceof AST_Call && p.expression === this)
  647. return true;
  648. // typeof (foo && bar)
  649. if (p instanceof AST_Unary)
  650. return true;
  651. // (foo && bar)["prop"], (foo && bar).prop
  652. if (p instanceof AST_PropAccess && p.expression === this)
  653. return true;
  654. // this deals with precedence: 3 * (2 + 1)
  655. if (p instanceof AST_Binary) {
  656. var po = p.operator, pp = PRECEDENCE[po];
  657. var so = this.operator, sp = PRECEDENCE[so];
  658. if (pp > sp
  659. || (pp == sp
  660. && this === p.right)) {
  661. return true;
  662. }
  663. }
  664. });
  665. PARENS(AST_PropAccess, function(output) {
  666. var p = output.parent();
  667. if (p instanceof AST_New && p.expression === this) {
  668. // i.e. new (foo.bar().baz)
  669. //
  670. // if there's one call into this subtree, then we need
  671. // parens around it too, otherwise the call will be
  672. // interpreted as passing the arguments to the upper New
  673. // expression.
  674. var parens = false;
  675. this.walk(new TreeWalker(function(node) {
  676. if (parens || node instanceof AST_Scope) return true;
  677. if (node instanceof AST_Call) {
  678. parens = true;
  679. return true;
  680. }
  681. }));
  682. return parens;
  683. }
  684. });
  685. PARENS(AST_Call, function(output) {
  686. var p = output.parent();
  687. if (p instanceof AST_New && p.expression === this) return true;
  688. // https://bugs.webkit.org/show_bug.cgi?id=123506
  689. if (output.option('webkit')) {
  690. var g = output.parent(1);
  691. return this.expression instanceof AST_Function
  692. && p instanceof AST_PropAccess
  693. && p.expression === this
  694. && g instanceof AST_Assign
  695. && g.left === p;
  696. }
  697. });
  698. PARENS(AST_New, function(output) {
  699. var p = output.parent();
  700. if (!need_constructor_parens(this, output)
  701. && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
  702. || p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
  703. return true;
  704. });
  705. PARENS(AST_Number, function(output) {
  706. var p = output.parent();
  707. if (p instanceof AST_PropAccess && p.expression === this) {
  708. var value = this.value;
  709. // https://github.com/mishoo/UglifyJS/issues/115
  710. // https://github.com/mishoo/UglifyJS/pull/1009
  711. if (value < 0 || /^0/.test(make_num(value))) {
  712. return true;
  713. }
  714. }
  715. });
  716. PARENS([ AST_Assign, AST_Conditional ], function(output) {
  717. var p = output.parent();
  718. // !(a = false) → true
  719. if (p instanceof AST_Unary)
  720. return true;
  721. // 1 + (a = 2) + 3 → 6, side effect setting a = 2
  722. if (p instanceof AST_Binary && !(p instanceof AST_Assign))
  723. return true;
  724. // (a = func)() —or— new (a = Object)()
  725. if (p instanceof AST_Call && p.expression === this)
  726. return true;
  727. // (a = foo) ? bar : baz
  728. if (p instanceof AST_Conditional && p.condition === this)
  729. return true;
  730. // (a = foo)["prop"] —or— (a = foo).prop
  731. if (p instanceof AST_PropAccess && p.expression === this)
  732. return true;
  733. });
  734. /* -----[ PRINTERS ]----- */
  735. DEFPRINT(AST_Directive, function(self, output) {
  736. var quote = self.quote;
  737. var value = self.value;
  738. switch (output.option("quote_style")) {
  739. case 0:
  740. case 2:
  741. if (value.indexOf('"') == -1) quote = '"';
  742. break;
  743. case 1:
  744. if (value.indexOf("'") == -1) quote = "'";
  745. break;
  746. }
  747. output.print(quote + value + quote);
  748. output.semicolon();
  749. });
  750. DEFPRINT(AST_Debugger, function(self, output) {
  751. output.print("debugger");
  752. output.semicolon();
  753. });
  754. /* -----[ statements ]----- */
  755. function display_body(body, is_toplevel, output, allow_directives) {
  756. var last = body.length - 1;
  757. var in_directive = allow_directives;
  758. var was_asm = use_asm;
  759. body.forEach(function(stmt, i) {
  760. if (in_directive) {
  761. if (stmt instanceof AST_Directive) {
  762. if (stmt.value == "use asm") use_asm = true;
  763. } else if (!(stmt instanceof AST_EmptyStatement)) {
  764. if (stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String) {
  765. output.force_semicolon();
  766. }
  767. in_directive = false;
  768. }
  769. }
  770. if (stmt instanceof AST_EmptyStatement) return;
  771. output.indent();
  772. stmt.print(output);
  773. if (i == last && is_toplevel) return;
  774. output.newline();
  775. if (is_toplevel) output.newline();
  776. });
  777. use_asm = was_asm;
  778. }
  779. AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output) {
  780. force_statement(this.body, output);
  781. });
  782. DEFPRINT(AST_Statement, function(self, output) {
  783. self.body.print(output);
  784. output.semicolon();
  785. });
  786. DEFPRINT(AST_Toplevel, function(self, output) {
  787. display_body(self.body, true, output, true);
  788. output.print("");
  789. });
  790. DEFPRINT(AST_LabeledStatement, function(self, output) {
  791. self.label.print(output);
  792. output.colon();
  793. self.body.print(output);
  794. });
  795. DEFPRINT(AST_SimpleStatement, function(self, output) {
  796. self.body.print(output);
  797. output.semicolon();
  798. });
  799. function print_braced_empty(self, output) {
  800. output.print("{");
  801. output.with_indent(output.next_indent(), function() {
  802. output.append_comments(self, true);
  803. });
  804. output.print("}");
  805. }
  806. function print_braced(self, output, allow_directives) {
  807. if (self.body.length > 0) {
  808. output.with_block(function() {
  809. display_body(self.body, false, output, allow_directives);
  810. });
  811. } else print_braced_empty(self, output);
  812. }
  813. DEFPRINT(AST_BlockStatement, function(self, output) {
  814. print_braced(self, output);
  815. });
  816. DEFPRINT(AST_EmptyStatement, function(self, output) {
  817. output.semicolon();
  818. });
  819. DEFPRINT(AST_Do, function(self, output) {
  820. output.print("do");
  821. output.space();
  822. make_block(self.body, output);
  823. output.space();
  824. output.print("while");
  825. output.space();
  826. output.with_parens(function() {
  827. self.condition.print(output);
  828. });
  829. output.semicolon();
  830. });
  831. DEFPRINT(AST_While, function(self, output) {
  832. output.print("while");
  833. output.space();
  834. output.with_parens(function() {
  835. self.condition.print(output);
  836. });
  837. output.space();
  838. self._do_print_body(output);
  839. });
  840. DEFPRINT(AST_For, function(self, output) {
  841. output.print("for");
  842. output.space();
  843. output.with_parens(function() {
  844. if (self.init) {
  845. if (self.init instanceof AST_Definitions) {
  846. self.init.print(output);
  847. } else {
  848. parenthesize_for_noin(self.init, output, true);
  849. }
  850. output.print(";");
  851. output.space();
  852. } else {
  853. output.print(";");
  854. }
  855. if (self.condition) {
  856. self.condition.print(output);
  857. output.print(";");
  858. output.space();
  859. } else {
  860. output.print(";");
  861. }
  862. if (self.step) {
  863. self.step.print(output);
  864. }
  865. });
  866. output.space();
  867. self._do_print_body(output);
  868. });
  869. DEFPRINT(AST_ForIn, function(self, output) {
  870. output.print("for");
  871. output.space();
  872. output.with_parens(function() {
  873. self.init.print(output);
  874. output.space();
  875. output.print("in");
  876. output.space();
  877. self.object.print(output);
  878. });
  879. output.space();
  880. self._do_print_body(output);
  881. });
  882. DEFPRINT(AST_With, function(self, output) {
  883. output.print("with");
  884. output.space();
  885. output.with_parens(function() {
  886. self.expression.print(output);
  887. });
  888. output.space();
  889. self._do_print_body(output);
  890. });
  891. /* -----[ functions ]----- */
  892. AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword) {
  893. var self = this;
  894. if (!nokeyword) {
  895. output.print("function");
  896. }
  897. if (self.name) {
  898. output.space();
  899. self.name.print(output);
  900. }
  901. output.with_parens(function() {
  902. self.argnames.forEach(function(arg, i) {
  903. if (i) output.comma();
  904. arg.print(output);
  905. });
  906. });
  907. output.space();
  908. print_braced(self, output, true);
  909. });
  910. DEFPRINT(AST_Lambda, function(self, output) {
  911. self._do_print(output);
  912. });
  913. /* -----[ jumps ]----- */
  914. function print_jump(output, kind, target) {
  915. output.print(kind);
  916. if (target) {
  917. output.space();
  918. target.print(output);
  919. }
  920. output.semicolon();
  921. }
  922. DEFPRINT(AST_Return, function(self, output) {
  923. print_jump(output, "return", self.value);
  924. });
  925. DEFPRINT(AST_Throw, function(self, output) {
  926. print_jump(output, "throw", self.value);
  927. });
  928. DEFPRINT(AST_Break, function(self, output) {
  929. print_jump(output, "break", self.label);
  930. });
  931. DEFPRINT(AST_Continue, function(self, output) {
  932. print_jump(output, "continue", self.label);
  933. });
  934. /* -----[ if ]----- */
  935. function make_then(self, output) {
  936. var b = self.body;
  937. if (output.option("braces")
  938. || output.option("ie8") && b instanceof AST_Do)
  939. return make_block(b, output);
  940. // The squeezer replaces "block"-s that contain only a single
  941. // statement with the statement itself; technically, the AST
  942. // is correct, but this can create problems when we output an
  943. // IF having an ELSE clause where the THEN clause ends in an
  944. // IF *without* an ELSE block (then the outer ELSE would refer
  945. // to the inner IF). This function checks for this case and
  946. // adds the block braces if needed.
  947. if (!b) return output.force_semicolon();
  948. while (true) {
  949. if (b instanceof AST_If) {
  950. if (!b.alternative) {
  951. make_block(self.body, output);
  952. return;
  953. }
  954. b = b.alternative;
  955. } else if (b instanceof AST_StatementWithBody) {
  956. b = b.body;
  957. } else break;
  958. }
  959. force_statement(self.body, output);
  960. }
  961. DEFPRINT(AST_If, function(self, output) {
  962. output.print("if");
  963. output.space();
  964. output.with_parens(function() {
  965. self.condition.print(output);
  966. });
  967. output.space();
  968. if (self.alternative) {
  969. make_then(self, output);
  970. output.space();
  971. output.print("else");
  972. output.space();
  973. if (self.alternative instanceof AST_If)
  974. self.alternative.print(output);
  975. else
  976. force_statement(self.alternative, output);
  977. } else {
  978. self._do_print_body(output);
  979. }
  980. });
  981. /* -----[ switch ]----- */
  982. DEFPRINT(AST_Switch, function(self, output) {
  983. output.print("switch");
  984. output.space();
  985. output.with_parens(function() {
  986. self.expression.print(output);
  987. });
  988. output.space();
  989. var last = self.body.length - 1;
  990. if (last < 0) print_braced_empty(self, output);
  991. else output.with_block(function() {
  992. self.body.forEach(function(branch, i) {
  993. output.indent(true);
  994. branch.print(output);
  995. if (i < last && branch.body.length > 0)
  996. output.newline();
  997. });
  998. });
  999. });
  1000. AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output) {
  1001. output.newline();
  1002. this.body.forEach(function(stmt) {
  1003. output.indent();
  1004. stmt.print(output);
  1005. output.newline();
  1006. });
  1007. });
  1008. DEFPRINT(AST_Default, function(self, output) {
  1009. output.print("default:");
  1010. self._do_print_body(output);
  1011. });
  1012. DEFPRINT(AST_Case, function(self, output) {
  1013. output.print("case");
  1014. output.space();
  1015. self.expression.print(output);
  1016. output.print(":");
  1017. self._do_print_body(output);
  1018. });
  1019. /* -----[ exceptions ]----- */
  1020. DEFPRINT(AST_Try, function(self, output) {
  1021. output.print("try");
  1022. output.space();
  1023. print_braced(self, output);
  1024. if (self.bcatch) {
  1025. output.space();
  1026. self.bcatch.print(output);
  1027. }
  1028. if (self.bfinally) {
  1029. output.space();
  1030. self.bfinally.print(output);
  1031. }
  1032. });
  1033. DEFPRINT(AST_Catch, function(self, output) {
  1034. output.print("catch");
  1035. output.space();
  1036. output.with_parens(function() {
  1037. self.argname.print(output);
  1038. });
  1039. output.space();
  1040. print_braced(self, output);
  1041. });
  1042. DEFPRINT(AST_Finally, function(self, output) {
  1043. output.print("finally");
  1044. output.space();
  1045. print_braced(self, output);
  1046. });
  1047. DEFPRINT(AST_Var, function(self, output) {
  1048. output.print("var");
  1049. output.space();
  1050. self.definitions.forEach(function(def, i) {
  1051. if (i) output.comma();
  1052. def.print(output);
  1053. });
  1054. var p = output.parent();
  1055. if (p && p.init !== self || !(p instanceof AST_For || p instanceof AST_ForIn)) output.semicolon();
  1056. });
  1057. function parenthesize_for_noin(node, output, noin) {
  1058. var parens = false;
  1059. // need to take some precautions here:
  1060. // https://github.com/mishoo/UglifyJS/issues/60
  1061. if (noin) node.walk(new TreeWalker(function(node) {
  1062. if (parens || node instanceof AST_Scope) return true;
  1063. if (node instanceof AST_Binary && node.operator == "in") {
  1064. parens = true;
  1065. return true;
  1066. }
  1067. }));
  1068. node.print(output, parens);
  1069. }
  1070. DEFPRINT(AST_VarDef, function(self, output) {
  1071. self.name.print(output);
  1072. if (self.value) {
  1073. output.space();
  1074. output.print("=");
  1075. output.space();
  1076. var p = output.parent(1);
  1077. var noin = p instanceof AST_For || p instanceof AST_ForIn;
  1078. parenthesize_for_noin(self.value, output, noin);
  1079. }
  1080. });
  1081. /* -----[ other expressions ]----- */
  1082. DEFPRINT(AST_Call, function(self, output) {
  1083. self.expression.print(output);
  1084. if (self instanceof AST_New && !need_constructor_parens(self, output))
  1085. return;
  1086. if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
  1087. output.add_mapping(self.start);
  1088. }
  1089. output.with_parens(function() {
  1090. self.args.forEach(function(expr, i) {
  1091. if (i) output.comma();
  1092. expr.print(output);
  1093. });
  1094. });
  1095. });
  1096. DEFPRINT(AST_New, function(self, output) {
  1097. output.print("new");
  1098. output.space();
  1099. AST_Call.prototype._codegen(self, output);
  1100. });
  1101. DEFPRINT(AST_Sequence, function(self, output) {
  1102. self.expressions.forEach(function(node, index) {
  1103. if (index > 0) {
  1104. output.comma();
  1105. if (output.should_break()) {
  1106. output.newline();
  1107. output.indent();
  1108. }
  1109. }
  1110. node.print(output);
  1111. });
  1112. });
  1113. DEFPRINT(AST_Dot, function(self, output) {
  1114. var expr = self.expression;
  1115. expr.print(output);
  1116. var prop = self.property;
  1117. if (output.option("ie8") && RESERVED_WORDS[prop]) {
  1118. output.print("[");
  1119. output.add_mapping(self.end);
  1120. output.print_string(prop);
  1121. output.print("]");
  1122. } else {
  1123. if (expr instanceof AST_Number && expr.value >= 0) {
  1124. if (!/[xa-f.)]/i.test(output.last())) {
  1125. output.print(".");
  1126. }
  1127. }
  1128. output.print(".");
  1129. // the name after dot would be mapped about here.
  1130. output.add_mapping(self.end);
  1131. output.print_name(prop);
  1132. }
  1133. });
  1134. DEFPRINT(AST_Sub, function(self, output) {
  1135. self.expression.print(output);
  1136. output.print("[");
  1137. self.property.print(output);
  1138. output.print("]");
  1139. });
  1140. DEFPRINT(AST_UnaryPrefix, function(self, output) {
  1141. var op = self.operator;
  1142. output.print(op);
  1143. if (/^[a-z]/i.test(op)
  1144. || (/[+-]$/.test(op)
  1145. && self.expression instanceof AST_UnaryPrefix
  1146. && /^[+-]/.test(self.expression.operator))) {
  1147. output.space();
  1148. }
  1149. self.expression.print(output);
  1150. });
  1151. DEFPRINT(AST_UnaryPostfix, function(self, output) {
  1152. self.expression.print(output);
  1153. output.print(self.operator);
  1154. });
  1155. DEFPRINT(AST_Binary, function(self, output) {
  1156. self.left.print(output);
  1157. output.space();
  1158. output.print(self.operator);
  1159. output.space();
  1160. self.right.print(output);
  1161. });
  1162. DEFPRINT(AST_Conditional, function(self, output) {
  1163. self.condition.print(output);
  1164. output.space();
  1165. output.print("?");
  1166. output.space();
  1167. self.consequent.print(output);
  1168. output.space();
  1169. output.colon();
  1170. self.alternative.print(output);
  1171. });
  1172. /* -----[ literals ]----- */
  1173. DEFPRINT(AST_Array, function(self, output) {
  1174. output.with_square(function() {
  1175. var a = self.elements, len = a.length;
  1176. if (len > 0) output.space();
  1177. a.forEach(function(exp, i) {
  1178. if (i) output.comma();
  1179. exp.print(output);
  1180. // If the final element is a hole, we need to make sure it
  1181. // doesn't look like a trailing comma, by inserting an actual
  1182. // trailing comma.
  1183. if (i === len - 1 && exp instanceof AST_Hole)
  1184. output.comma();
  1185. });
  1186. if (len > 0) output.space();
  1187. });
  1188. });
  1189. DEFPRINT(AST_Object, function(self, output) {
  1190. if (self.properties.length > 0) output.with_block(function() {
  1191. self.properties.forEach(function(prop, i) {
  1192. if (i) {
  1193. output.print(",");
  1194. output.newline();
  1195. }
  1196. output.indent();
  1197. prop.print(output);
  1198. });
  1199. output.newline();
  1200. });
  1201. else print_braced_empty(self, output);
  1202. });
  1203. function print_property_name(key, quote, output) {
  1204. if (output.option("quote_keys")) {
  1205. output.print_string(key);
  1206. } else if ("" + +key == key && key >= 0) {
  1207. output.print(make_num(key));
  1208. } else if (RESERVED_WORDS[key] ? !output.option("ie8") : is_identifier_string(key)) {
  1209. if (quote && output.option("keep_quoted_props")) {
  1210. output.print_string(key, quote);
  1211. } else {
  1212. output.print_name(key);
  1213. }
  1214. } else {
  1215. output.print_string(key, quote);
  1216. }
  1217. }
  1218. DEFPRINT(AST_ObjectKeyVal, function(self, output) {
  1219. print_property_name(self.key, self.quote, output);
  1220. output.colon();
  1221. self.value.print(output);
  1222. });
  1223. AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) {
  1224. output.print(type);
  1225. output.space();
  1226. print_property_name(this.key.name, this.quote, output);
  1227. this.value._do_print(output, true);
  1228. });
  1229. DEFPRINT(AST_ObjectSetter, function(self, output) {
  1230. self._print_getter_setter("set", output);
  1231. });
  1232. DEFPRINT(AST_ObjectGetter, function(self, output) {
  1233. self._print_getter_setter("get", output);
  1234. });
  1235. DEFPRINT(AST_Symbol, function(self, output) {
  1236. var def = self.definition();
  1237. output.print_name(def && def.mangled_name || self.name);
  1238. });
  1239. DEFPRINT(AST_Hole, noop);
  1240. DEFPRINT(AST_This, function(self, output) {
  1241. output.print("this");
  1242. });
  1243. DEFPRINT(AST_Constant, function(self, output) {
  1244. output.print(self.value);
  1245. });
  1246. DEFPRINT(AST_String, function(self, output) {
  1247. output.print_string(self.value, self.quote);
  1248. });
  1249. DEFPRINT(AST_Number, function(self, output) {
  1250. if (use_asm && self.start && self.start.raw != null) {
  1251. output.print(self.start.raw);
  1252. } else {
  1253. output.print(make_num(self.value));
  1254. }
  1255. });
  1256. DEFPRINT(AST_RegExp, function(self, output) {
  1257. var regexp = self.value;
  1258. var str = regexp.toString();
  1259. var end = str.lastIndexOf("/");
  1260. if (regexp.raw_source) {
  1261. str = "/" + regexp.raw_source + str.slice(end);
  1262. } else if (end == 1) {
  1263. str = "/(?:)" + str.slice(end);
  1264. } else if (str.indexOf("/", 1) < end) {
  1265. str = "/" + str.slice(1, end).replace(/\\\\|[^/]?\//g, function(match) {
  1266. return match[0] == "\\" ? match : match.slice(0, -1) + "\\/";
  1267. }) + str.slice(end);
  1268. }
  1269. output.print(output.to_utf8(str).replace(/\\(?:\0(?![0-9])|[^\0])/g, function(match) {
  1270. switch (match[1]) {
  1271. case "\n": return "\\n";
  1272. case "\r": return "\\r";
  1273. case "\t": return "\t";
  1274. case "\b": return "\b";
  1275. case "\f": return "\f";
  1276. case "\0": return "\0";
  1277. case "\x0B": return "\v";
  1278. case "\u2028": return "\\u2028";
  1279. case "\u2029": return "\\u2029";
  1280. default: return match;
  1281. }
  1282. }).replace(/[\n\r\u2028\u2029]/g, function(c) {
  1283. switch (c) {
  1284. case "\n": return "\\n";
  1285. case "\r": return "\\r";
  1286. case "\u2028": return "\\u2028";
  1287. case "\u2029": return "\\u2029";
  1288. }
  1289. }));
  1290. var p = output.parent();
  1291. if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
  1292. output.print(" ");
  1293. });
  1294. function force_statement(stat, output) {
  1295. if (output.option("braces")) {
  1296. make_block(stat, output);
  1297. } else {
  1298. if (!stat || stat instanceof AST_EmptyStatement)
  1299. output.force_semicolon();
  1300. else
  1301. stat.print(output);
  1302. }
  1303. }
  1304. // self should be AST_New. decide if we want to show parens or not.
  1305. function need_constructor_parens(self, output) {
  1306. // Always print parentheses with arguments
  1307. if (self.args.length > 0) return true;
  1308. return output.option("beautify");
  1309. }
  1310. function best_of(a) {
  1311. var best = a[0], len = best.length;
  1312. for (var i = 1; i < a.length; ++i) {
  1313. if (a[i].length < len) {
  1314. best = a[i];
  1315. len = best.length;
  1316. }
  1317. }
  1318. return best;
  1319. }
  1320. function make_num(num) {
  1321. var str = num.toString(10).replace(/^0\./, ".").replace("e+", "e");
  1322. var candidates = [ str ];
  1323. if (Math.floor(num) === num) {
  1324. if (num < 0) {
  1325. candidates.push("-0x" + (-num).toString(16).toLowerCase());
  1326. } else {
  1327. candidates.push("0x" + num.toString(16).toLowerCase());
  1328. }
  1329. }
  1330. var match, len, digits;
  1331. if (match = /^\.0+/.exec(str)) {
  1332. len = match[0].length;
  1333. digits = str.slice(len);
  1334. candidates.push(digits + "e-" + (digits.length + len - 1));
  1335. } else if (match = /0+$/.exec(str)) {
  1336. len = match[0].length;
  1337. candidates.push(str.slice(0, -len) + "e" + len);
  1338. } else if (match = /^(\d)\.(\d+)e(-?\d+)$/.exec(str)) {
  1339. candidates.push(match[1] + match[2] + "e" + (match[3] - match[2].length));
  1340. }
  1341. return best_of(candidates);
  1342. }
  1343. function make_block(stmt, output) {
  1344. if (!stmt || stmt instanceof AST_EmptyStatement)
  1345. output.print("{}");
  1346. else if (stmt instanceof AST_BlockStatement)
  1347. stmt.print(output);
  1348. else output.with_block(function() {
  1349. output.indent();
  1350. stmt.print(output);
  1351. output.newline();
  1352. });
  1353. }
  1354. /* -----[ source map generators ]----- */
  1355. function DEFMAP(nodetype, generator) {
  1356. nodetype.forEach(function(nodetype) {
  1357. nodetype.DEFMETHOD("add_source_map", generator);
  1358. });
  1359. }
  1360. DEFMAP([
  1361. // We could easily add info for ALL nodes, but it seems to me that
  1362. // would be quite wasteful, hence this noop in the base class.
  1363. AST_Node,
  1364. // since the label symbol will mark it
  1365. AST_LabeledStatement,
  1366. ], noop);
  1367. // XXX: I'm not exactly sure if we need it for all of these nodes,
  1368. // or if we should add even more.
  1369. DEFMAP([
  1370. AST_Array,
  1371. AST_BlockStatement,
  1372. AST_Catch,
  1373. AST_Constant,
  1374. AST_Debugger,
  1375. AST_Definitions,
  1376. AST_Finally,
  1377. AST_Jump,
  1378. AST_Lambda,
  1379. AST_New,
  1380. AST_Object,
  1381. AST_StatementWithBody,
  1382. AST_Symbol,
  1383. AST_Switch,
  1384. AST_SwitchBranch,
  1385. AST_Try,
  1386. ], function(output) {
  1387. output.add_mapping(this.start);
  1388. });
  1389. DEFMAP([
  1390. AST_ObjectGetter,
  1391. AST_ObjectSetter,
  1392. ], function(output) {
  1393. output.add_mapping(this.start, this.key.name);
  1394. });
  1395. DEFMAP([ AST_ObjectProperty ], function(output) {
  1396. output.add_mapping(this.start, this.key);
  1397. });
  1398. })();