UglifyJS/lib/scope.js

623 lines
20 KiB
JavaScript
Raw Normal View History

2012-08-22 18:28:59 +00:00
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
-------------------------------- (C) ---------------------------------
Author: Mihai Bazon
<mihai.bazon@gmail.com>
http://mihai.bazon.net/blog
Distributed under the BSD license:
2012-08-27 08:01:27 +00:00
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
2012-08-22 18:28:59 +00:00
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AS IS AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
***********************************************************************/
2012-10-02 09:45:31 +00:00
"use strict";
2018-06-09 17:40:40 +00:00
var utils = require("./utils");
var defaults = utils.defaults;
var return_false = utils.return_false;
var return_this = utils.return_this;
var push_uniq = utils.push_uniq;
var makePredicate = utils.makePredicate;
var Dictionary = utils.Dictionary;
var merge = utils.merge;
2018-06-13 10:50:22 +00:00
var AST = require("./ast");
var TreeWalker = AST.TreeWalker;
2018-06-09 17:40:40 +00:00
var parse = require("./parse");
var is_identifier = parse.is_identifier;
var parse = parse.parse;
function SymbolDef(scope, orig, init) {
this.name = orig.name;
this.orig = [ orig ];
this.init = init;
this.eliminated = 0;
this.scope = scope;
this.references = [];
this.replaced = 0;
this.global = false;
this.mangled_name = null;
this.undeclared = false;
this.id = SymbolDef.next_id++;
2018-04-26 20:30:29 +00:00
}
SymbolDef.next_id = 1;
SymbolDef.prototype = {
unmangleable: function(options) {
if (!options) options = {};
return this.global && !options.toplevel
|| this.undeclared
|| !options.eval && this.scope.pinned()
|| options.keep_fnames
2018-06-13 10:50:22 +00:00
&& (this.orig[0] instanceof AST.SymbolLambda
|| this.orig[0] instanceof AST.SymbolDefun);
},
mangle: function(options) {
var cache = options.cache && options.cache.props;
if (this.global && cache && cache.has(this.name)) {
this.mangled_name = cache.get(this.name);
} else if (!this.mangled_name && !this.unmangleable(options)) {
var def;
if (def = this.redefined()) {
this.mangled_name = def.mangled_name || def.name;
2018-02-23 15:51:49 +00:00
} else {
this.mangled_name = next_mangled_name(this.scope, options, this);
2018-02-23 15:51:49 +00:00
}
if (this.global && cache) {
cache.set(this.name, this.mangled_name);
}
}
},
redefined: function() {
return this.defun && this.defun.variables.get(this.name);
}
};
2018-06-13 10:50:22 +00:00
AST.Toplevel.DEFMETHOD("figure_out_scope", function(options) {
options = defaults(options, {
cache: null,
unify CLI & API under `minify()` (#1811) - rename `screw_ie8` to `ie8` - rename `mangle.except` to `mangle.reserved` - rename `mangle.properties.ignore_quoted` to `mangle.properties.keep_quoted` - compact `sourceMap` options - more stringent verification on input `options` - toplevel shorthands - `ie8` - `keep_fnames` - `toplevel` - `warnings` - support arrays and unquoted string values on CLI - drop `fromString` from `minify()` - `minify()` no longer handles any `fs` operations - unify order of operations for `mangle_properties()` on CLI & API - `bin/uglifyjs` used to `mangle_properties()` before even `Compressor` - `minify()` used to `mangle_properties()` after `Compressor` but before `mangle_names()` - both will now do `Compressor`, `mangle_names()` then `mangle_properties()` - `options.parse` / `--parse` for parser options beyond `bare_returns` - add `mangle.properties.builtins` to disable built-in reserved list - disable with `--mangle-props builtins` on CLI - `warnings` now off by default - add `--warn` and `--verbose` on CLI - drop `--enclose` - drop `--export-all` - drop `--reserved-file` - use `--mangle reserved` instead - drop `--reserve-domprops` - enabled by default, disable with `--mangle-props domprops` - drop `--prefix` - use `--source-map base` instead - drop `--lint` - remove `bin/extract-props.js` - limit exposure of internal APIs - update documentations closes #96 closes #102 closes #136 closes #166 closes #243 closes #254 closes #261 closes #311 closes #700 closes #748 closes #912 closes #1072 closes #1366 fixes #101 fixes #123 fixes #124 fixes #263 fixes #379 fixes #419 fixes #423 fixes #461 fixes #465 fixes #576 fixes #737 fixes #772 fixes #958 fixes #1036 fixes #1142 fixes #1175 fixes #1220 fixes #1223 fixes #1280 fixes #1359 fixes #1368
2017-04-15 15:50:50 +00:00
ie8: false,
});
2012-08-19 19:46:00 +00:00
// pass 1: setup scope chaining and handle definitions
var self = this;
var scope = self.parent_scope = null;
var defun = null;
2018-06-06 09:50:56 +00:00
var tw = new TreeWalker(function(node, descend) {
2018-06-13 10:50:22 +00:00
if (node instanceof AST.Catch) {
var save_scope = scope;
2018-06-13 10:50:22 +00:00
scope = new AST.Scope(node);
scope.init_scope_vars(save_scope);
descend();
scope = save_scope;
return true;
}
2018-06-13 10:50:22 +00:00
if (node instanceof AST.Scope) {
node.init_scope_vars(scope);
var save_scope = scope;
var save_defun = defun;
defun = scope = node;
descend();
scope = save_scope;
defun = save_defun;
return true; // don't descend again in TreeWalker
}
2018-06-13 10:50:22 +00:00
if (node instanceof AST.With) {
for (var s = scope; s; s = s.parent_scope)
s.uses_with = true;
return;
}
2018-06-13 10:50:22 +00:00
if (node instanceof AST.Symbol) {
node.scope = scope;
}
2018-06-13 10:50:22 +00:00
if (node instanceof AST.Label) {
node.thedef = node;
node.references = [];
}
2018-06-13 10:50:22 +00:00
if (node instanceof AST.SymbolLambda) {
defun.def_function(node, node.name == "arguments" ? undefined : defun);
}
2018-06-13 10:50:22 +00:00
else if (node instanceof AST.SymbolDefun) {
2012-08-20 14:19:30 +00:00
// Careful here, the scope where this should be defined is
// the parent scope. The reason is that we enter a new
2018-06-13 10:50:22 +00:00
// scope when we encounter the AST.Defun node (which is
// instanceof AST.Scope) but we get to the symbol a bit
2012-08-20 14:19:30 +00:00
// later.
(node.scope = defun.parent_scope).def_function(node, defun);
}
2018-06-13 10:50:22 +00:00
else if (node instanceof AST.SymbolVar) {
defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
}
2018-02-23 15:51:49 +00:00
node.reference(options);
}
2012-08-20 14:19:30 +00:00
}
2018-06-13 10:50:22 +00:00
else if (node instanceof AST.SymbolCatch) {
scope.def_variable(node).defun = defun;
}
});
self.walk(tw);
2012-08-19 19:46:00 +00:00
// pass 2: find back references and eval
self.globals = new Dictionary();
2018-06-06 09:50:56 +00:00
var tw = new TreeWalker(function(node, descend) {
2018-06-13 10:50:22 +00:00
if (node instanceof AST.LoopControl && node.label) {
node.label.thedef.references.push(node);
return true;
}
2018-06-13 10:50:22 +00:00
if (node instanceof AST.SymbolRef) {
var name = node.name;
2018-06-13 10:50:22 +00:00
if (name == "eval" && tw.parent() instanceof AST.Call) {
2016-02-16 18:52:28 +00:00
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
s.uses_eval = true;
}
}
var sym = node.scope.find_variable(name);
if (!sym) {
sym = self.def_global(node);
2018-06-13 10:50:22 +00:00
} else if (sym.scope instanceof AST.Lambda && name == "arguments") {
sym.scope.uses_arguments = true;
2016-09-20 14:23:27 +00:00
}
node.thedef = sym;
node.reference(options);
2012-09-28 08:12:47 +00:00
return true;
}
// ensure mangling works if catch reuses a scope variable
var def;
2018-06-13 10:50:22 +00:00
if (node instanceof AST.SymbolCatch && (def = node.definition().redefined())) {
var s = node.scope;
while (s) {
push_uniq(s.enclosed, def);
if (s === def.scope) break;
s = s.parent_scope;
}
}
});
self.walk(tw);
// pass 3: fix up any scoping issue with IE8
unify CLI & API under `minify()` (#1811) - rename `screw_ie8` to `ie8` - rename `mangle.except` to `mangle.reserved` - rename `mangle.properties.ignore_quoted` to `mangle.properties.keep_quoted` - compact `sourceMap` options - more stringent verification on input `options` - toplevel shorthands - `ie8` - `keep_fnames` - `toplevel` - `warnings` - support arrays and unquoted string values on CLI - drop `fromString` from `minify()` - `minify()` no longer handles any `fs` operations - unify order of operations for `mangle_properties()` on CLI & API - `bin/uglifyjs` used to `mangle_properties()` before even `Compressor` - `minify()` used to `mangle_properties()` after `Compressor` but before `mangle_names()` - both will now do `Compressor`, `mangle_names()` then `mangle_properties()` - `options.parse` / `--parse` for parser options beyond `bare_returns` - add `mangle.properties.builtins` to disable built-in reserved list - disable with `--mangle-props builtins` on CLI - `warnings` now off by default - add `--warn` and `--verbose` on CLI - drop `--enclose` - drop `--export-all` - drop `--reserved-file` - use `--mangle reserved` instead - drop `--reserve-domprops` - enabled by default, disable with `--mangle-props domprops` - drop `--prefix` - use `--source-map base` instead - drop `--lint` - remove `bin/extract-props.js` - limit exposure of internal APIs - update documentations closes #96 closes #102 closes #136 closes #166 closes #243 closes #254 closes #261 closes #311 closes #700 closes #748 closes #912 closes #1072 closes #1366 fixes #101 fixes #123 fixes #124 fixes #263 fixes #379 fixes #419 fixes #423 fixes #461 fixes #465 fixes #576 fixes #737 fixes #772 fixes #958 fixes #1036 fixes #1142 fixes #1175 fixes #1220 fixes #1223 fixes #1280 fixes #1359 fixes #1368
2017-04-15 15:50:50 +00:00
if (options.ie8) {
self.walk(new TreeWalker(function(node, descend) {
2018-06-13 10:50:22 +00:00
if (node instanceof AST.SymbolCatch) {
var name = node.name;
var refs = node.thedef.references;
var scope = node.thedef.defun;
var def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node);
refs.forEach(function(ref) {
ref.thedef = def;
ref.reference(options);
});
node.thedef = def;
node.reference(options);
return true;
}
}));
}
});
2012-08-19 19:46:00 +00:00
2018-06-13 10:50:22 +00:00
AST.Toplevel.DEFMETHOD("def_global", function(node) {
var globals = this.globals, name = node.name;
if (globals.has(name)) {
return globals.get(name);
} else {
var g = new SymbolDef(this, node);
g.undeclared = true;
g.global = true;
globals.set(name, g);
return g;
}
});
2018-06-13 10:50:22 +00:00
AST.Scope.DEFMETHOD("init_scope_vars", function(parent_scope) {
this.variables = new Dictionary(); // map name to AST.SymbolVar (variables defined in this scope; includes functions)
this.functions = new Dictionary(); // map name to AST.SymbolDefun (functions defined in this scope)
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
this.parent_scope = parent_scope; // the parent scope
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
this.cname = -1; // the current index for mangling functions/variables
});
2018-06-13 10:50:22 +00:00
AST.Lambda.DEFMETHOD("init_scope_vars", function() {
AST.Scope.prototype.init_scope_vars.apply(this, arguments);
this.uses_arguments = false;
2018-06-13 10:50:22 +00:00
this.def_variable(new AST.SymbolFunarg({
name: "arguments",
start: this.start,
end: this.end
}));
});
2018-06-13 10:50:22 +00:00
AST.Symbol.DEFMETHOD("mark_enclosed", function(options) {
var def = this.definition();
2012-10-04 05:49:18 +00:00
var s = this.scope;
while (s) {
push_uniq(s.enclosed, def);
if (options.keep_fnames) {
s.functions.each(function(d) {
push_uniq(def.scope.enclosed, d);
});
}
if (s === def.scope) break;
s = s.parent_scope;
}
});
2018-06-13 10:50:22 +00:00
AST.Symbol.DEFMETHOD("reference", function(options) {
this.definition().references.push(this);
this.mark_enclosed(options);
});
2018-06-13 10:50:22 +00:00
AST.Scope.DEFMETHOD("find_variable", function(name) {
if (name instanceof AST.Symbol) name = name.name;
return this.variables.get(name)
|| (this.parent_scope && this.parent_scope.find_variable(name));
});
2012-08-19 19:46:00 +00:00
2018-06-13 10:50:22 +00:00
AST.Scope.DEFMETHOD("def_function", function(symbol, init) {
var def = this.def_variable(symbol, init);
2018-06-13 10:50:22 +00:00
if (!def.init || def.init instanceof AST.Defun) def.init = init;
this.functions.set(symbol.name, def);
return def;
});
2012-08-19 19:46:00 +00:00
2018-06-13 10:50:22 +00:00
AST.Scope.DEFMETHOD("def_variable", function(symbol, init) {
var def = this.variables.get(symbol.name);
if (def) {
def.orig.push(symbol);
2018-06-13 10:50:22 +00:00
if (def.init && (def.scope !== symbol.scope || def.init instanceof AST.Function)) {
def.init = init;
}
} else {
def = new SymbolDef(this, symbol, init);
this.variables.set(symbol.name, def);
def.global = !this.parent_scope;
}
return symbol.thedef = def;
});
2012-08-19 19:46:00 +00:00
2018-02-23 15:51:49 +00:00
function names_in_use(scope, options) {
var names = scope.names_in_use;
if (!names) {
scope.names_in_use = names = Object.create(scope.mangled_names || null);
scope.cname_holes = [];
scope.enclosed.forEach(function(def) {
if (def.unmangleable(options)) names[def.name] = true;
});
2012-08-20 14:19:30 +00:00
}
2018-02-23 15:51:49 +00:00
return names;
}
2018-02-23 15:51:49 +00:00
function next_mangled_name(scope, options, def) {
var in_use = names_in_use(scope, options);
var holes = scope.cname_holes;
var names = Object.create(null);
// #179, #326
2013-10-29 13:53:54 +00:00
// in Safari strict mode, something like (function x(x){...}) is a syntax error;
// a function expression's argument cannot shadow the function expression's name
2018-06-13 10:50:22 +00:00
if (scope instanceof AST.Function && scope.name && def.orig[0] instanceof AST.SymbolFunarg) {
2018-02-23 15:51:49 +00:00
var tricky_def = scope.name.definition();
// the function's mangled_name is null when keep_fnames is true
names[tricky_def.mangled_name || tricky_def.name] = true;
}
var scopes = [ scope ];
def.references.forEach(function(sym) {
var scope = sym.scope;
do {
if (scopes.indexOf(scope) < 0) {
for (var name in names_in_use(scope, options)) {
names[name] = true;
}
scopes.push(scope);
} else break;
} while (scope = scope.parent_scope);
});
var name;
for (var i = 0, len = holes.length; i < len; i++) {
name = base54(holes[i]);
if (names[name]) continue;
holes.splice(i, 1);
scope.names_in_use[name] = true;
return name;
}
while (true) {
2018-02-23 15:51:49 +00:00
name = base54(++scope.cname);
if (in_use[name] || !is_identifier(name) || options.reserved.has[name]) continue;
2018-02-23 15:51:49 +00:00
if (!names[name]) break;
holes.push(scope.cname);
}
2018-02-23 15:51:49 +00:00
scope.names_in_use[name] = true;
2018-06-13 10:50:22 +00:00
if (options.ie8 && def.orig[0] instanceof AST.SymbolLambda) {
names_in_use(scope.parent_scope, options)[name] = true;
}
2018-02-23 15:51:49 +00:00
return name;
}
2018-06-13 10:50:22 +00:00
AST.Symbol.DEFMETHOD("unmangleable", function(options) {
var def = this.definition();
return !def || def.unmangleable(options);
});
// labels are always mangleable
2018-06-13 10:50:22 +00:00
AST.Label.DEFMETHOD("unmangleable", return_false);
2018-06-13 10:50:22 +00:00
AST.Symbol.DEFMETHOD("unreferenced", function() {
return !this.definition().references.length && !this.scope.pinned();
2012-08-20 14:19:30 +00:00
});
2018-06-13 10:50:22 +00:00
AST.Symbol.DEFMETHOD("definition", function() {
return this.thedef;
2012-08-21 08:53:19 +00:00
});
2018-06-13 10:50:22 +00:00
AST.Symbol.DEFMETHOD("global", function() {
return this.definition().global;
2012-08-21 08:53:19 +00:00
});
2018-02-23 18:24:47 +00:00
function _default_mangler_options(options) {
options = defaults(options, {
eval : false,
unify CLI & API under `minify()` (#1811) - rename `screw_ie8` to `ie8` - rename `mangle.except` to `mangle.reserved` - rename `mangle.properties.ignore_quoted` to `mangle.properties.keep_quoted` - compact `sourceMap` options - more stringent verification on input `options` - toplevel shorthands - `ie8` - `keep_fnames` - `toplevel` - `warnings` - support arrays and unquoted string values on CLI - drop `fromString` from `minify()` - `minify()` no longer handles any `fs` operations - unify order of operations for `mangle_properties()` on CLI & API - `bin/uglifyjs` used to `mangle_properties()` before even `Compressor` - `minify()` used to `mangle_properties()` after `Compressor` but before `mangle_names()` - both will now do `Compressor`, `mangle_names()` then `mangle_properties()` - `options.parse` / `--parse` for parser options beyond `bare_returns` - add `mangle.properties.builtins` to disable built-in reserved list - disable with `--mangle-props builtins` on CLI - `warnings` now off by default - add `--warn` and `--verbose` on CLI - drop `--enclose` - drop `--export-all` - drop `--reserved-file` - use `--mangle reserved` instead - drop `--reserve-domprops` - enabled by default, disable with `--mangle-props domprops` - drop `--prefix` - use `--source-map base` instead - drop `--lint` - remove `bin/extract-props.js` - limit exposure of internal APIs - update documentations closes #96 closes #102 closes #136 closes #166 closes #243 closes #254 closes #261 closes #311 closes #700 closes #748 closes #912 closes #1072 closes #1366 fixes #101 fixes #123 fixes #124 fixes #263 fixes #379 fixes #419 fixes #423 fixes #461 fixes #465 fixes #576 fixes #737 fixes #772 fixes #958 fixes #1036 fixes #1142 fixes #1175 fixes #1220 fixes #1223 fixes #1280 fixes #1359 fixes #1368
2017-04-15 15:50:50 +00:00
ie8 : false,
keep_fnames : false,
unify CLI & API under `minify()` (#1811) - rename `screw_ie8` to `ie8` - rename `mangle.except` to `mangle.reserved` - rename `mangle.properties.ignore_quoted` to `mangle.properties.keep_quoted` - compact `sourceMap` options - more stringent verification on input `options` - toplevel shorthands - `ie8` - `keep_fnames` - `toplevel` - `warnings` - support arrays and unquoted string values on CLI - drop `fromString` from `minify()` - `minify()` no longer handles any `fs` operations - unify order of operations for `mangle_properties()` on CLI & API - `bin/uglifyjs` used to `mangle_properties()` before even `Compressor` - `minify()` used to `mangle_properties()` after `Compressor` but before `mangle_names()` - both will now do `Compressor`, `mangle_names()` then `mangle_properties()` - `options.parse` / `--parse` for parser options beyond `bare_returns` - add `mangle.properties.builtins` to disable built-in reserved list - disable with `--mangle-props builtins` on CLI - `warnings` now off by default - add `--warn` and `--verbose` on CLI - drop `--enclose` - drop `--export-all` - drop `--reserved-file` - use `--mangle reserved` instead - drop `--reserve-domprops` - enabled by default, disable with `--mangle-props domprops` - drop `--prefix` - use `--source-map base` instead - drop `--lint` - remove `bin/extract-props.js` - limit exposure of internal APIs - update documentations closes #96 closes #102 closes #136 closes #166 closes #243 closes #254 closes #261 closes #311 closes #700 closes #748 closes #912 closes #1072 closes #1366 fixes #101 fixes #123 fixes #124 fixes #263 fixes #379 fixes #419 fixes #423 fixes #461 fixes #465 fixes #576 fixes #737 fixes #772 fixes #958 fixes #1036 fixes #1142 fixes #1175 fixes #1220 fixes #1223 fixes #1280 fixes #1359 fixes #1368
2017-04-15 15:50:50 +00:00
reserved : [],
toplevel : false,
});
if (!Array.isArray(options.reserved)) options.reserved = [];
// Never mangle arguments
push_uniq(options.reserved, "arguments");
options.reserved.has = makePredicate(options.reserved);
return options;
2018-02-23 18:24:47 +00:00
}
2012-11-08 10:31:28 +00:00
2018-06-13 10:50:22 +00:00
AST.Toplevel.DEFMETHOD("mangle_names", function(options) {
2018-02-23 18:24:47 +00:00
options = _default_mangler_options(options);
// We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's
2018-06-13 10:50:22 +00:00
// present (and for AST.SymbolRef-s it'll use the mangled name of
// the AST.SymbolDeclaration that it points to).
var lname = -1;
2018-02-23 15:51:49 +00:00
if (options.cache && options.cache.props) {
var mangled_names = this.mangled_names = Object.create(null);
options.cache.props.each(function(mangled_name) {
mangled_names[mangled_name] = true;
});
}
2018-02-23 15:51:49 +00:00
var redefined = [];
2018-06-06 09:50:56 +00:00
var tw = new TreeWalker(function(node, descend) {
2018-06-13 10:50:22 +00:00
if (node instanceof AST.LabeledStatement) {
// lname is incremented when we get to the AST.Label
var save_nesting = lname;
descend();
lname = save_nesting;
return true; // don't descend again in TreeWalker
}
2018-06-13 10:50:22 +00:00
if (node instanceof AST.Scope) {
2018-02-23 15:51:49 +00:00
descend();
2018-06-13 10:50:22 +00:00
if (options.cache && node instanceof AST.Toplevel) {
2018-02-23 15:51:49 +00:00
node.globals.each(mangle);
}
node.variables.each(mangle);
return true;
}
2018-06-13 10:50:22 +00:00
if (node instanceof AST.Label) {
var name;
do name = base54(++lname); while (!is_identifier(name));
node.mangled_name = name;
return true;
2012-08-20 14:19:30 +00:00
}
2018-06-13 10:50:22 +00:00
if (!options.ie8 && node instanceof AST.Catch) {
2018-02-23 15:51:49 +00:00
var def = node.argname.definition();
var redef = def.redefined();
if (redef) {
redefined.push(def);
reference(node.argname);
def.references.forEach(reference);
2018-02-23 15:51:49 +00:00
}
descend();
if (!redef) mangle(def);
return true;
}
function reference(sym) {
sym.thedef = redef;
sym.reference(options);
sym.thedef = def;
}
2012-08-20 14:19:30 +00:00
});
this.walk(tw);
2018-02-23 15:51:49 +00:00
redefined.forEach(mangle);
2018-02-23 15:51:49 +00:00
function mangle(def) {
if (options.reserved.has[def.name]) return;
def.mangle(options);
}
});
2018-06-13 10:50:22 +00:00
AST.Toplevel.DEFMETHOD("find_colliding_names", function(options) {
var cache = options.cache && options.cache.props;
var avoid = Object.create(null);
options.reserved.forEach(to_avoid);
this.globals.each(add_def);
this.walk(new TreeWalker(function(node) {
2018-06-13 10:50:22 +00:00
if (node instanceof AST.Scope) node.variables.each(add_def);
if (node instanceof AST.SymbolCatch) add_def(node.definition());
}));
return avoid;
function to_avoid(name) {
avoid[name] = true;
}
function add_def(def) {
var name = def.name;
if (def.global && cache && cache.has(name)) name = cache.get(name);
else if (!def.unmangleable(options)) return;
to_avoid(name);
}
});
2018-06-13 10:50:22 +00:00
AST.Toplevel.DEFMETHOD("expand_names", function(options) {
base54.reset();
base54.sort();
2018-02-23 18:24:47 +00:00
options = _default_mangler_options(options);
var avoid = this.find_colliding_names(options);
var cname = 0;
this.globals.each(rename);
this.walk(new TreeWalker(function(node) {
2018-06-13 10:50:22 +00:00
if (node instanceof AST.Scope) node.variables.each(rename);
if (node instanceof AST.SymbolCatch) rename(node.definition());
}));
function next_name() {
var name;
do {
name = base54(cname++);
} while (avoid[name] || !is_identifier(name));
return name;
}
function rename(def) {
if (def.global && options.cache) return;
if (def.unmangleable(options)) return;
if (options.reserved.has[def.name]) return;
var d = def.redefined();
def.name = d ? d.name : next_name();
def.orig.forEach(function(sym) {
sym.name = def.name;
});
def.references.forEach(function(sym) {
sym.name = def.name;
});
}
2012-08-20 14:19:30 +00:00
});
2018-06-13 10:50:22 +00:00
AST.Node.DEFMETHOD("tail_node", return_this);
AST.Sequence.DEFMETHOD("tail_node", function() {
2017-11-30 19:40:46 +00:00
return this.expressions[this.expressions.length - 1];
});
2018-06-13 10:50:22 +00:00
AST.Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
2018-02-23 18:24:47 +00:00
options = _default_mangler_options(options);
base54.reset();
try {
2018-06-13 10:50:22 +00:00
AST.Node.prototype.print = function(stream, force_parens) {
this._print(stream, force_parens);
2018-06-13 10:50:22 +00:00
if (this instanceof AST.Symbol && !this.unmangleable(options)) {
base54.consider(this.name, -1);
} else if (options.properties) {
2018-06-13 10:50:22 +00:00
if (this instanceof AST.Dot) {
base54.consider(this.property, -1);
2018-06-13 10:50:22 +00:00
} else if (this instanceof AST.Sub) {
skip_string(this.property);
}
}
};
base54.consider(this.print_to_string(), 1);
} finally {
2018-06-13 10:50:22 +00:00
AST.Node.prototype.print = AST.Node.prototype._print;
}
base54.sort();
function skip_string(node) {
2018-06-13 10:50:22 +00:00
if (node instanceof AST.String) {
base54.consider(node.value, -1);
2018-06-13 10:50:22 +00:00
} else if (node instanceof AST.Conditional) {
skip_string(node.consequent);
skip_string(node.alternative);
2018-06-13 10:50:22 +00:00
} else if (node instanceof AST.Sequence) {
2017-11-30 19:40:46 +00:00
skip_string(node.tail_node());
}
}
});
var base54 = (function() {
2018-04-26 20:30:29 +00:00
var freq = Object.create(null);
function init(chars) {
var array = [];
for (var i = 0, len = chars.length; i < len; i++) {
var ch = chars[i];
array.push(ch);
freq[ch] = -1e-2 * i;
}
return array;
}
var digits = init("0123456789");
var leading = init("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_");
var chars, frequency;
function reset() {
2018-04-26 20:30:29 +00:00
frequency = Object.create(freq);
}
base54.consider = function(str, delta) {
for (var i = str.length; --i >= 0;) {
frequency[str[i]] += delta;
}
};
function compare(a, b) {
return frequency[b] - frequency[a];
}
base54.sort = function() {
2018-04-26 20:30:29 +00:00
chars = leading.sort(compare).concat(digits.sort(compare));
};
base54.reset = reset;
reset();
function base54(num) {
var ret = "", base = 54;
2014-12-01 05:16:44 +00:00
num++;
do {
2014-12-01 05:16:44 +00:00
num--;
ret += chars[num % base];
num = Math.floor(num / base);
base = 64;
} while (num > 0);
return ret;
2018-04-26 20:30:29 +00:00
}
return base54;
})();
2018-06-09 17:40:40 +00:00
merge(exports, {
SymbolDef : SymbolDef,
names_in_use : names_in_use,
next_mangled_name : next_mangled_name,
base54 : base54,
});