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