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