UNPKG

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