UNPKG

29.6 kBJavaScriptView Raw
1/***********************************************************************
2
3 A JavaScript tokenizer / parser / beautifier / compressor.
4 https://github.com/mishoo/UglifyJS
5
6 -------------------------------- (C) ---------------------------------
7
8 Author: Mihai Bazon
9 <mihai.bazon@gmail.com>
10 http://mihai.bazon.net/blog
11
12 Distributed under the BSD license:
13
14 Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
15
16 Redistribution and use in source and binary forms, with or without
17 modification, are permitted provided that the following conditions
18 are met:
19
20 * Redistributions of source code must retain the above
21 copyright notice, this list of conditions and the following
22 disclaimer.
23
24 * Redistributions in binary form must reproduce the above
25 copyright notice, this list of conditions and the following
26 disclaimer in the documentation and/or other materials
27 provided with the distribution.
28
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
30 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
33 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
34 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
38 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
39 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 SUCH DAMAGE.
41
42 ***********************************************************************/
43
44"use strict";
45
46function SymbolDef(id, scope, orig, init) {
47 this.eliminated = 0;
48 this.exported = false;
49 this.global = false;
50 this.id = id;
51 this.init = init;
52 this.mangled_name = null;
53 this.name = orig.name;
54 this.orig = [ orig ];
55 this.references = [];
56 this.replaced = 0;
57 this.scope = scope;
58 this.undeclared = false;
59}
60
61SymbolDef.prototype = {
62 forEach: function(fn) {
63 this.orig.forEach(fn);
64 this.references.forEach(fn);
65 },
66 mangle: function(options) {
67 var cache = options.cache && options.cache.props;
68 if (this.global && cache && cache.has(this.name)) {
69 this.mangled_name = cache.get(this.name);
70 } else if (!this.mangled_name && !this.unmangleable(options)) {
71 var def = this.redefined();
72 if (def) {
73 this.mangled_name = def.mangled_name || def.name;
74 } else {
75 this.mangled_name = next_mangled_name(this, options);
76 }
77 if (this.global && cache) {
78 cache.set(this.name, this.mangled_name);
79 }
80 }
81 },
82 redefined: function() {
83 var self = this;
84 var scope = self.defun;
85 if (!scope) return;
86 var name = self.name;
87 var def = scope.variables.get(name)
88 || scope instanceof AST_Toplevel && scope.globals.get(name)
89 || self.orig[0] instanceof AST_SymbolConst && find_if(function(def) {
90 return def.name == name;
91 }, scope.enclosed);
92 if (def && def !== self) return def.redefined() || def;
93 },
94 unmangleable: function(options) {
95 return this.global && !options.toplevel
96 || this.exported
97 || this.undeclared
98 || !options.eval && this.scope.pinned()
99 || options.keep_fnames
100 && (this.orig[0] instanceof AST_SymbolClass
101 || this.orig[0] instanceof AST_SymbolDefClass
102 || this.orig[0] instanceof AST_SymbolDefun
103 || this.orig[0] instanceof AST_SymbolLambda);
104 },
105};
106
107var unary_side_effects = makePredicate("delete ++ --");
108
109function is_lhs(node, parent) {
110 if (parent instanceof AST_Assign) return parent.left === node && node;
111 if (parent instanceof AST_DefaultValue) return parent.name === node && node;
112 if (parent instanceof AST_Destructured) return node;
113 if (parent instanceof AST_DestructuredKeyVal) return node;
114 if (parent instanceof AST_ForEnumeration) return parent.init === node && node;
115 if (parent instanceof AST_Unary) return unary_side_effects[parent.operator] && parent.expression;
116}
117
118AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
119 options = defaults(options, {
120 cache: null,
121 ie: false,
122 });
123
124 // pass 1: setup scope chaining and handle definitions
125 var self = this;
126 var defun = null;
127 var exported = false;
128 var next_def_id = 0;
129 var scope = self.parent_scope = null;
130 var tw = new TreeWalker(function(node, descend) {
131 if (node instanceof AST_DefClass) {
132 var save_exported = exported;
133 exported = tw.parent() instanceof AST_ExportDeclaration;
134 node.name.walk(tw);
135 exported = save_exported;
136 walk_scope(function() {
137 if (node.extends) node.extends.walk(tw);
138 node.properties.forEach(function(prop) {
139 prop.walk(tw);
140 });
141 });
142 return true;
143 }
144 if (node instanceof AST_Definitions) {
145 var save_exported = exported;
146 exported = tw.parent() instanceof AST_ExportDeclaration;
147 descend();
148 exported = save_exported;
149 return true;
150 }
151 if (node instanceof AST_LambdaDefinition) {
152 var save_exported = exported;
153 exported = tw.parent() instanceof AST_ExportDeclaration;
154 node.name.walk(tw);
155 exported = save_exported;
156 walk_scope(function() {
157 node.argnames.forEach(function(argname) {
158 argname.walk(tw);
159 });
160 if (node.rest) node.rest.walk(tw);
161 walk_body(node, tw);
162 });
163 return true;
164 }
165 if (node instanceof AST_SwitchBranch) {
166 node.init_vars(scope);
167 descend();
168 return true;
169 }
170 if (node instanceof AST_Try) {
171 walk_scope(function() {
172 walk_body(node, tw);
173 });
174 if (node.bcatch) node.bcatch.walk(tw);
175 if (node.bfinally) node.bfinally.walk(tw);
176 return true;
177 }
178 if (node instanceof AST_With) {
179 var s = scope;
180 do {
181 s = s.resolve();
182 if (s.uses_with) break;
183 s.uses_with = true;
184 } while (s = s.parent_scope);
185 walk_scope(descend);
186 return true;
187 }
188 if (node instanceof AST_BlockScope) {
189 walk_scope(descend);
190 return true;
191 }
192 if (node instanceof AST_Symbol) {
193 node.scope = scope;
194 }
195 if (node instanceof AST_Label) {
196 node.thedef = node;
197 node.references = [];
198 }
199 if (node instanceof AST_SymbolCatch) {
200 scope.def_variable(node).defun = defun;
201 } else if (node instanceof AST_SymbolConst) {
202 var def = scope.def_variable(node);
203 def.defun = defun;
204 if (exported) def.exported = true;
205 } else if (node instanceof AST_SymbolDefun) {
206 var def = defun.def_function(node, tw.parent());
207 if (exported) def.exported = true;
208 entangle(defun, scope);
209 } else if (node instanceof AST_SymbolFunarg) {
210 defun.def_variable(node);
211 entangle(defun, scope);
212 } else if (node instanceof AST_SymbolLambda) {
213 var def = defun.def_function(node, node.name == "arguments" ? undefined : defun);
214 if (options.ie) def.defun = defun.parent_scope.resolve();
215 } else if (node instanceof AST_SymbolLet) {
216 var def = scope.def_variable(node);
217 if (exported) def.exported = true;
218 } else if (node instanceof AST_SymbolVar) {
219 var def = defun.def_variable(node, node instanceof AST_SymbolImport ? undefined : null);
220 if (exported) def.exported = true;
221 entangle(defun, scope);
222 }
223
224 function walk_scope(descend) {
225 node.init_vars(scope);
226 var save_defun = defun;
227 var save_scope = scope;
228 if (node instanceof AST_Scope) defun = node;
229 scope = node;
230 descend();
231 scope = save_scope;
232 defun = save_defun;
233 }
234
235 function entangle(defun, scope) {
236 if (defun === scope) return;
237 node.mark_enclosed(options);
238 var def = scope.find_variable(node.name);
239 if (node.thedef === def) return;
240 node.thedef = def;
241 def.orig.push(node);
242 node.mark_enclosed(options);
243 }
244 });
245 self.make_def = function(orig, init) {
246 return new SymbolDef(++next_def_id, this, orig, init);
247 };
248 self.walk(tw);
249
250 // pass 2: find back references and eval
251 self.globals = new Dictionary();
252 var in_arg = [];
253 var tw = new TreeWalker(function(node) {
254 if (node instanceof AST_Catch) {
255 if (!(node.argname instanceof AST_Destructured)) return;
256 in_arg.push(node);
257 node.argname.walk(tw);
258 in_arg.pop();
259 walk_body(node, tw);
260 return true;
261 }
262 if (node instanceof AST_Lambda) {
263 in_arg.push(node);
264 node.argnames.forEach(function(argname) {
265 argname.walk(tw);
266 });
267 if (node.rest) node.rest.walk(tw);
268 in_arg.pop();
269 walk_lambda(node, tw);
270 return true;
271 }
272 if (node instanceof AST_LoopControl) {
273 if (node.label) node.label.thedef.references.push(node);
274 return true;
275 }
276 if (node instanceof AST_SymbolDeclaration) {
277 var def = node.definition();
278 def.preinit = def.references.length;
279 if (node instanceof AST_SymbolCatch) {
280 // ensure mangling works if `catch` reuses a scope variable
281 var redef = def.redefined();
282 if (redef) for (var s = node.scope; s; s = s.parent_scope) {
283 push_uniq(s.enclosed, redef);
284 if (s === redef.scope) break;
285 }
286 } else if (node instanceof AST_SymbolConst) {
287 // ensure compression works if `const` reuses a scope variable
288 var redef = def.redefined();
289 if (redef) redef.const_redefs = true;
290 }
291 if (node.name != "arguments") return true;
292 var parent = node instanceof AST_SymbolVar && tw.parent();
293 if (parent instanceof AST_VarDef && !parent.value) return true;
294 var sym = node.scope.resolve().find_variable("arguments");
295 if (sym && is_arguments(sym)) sym.scope.uses_arguments = 3;
296 return true;
297 }
298 if (node instanceof AST_SymbolRef) {
299 var name = node.name;
300 var sym = node.scope.find_variable(name);
301 for (var i = in_arg.length; i > 0 && sym;) {
302 i = in_arg.lastIndexOf(sym.scope, i - 1);
303 if (i < 0) break;
304 var decl = sym.orig[0];
305 if (decl instanceof AST_SymbolCatch
306 || decl instanceof AST_SymbolFunarg
307 || decl instanceof AST_SymbolLambda) {
308 node.in_arg = true;
309 break;
310 }
311 sym = sym.scope.parent_scope.find_variable(name);
312 }
313 if (!sym) {
314 sym = self.def_global(node);
315 } else if (name == "arguments" && is_arguments(sym)) {
316 var parent = tw.parent();
317 if (is_lhs(node, parent)) {
318 sym.scope.uses_arguments = 3;
319 } else if (sym.scope.uses_arguments < 2
320 && !(parent instanceof AST_PropAccess && parent.expression === node)) {
321 sym.scope.uses_arguments = 2;
322 } else if (!sym.scope.uses_arguments) {
323 sym.scope.uses_arguments = true;
324 }
325 }
326 if (name == "eval") {
327 var parent = tw.parent();
328 if (parent.TYPE == "Call" && parent.expression === node) {
329 var s = node.scope;
330 do {
331 s = s.resolve();
332 if (s.uses_eval) break;
333 s.uses_eval = true;
334 } while (s = s.parent_scope);
335 } else if (sym.undeclared) {
336 self.uses_eval = true;
337 }
338 }
339 if (sym.init instanceof AST_LambdaDefinition && sym.scope !== sym.init.name.scope) {
340 var scope = node.scope;
341 do {
342 if (scope === sym.init.name.scope) break;
343 } while (scope = scope.parent_scope);
344 if (!scope) sym.init = undefined;
345 }
346 node.thedef = sym;
347 node.reference(options);
348 return true;
349 }
350 });
351 self.walk(tw);
352
353 // pass 3: fix up any scoping issue with IE8
354 if (options.ie) self.walk(new TreeWalker(function(node) {
355 if (node instanceof AST_SymbolCatch) {
356 var scope = node.thedef.defun;
357 if (scope.name instanceof AST_SymbolLambda && scope.name.name == node.name) {
358 scope = scope.parent_scope.resolve();
359 }
360 redefine(node, scope);
361 return true;
362 }
363 if (node instanceof AST_SymbolLambda) {
364 var def = node.thedef;
365 if (!redefine(node, node.scope.parent_scope.resolve())) {
366 delete def.defun;
367 } else if (typeof node.thedef.init !== "undefined") {
368 node.thedef.init = false;
369 } else if (def.init) {
370 node.thedef.init = def.init;
371 }
372 return true;
373 }
374 }));
375
376 function is_arguments(sym) {
377 return sym.orig[0] instanceof AST_SymbolFunarg
378 && !(sym.orig[1] instanceof AST_SymbolFunarg || sym.orig[2] instanceof AST_SymbolFunarg)
379 && !is_arrow(sym.scope);
380 }
381
382 function redefine(node, scope) {
383 var name = node.name;
384 var old_def = node.thedef;
385 if (!all(old_def.orig, function(sym) {
386 return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet);
387 })) return false;
388 var new_def = scope.find_variable(name);
389 if (new_def) {
390 var redef = new_def.redefined();
391 if (redef) new_def = redef;
392 } else {
393 new_def = self.globals.get(name);
394 }
395 if (new_def) {
396 new_def.orig.push(node);
397 } else {
398 new_def = scope.def_variable(node);
399 }
400 if (new_def.undeclared) self.variables.set(name, new_def);
401 if (name == "arguments" && is_arguments(old_def) && node instanceof AST_SymbolLambda) return true;
402 old_def.defun = new_def.scope;
403 old_def.forEach(function(node) {
404 node.redef = old_def;
405 node.thedef = new_def;
406 node.reference(options);
407 });
408 return true;
409 }
410});
411
412AST_Toplevel.DEFMETHOD("def_global", function(node) {
413 var globals = this.globals, name = node.name;
414 if (globals.has(name)) {
415 return globals.get(name);
416 } else {
417 var g = this.make_def(node);
418 g.undeclared = true;
419 g.global = true;
420 globals.set(name, g);
421 return g;
422 }
423});
424
425function init_block_vars(scope, parent) {
426 scope.enclosed = []; // variables from this or outer scope(s) that are referenced from this or inner scopes
427 scope.parent_scope = parent; // the parent scope (null if this is the top level)
428 scope.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
429 scope.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
430 if (parent) scope.make_def = parent.make_def; // top-level tracking of SymbolDef instances
431}
432
433function init_scope_vars(scope, parent) {
434 init_block_vars(scope, parent);
435 scope.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
436 scope.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
437}
438
439AST_BlockScope.DEFMETHOD("init_vars", function(parent_scope) {
440 init_block_vars(this, parent_scope);
441});
442AST_Scope.DEFMETHOD("init_vars", function(parent_scope) {
443 init_scope_vars(this, parent_scope);
444});
445AST_Arrow.DEFMETHOD("init_vars", function(parent_scope) {
446 init_scope_vars(this, parent_scope);
447 return this;
448});
449AST_AsyncArrow.DEFMETHOD("init_vars", function(parent_scope) {
450 init_scope_vars(this, parent_scope);
451});
452AST_Lambda.DEFMETHOD("init_vars", function(parent_scope) {
453 init_scope_vars(this, parent_scope);
454 this.uses_arguments = false;
455 this.def_variable(new AST_SymbolFunarg({
456 name: "arguments",
457 start: this.start,
458 end: this.end,
459 }));
460 return this;
461});
462
463AST_Symbol.DEFMETHOD("mark_enclosed", function(options) {
464 var def = this.definition();
465 for (var s = this.scope; s; s = s.parent_scope) {
466 push_uniq(s.enclosed, def);
467 if (!options) {
468 delete s._var_names;
469 } else if (options.keep_fnames) {
470 s.functions.each(function(d) {
471 push_uniq(def.scope.enclosed, d);
472 });
473 }
474 if (s === def.scope) break;
475 }
476});
477
478AST_Symbol.DEFMETHOD("reference", function(options) {
479 this.definition().references.push(this);
480 this.mark_enclosed(options);
481});
482
483AST_BlockScope.DEFMETHOD("find_variable", function(name) {
484 return this.variables.get(name)
485 || this.parent_scope && this.parent_scope.find_variable(name);
486});
487
488AST_BlockScope.DEFMETHOD("def_function", function(symbol, init) {
489 var def = this.def_variable(symbol, init);
490 if (!def.init || def.init instanceof AST_LambdaDefinition) def.init = init;
491 this.functions.set(symbol.name, def);
492 return def;
493});
494
495AST_BlockScope.DEFMETHOD("def_variable", function(symbol, init) {
496 var def = this.variables.get(symbol.name);
497 if (def) {
498 def.orig.push(symbol);
499 if (def.init instanceof AST_LambdaExpression) def.init = init;
500 } else {
501 def = this.make_def(symbol, init);
502 this.variables.set(symbol.name, def);
503 def.global = !this.parent_scope;
504 }
505 return symbol.thedef = def;
506});
507
508function names_in_use(scope, options) {
509 var names = scope.names_in_use;
510 if (!names) {
511 scope.cname = -1;
512 scope.cname_holes = [];
513 scope.names_in_use = names = Object.create(null);
514 var cache = options.cache && options.cache.props;
515 scope.enclosed.forEach(function(def) {
516 if (def.unmangleable(options)) names[def.name] = true;
517 if (def.global && cache && cache.has(def.name)) {
518 names[cache.get(def.name)] = true;
519 }
520 });
521 }
522 return names;
523}
524
525function next_mangled_name(def, options) {
526 var scope = def.scope;
527 var in_use = names_in_use(scope, options);
528 var holes = scope.cname_holes;
529 var names = Object.create(null);
530 var scopes = [ scope ];
531 def.forEach(function(sym) {
532 var scope = sym.scope;
533 do {
534 if (scopes.indexOf(scope) < 0) {
535 for (var name in names_in_use(scope, options)) {
536 names[name] = true;
537 }
538 scopes.push(scope);
539 } else break;
540 } while (scope = scope.parent_scope);
541 });
542 var name;
543 for (var i = 0; i < holes.length; i++) {
544 name = base54(holes[i]);
545 if (names[name]) continue;
546 holes.splice(i, 1);
547 in_use[name] = true;
548 return name;
549 }
550 while (true) {
551 name = base54(++scope.cname);
552 if (in_use[name] || RESERVED_WORDS[name] || options.reserved.has[name]) continue;
553 if (!names[name]) break;
554 holes.push(scope.cname);
555 }
556 in_use[name] = true;
557 return name;
558}
559
560AST_Symbol.DEFMETHOD("unmangleable", function(options) {
561 var def = this.definition();
562 return !def || def.unmangleable(options);
563});
564
565// labels are always mangleable
566AST_Label.DEFMETHOD("unmangleable", return_false);
567
568AST_Symbol.DEFMETHOD("definition", function() {
569 return this.thedef;
570});
571
572function _default_mangler_options(options) {
573 options = defaults(options, {
574 eval : false,
575 ie : false,
576 keep_fnames : false,
577 reserved : [],
578 toplevel : false,
579 v8 : false,
580 webkit : false,
581 });
582 if (!Array.isArray(options.reserved)) options.reserved = [];
583 // Never mangle arguments
584 push_uniq(options.reserved, "arguments");
585 options.reserved.has = makePredicate(options.reserved);
586 return options;
587}
588
589AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
590 options = _default_mangler_options(options);
591
592 // We only need to mangle declaration nodes. Special logic wired
593 // into the code generator will display the mangled name if it's
594 // present (and for AST_SymbolRef-s it'll use the mangled name of
595 // the AST_SymbolDeclaration that it points to).
596 var lname = -1;
597
598 if (options.cache && options.cache.props) {
599 var mangled_names = names_in_use(this, options);
600 options.cache.props.each(function(mangled_name) {
601 mangled_names[mangled_name] = true;
602 });
603 }
604
605 var redefined = [];
606 var tw = new TreeWalker(function(node, descend) {
607 if (node instanceof AST_LabeledStatement) {
608 // lname is incremented when we get to the AST_Label
609 var save_nesting = lname;
610 descend();
611 if (!options.v8 || !in_label(tw)) lname = save_nesting;
612 return true;
613 }
614 if (node instanceof AST_BlockScope) {
615 if (options.webkit && node instanceof AST_IterationStatement && node.init instanceof AST_Let) {
616 node.init.definitions.forEach(function(defn) {
617 defn.name.match_symbol(function(sym) {
618 if (!(sym instanceof AST_SymbolLet)) return;
619 var def = sym.definition();
620 var scope = sym.scope.parent_scope;
621 var redef = scope.def_variable(sym);
622 sym.thedef = def;
623 scope.to_mangle.push(redef);
624 def.redefined = function() {
625 return redef;
626 };
627 });
628 }, true);
629 }
630 node.to_mangle = [];
631 node.variables.each(function(def) {
632 if (!defer_redef(def)) node.to_mangle.push(def);
633 });
634 descend();
635 if (options.cache && node instanceof AST_Toplevel) {
636 node.globals.each(mangle);
637 }
638 if (node instanceof AST_Defun && tw.has_directive("use asm")) {
639 var sym = new AST_SymbolRef(node.name);
640 sym.scope = node;
641 sym.reference(options);
642 }
643 node.to_mangle.forEach(mangle);
644 return true;
645 }
646 if (node instanceof AST_Label) {
647 var name;
648 do {
649 name = base54(++lname);
650 } while (RESERVED_WORDS[name]);
651 node.mangled_name = name;
652 return true;
653 }
654 });
655 this.walk(tw);
656 redefined.forEach(mangle);
657
658 function mangle(def) {
659 if (options.reserved.has[def.name]) return;
660 def.mangle(options);
661 }
662
663 function defer_redef(def) {
664 var sym = def.orig[0];
665 var redef = def.redefined();
666 if (!redef) {
667 if (!(sym instanceof AST_SymbolConst)) return false;
668 var scope = def.scope.resolve();
669 if (def.scope === scope) return false;
670 if (def.scope.parent_scope.find_variable(sym.name)) return false;
671 redef = scope.def_variable(sym);
672 scope.to_mangle.push(redef);
673 }
674 redefined.push(def);
675 def.references.forEach(reference);
676 if (sym instanceof AST_SymbolCatch || sym instanceof AST_SymbolConst) reference(sym);
677 return true;
678
679 function reference(sym) {
680 sym.thedef = redef;
681 sym.reference(options);
682 sym.thedef = def;
683 }
684 }
685
686 function in_label(tw) {
687 var level = 0, parent;
688 while (parent = tw.parent(level++)) {
689 if (parent instanceof AST_Block) return parent instanceof AST_Toplevel && !options.toplevel;
690 if (parent instanceof AST_LabeledStatement) return true;
691 }
692 }
693});
694
695AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
696 var cache = options.cache && options.cache.props;
697 var avoid = Object.create(RESERVED_WORDS);
698 options.reserved.forEach(to_avoid);
699 this.globals.each(add_def);
700 this.walk(new TreeWalker(function(node) {
701 if (node instanceof AST_BlockScope) node.variables.each(add_def);
702 }));
703 return avoid;
704
705 function to_avoid(name) {
706 avoid[name] = true;
707 }
708
709 function add_def(def) {
710 var name = def.name;
711 if (def.global && cache && cache.has(name)) name = cache.get(name);
712 else if (!def.unmangleable(options)) return;
713 to_avoid(name);
714 }
715});
716
717AST_Toplevel.DEFMETHOD("expand_names", function(options) {
718 base54.reset();
719 base54.sort();
720 options = _default_mangler_options(options);
721 var avoid = this.find_colliding_names(options);
722 var cname = 0;
723 this.globals.each(rename);
724 this.walk(new TreeWalker(function(node) {
725 if (node instanceof AST_BlockScope) node.variables.each(rename);
726 }));
727
728 function next_name() {
729 var name;
730 do {
731 name = base54(cname++);
732 } while (avoid[name]);
733 return name;
734 }
735
736 function rename(def) {
737 if (def.global && options.cache) return;
738 if (def.unmangleable(options)) return;
739 if (options.reserved.has[def.name]) return;
740 var redef = def.redefined();
741 var name = redef ? redef.rename || redef.name : next_name();
742 def.rename = name;
743 def.forEach(function(sym) {
744 if (sym.definition() === def) sym.name = name;
745 });
746 }
747});
748
749AST_Node.DEFMETHOD("tail_node", return_this);
750AST_Sequence.DEFMETHOD("tail_node", function() {
751 return this.expressions[this.expressions.length - 1];
752});
753
754AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
755 options = _default_mangler_options(options);
756 base54.reset();
757 var fn = AST_Symbol.prototype.add_source_map;
758 try {
759 AST_Symbol.prototype.add_source_map = function() {
760 if (!this.unmangleable(options)) base54.consider(this.name, -1);
761 };
762 if (options.properties) {
763 AST_Dot.prototype.add_source_map = function() {
764 base54.consider(this.property, -1);
765 };
766 AST_Sub.prototype.add_source_map = function() {
767 skip_string(this.property);
768 };
769 }
770 base54.consider(this.print_to_string(), 1);
771 } finally {
772 AST_Symbol.prototype.add_source_map = fn;
773 delete AST_Dot.prototype.add_source_map;
774 delete AST_Sub.prototype.add_source_map;
775 }
776 base54.sort();
777
778 function skip_string(node) {
779 if (node instanceof AST_String) {
780 base54.consider(node.value, -1);
781 } else if (node instanceof AST_Conditional) {
782 skip_string(node.consequent);
783 skip_string(node.alternative);
784 } else if (node instanceof AST_Sequence) {
785 skip_string(node.tail_node());
786 }
787 }
788});
789
790var base54 = (function() {
791 var freq = Object.create(null);
792 function init(chars) {
793 var array = [];
794 for (var i = 0; i < chars.length; i++) {
795 var ch = chars[i];
796 array.push(ch);
797 freq[ch] = -1e-2 * i;
798 }
799 return array;
800 }
801 var digits = init("0123456789");
802 var leading = init("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_");
803 var chars, frequency;
804 function reset() {
805 chars = null;
806 frequency = Object.create(freq);
807 }
808 base54.consider = function(str, delta) {
809 for (var i = str.length; --i >= 0;) {
810 frequency[str[i]] += delta;
811 }
812 };
813 function compare(a, b) {
814 return frequency[b] - frequency[a];
815 }
816 base54.sort = function() {
817 chars = leading.sort(compare).concat(digits).sort(compare);
818 };
819 base54.reset = reset;
820 reset();
821 function base54(num) {
822 var ret = leading[num % 54];
823 for (num = Math.floor(num / 54); --num >= 0; num >>= 6) {
824 ret += chars[num & 0x3F];
825 }
826 return ret;
827 }
828 return base54;
829})();