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