react / react-0.13.3 / examples / basic-commonjs / node_modules / browserify / node_modules / umd / node_modules / uglify-js / lib / scope.js
80743 views/***********************************************************************12A JavaScript tokenizer / parser / beautifier / compressor.3https://github.com/mishoo/UglifyJS245-------------------------------- (C) ---------------------------------67Author: Mihai Bazon8<[email protected]>9http://mihai.bazon.net/blog1011Distributed under the BSD license:1213Copyright 2012 (c) Mihai Bazon <[email protected]>1415Redistribution and use in source and binary forms, with or without16modification, are permitted provided that the following conditions17are met:1819* Redistributions of source code must retain the above20copyright notice, this list of conditions and the following21disclaimer.2223* Redistributions in binary form must reproduce the above24copyright notice, this list of conditions and the following25disclaimer in the documentation and/or other materials26provided with the distribution.2728THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY29EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE30IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR31PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE32LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,33OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,34PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR35PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY36THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR37TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF38THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF39SUCH DAMAGE.4041***********************************************************************/4243"use strict";4445function SymbolDef(scope, index, orig) {46this.name = orig.name;47this.orig = [ orig ];48this.scope = scope;49this.references = [];50this.global = false;51this.mangled_name = null;52this.undeclared = false;53this.constant = false;54this.index = index;55};5657SymbolDef.prototype = {58unmangleable: function(options) {59if (!options) options = {};6061return (this.global && !options.toplevel)62|| this.undeclared63|| (!options.eval && (this.scope.uses_eval || this.scope.uses_with))64|| (options.keep_fnames65&& (this.orig[0] instanceof AST_SymbolLambda66|| this.orig[0] instanceof AST_SymbolDefun));67},68mangle: function(options) {69var cache = options.cache && options.cache.props;70if (this.global && cache && cache.has(this.name)) {71this.mangled_name = cache.get(this.name);72}73else if (!this.mangled_name && !this.unmangleable(options)) {74var s = this.scope;75if (!options.screw_ie8 && this.orig[0] instanceof AST_SymbolLambda)76s = s.parent_scope;77this.mangled_name = s.next_mangled(options, this);78if (this.global && cache) {79cache.set(this.name, this.mangled_name);80}81}82}83};8485AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){86options = defaults(options, {87screw_ie8: false,88cache: null89});9091// pass 1: setup scope chaining and handle definitions92var self = this;93var scope = self.parent_scope = null;94var defun = null;95var nesting = 0;96var tw = new TreeWalker(function(node, descend){97if (options.screw_ie8 && node instanceof AST_Catch) {98var save_scope = scope;99scope = new AST_Scope(node);100scope.init_scope_vars(nesting);101scope.parent_scope = save_scope;102descend();103scope = save_scope;104return true;105}106if (node instanceof AST_Scope) {107node.init_scope_vars(nesting);108var save_scope = node.parent_scope = scope;109var save_defun = defun;110defun = scope = node;111++nesting; descend(); --nesting;112scope = save_scope;113defun = save_defun;114return true; // don't descend again in TreeWalker115}116if (node instanceof AST_Directive) {117node.scope = scope;118push_uniq(scope.directives, node.value);119return true;120}121if (node instanceof AST_With) {122for (var s = scope; s; s = s.parent_scope)123s.uses_with = true;124return;125}126if (node instanceof AST_Symbol) {127node.scope = scope;128}129if (node instanceof AST_SymbolLambda) {130defun.def_function(node);131}132else if (node instanceof AST_SymbolDefun) {133// Careful here, the scope where this should be defined is134// the parent scope. The reason is that we enter a new135// scope when we encounter the AST_Defun node (which is136// instanceof AST_Scope) but we get to the symbol a bit137// later.138(node.scope = defun.parent_scope).def_function(node);139}140else if (node instanceof AST_SymbolVar141|| node instanceof AST_SymbolConst) {142var def = defun.def_variable(node);143def.constant = node instanceof AST_SymbolConst;144def.init = tw.parent().value;145}146else if (node instanceof AST_SymbolCatch) {147(options.screw_ie8 ? scope : defun)148.def_variable(node);149}150});151self.walk(tw);152153// pass 2: find back references and eval154var func = null;155var globals = self.globals = new Dictionary();156var tw = new TreeWalker(function(node, descend){157if (node instanceof AST_Lambda) {158var prev_func = func;159func = node;160descend();161func = prev_func;162return true;163}164if (node instanceof AST_SymbolRef) {165var name = node.name;166var sym = node.scope.find_variable(name);167if (!sym) {168var g;169if (globals.has(name)) {170g = globals.get(name);171} else {172g = new SymbolDef(self, globals.size(), node);173g.undeclared = true;174g.global = true;175globals.set(name, g);176}177node.thedef = g;178if (name == "eval" && tw.parent() instanceof AST_Call) {179for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope)180s.uses_eval = true;181}182if (func && name == "arguments") {183func.uses_arguments = true;184}185} else {186node.thedef = sym;187}188node.reference();189return true;190}191});192self.walk(tw);193194if (options.cache) {195this.cname = options.cache.cname;196}197});198199AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){200this.directives = []; // contains the directives defined in this scope, i.e. "use strict"201this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)202this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)203this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement204this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`205this.parent_scope = null; // the parent scope206this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes207this.cname = -1; // the current index for mangling functions/variables208this.nesting = nesting; // the nesting level of this scope (0 means toplevel)209});210211AST_Scope.DEFMETHOD("strict", function(){212return this.has_directive("use strict");213});214215AST_Lambda.DEFMETHOD("init_scope_vars", function(){216AST_Scope.prototype.init_scope_vars.apply(this, arguments);217this.uses_arguments = false;218});219220AST_SymbolRef.DEFMETHOD("reference", function() {221var def = this.definition();222def.references.push(this);223var s = this.scope;224while (s) {225push_uniq(s.enclosed, def);226if (s === def.scope) break;227s = s.parent_scope;228}229this.frame = this.scope.nesting - def.scope.nesting;230});231232AST_Scope.DEFMETHOD("find_variable", function(name){233if (name instanceof AST_Symbol) name = name.name;234return this.variables.get(name)235|| (this.parent_scope && this.parent_scope.find_variable(name));236});237238AST_Scope.DEFMETHOD("has_directive", function(value){239return this.parent_scope && this.parent_scope.has_directive(value)240|| (this.directives.indexOf(value) >= 0 ? this : null);241});242243AST_Scope.DEFMETHOD("def_function", function(symbol){244this.functions.set(symbol.name, this.def_variable(symbol));245});246247AST_Scope.DEFMETHOD("def_variable", function(symbol){248var def;249if (!this.variables.has(symbol.name)) {250def = new SymbolDef(this, this.variables.size(), symbol);251this.variables.set(symbol.name, def);252def.global = !this.parent_scope;253} else {254def = this.variables.get(symbol.name);255def.orig.push(symbol);256}257return symbol.thedef = def;258});259260AST_Scope.DEFMETHOD("next_mangled", function(options){261var ext = this.enclosed;262out: while (true) {263var m = base54(++this.cname);264if (!is_identifier(m)) continue; // skip over "do"265266// https://github.com/mishoo/UglifyJS2/issues/242 -- do not267// shadow a name excepted from mangling.268if (options.except.indexOf(m) >= 0) continue;269270// we must ensure that the mangled name does not shadow a name271// from some parent scope that is referenced in this or in272// inner scopes.273for (var i = ext.length; --i >= 0;) {274var sym = ext[i];275var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);276if (m == name) continue out;277}278return m;279}280});281282AST_Function.DEFMETHOD("next_mangled", function(options, def){283// #179, #326284// in Safari strict mode, something like (function x(x){...}) is a syntax error;285// a function expression's argument cannot shadow the function expression's name286287var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition();288while (true) {289var name = AST_Lambda.prototype.next_mangled.call(this, options, def);290if (!(tricky_def && tricky_def.mangled_name == name))291return name;292}293});294295AST_Scope.DEFMETHOD("references", function(sym){296if (sym instanceof AST_Symbol) sym = sym.definition();297return this.enclosed.indexOf(sym) < 0 ? null : sym;298});299300AST_Symbol.DEFMETHOD("unmangleable", function(options){301return this.definition().unmangleable(options);302});303304// property accessors are not mangleable305AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){306return true;307});308309// labels are always mangleable310AST_Label.DEFMETHOD("unmangleable", function(){311return false;312});313314AST_Symbol.DEFMETHOD("unreferenced", function(){315return this.definition().references.length == 0316&& !(this.scope.uses_eval || this.scope.uses_with);317});318319AST_Symbol.DEFMETHOD("undeclared", function(){320return this.definition().undeclared;321});322323AST_LabelRef.DEFMETHOD("undeclared", function(){324return false;325});326327AST_Label.DEFMETHOD("undeclared", function(){328return false;329});330331AST_Symbol.DEFMETHOD("definition", function(){332return this.thedef;333});334335AST_Symbol.DEFMETHOD("global", function(){336return this.definition().global;337});338339AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){340return defaults(options, {341except : [],342eval : false,343sort : false,344toplevel : false,345screw_ie8 : false,346keep_fnames : false347});348});349350AST_Toplevel.DEFMETHOD("mangle_names", function(options){351options = this._default_mangler_options(options);352// We only need to mangle declaration nodes. Special logic wired353// into the code generator will display the mangled name if it's354// present (and for AST_SymbolRef-s it'll use the mangled name of355// the AST_SymbolDeclaration that it points to).356var lname = -1;357var to_mangle = [];358359if (options.cache) {360this.globals.each(function(symbol){361if (options.except.indexOf(symbol.name) < 0) {362to_mangle.push(symbol);363}364});365}366367var tw = new TreeWalker(function(node, descend){368if (node instanceof AST_LabeledStatement) {369// lname is incremented when we get to the AST_Label370var save_nesting = lname;371descend();372lname = save_nesting;373return true; // don't descend again in TreeWalker374}375if (node instanceof AST_Scope) {376var p = tw.parent(), a = [];377node.variables.each(function(symbol){378if (options.except.indexOf(symbol.name) < 0) {379a.push(symbol);380}381});382if (options.sort) a.sort(function(a, b){383return b.references.length - a.references.length;384});385to_mangle.push.apply(to_mangle, a);386return;387}388if (node instanceof AST_Label) {389var name;390do name = base54(++lname); while (!is_identifier(name));391node.mangled_name = name;392return true;393}394if (options.screw_ie8 && node instanceof AST_SymbolCatch) {395to_mangle.push(node.definition());396return;397}398});399this.walk(tw);400to_mangle.forEach(function(def){ def.mangle(options) });401402if (options.cache) {403options.cache.cname = this.cname;404}405});406407AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){408options = this._default_mangler_options(options);409var tw = new TreeWalker(function(node){410if (node instanceof AST_Constant)411base54.consider(node.print_to_string());412else if (node instanceof AST_Return)413base54.consider("return");414else if (node instanceof AST_Throw)415base54.consider("throw");416else if (node instanceof AST_Continue)417base54.consider("continue");418else if (node instanceof AST_Break)419base54.consider("break");420else if (node instanceof AST_Debugger)421base54.consider("debugger");422else if (node instanceof AST_Directive)423base54.consider(node.value);424else if (node instanceof AST_While)425base54.consider("while");426else if (node instanceof AST_Do)427base54.consider("do while");428else if (node instanceof AST_If) {429base54.consider("if");430if (node.alternative) base54.consider("else");431}432else if (node instanceof AST_Var)433base54.consider("var");434else if (node instanceof AST_Const)435base54.consider("const");436else if (node instanceof AST_Lambda)437base54.consider("function");438else if (node instanceof AST_For)439base54.consider("for");440else if (node instanceof AST_ForIn)441base54.consider("for in");442else if (node instanceof AST_Switch)443base54.consider("switch");444else if (node instanceof AST_Case)445base54.consider("case");446else if (node instanceof AST_Default)447base54.consider("default");448else if (node instanceof AST_With)449base54.consider("with");450else if (node instanceof AST_ObjectSetter)451base54.consider("set" + node.key);452else if (node instanceof AST_ObjectGetter)453base54.consider("get" + node.key);454else if (node instanceof AST_ObjectKeyVal)455base54.consider(node.key);456else if (node instanceof AST_New)457base54.consider("new");458else if (node instanceof AST_This)459base54.consider("this");460else if (node instanceof AST_Try)461base54.consider("try");462else if (node instanceof AST_Catch)463base54.consider("catch");464else if (node instanceof AST_Finally)465base54.consider("finally");466else if (node instanceof AST_Symbol && node.unmangleable(options))467base54.consider(node.name);468else if (node instanceof AST_Unary || node instanceof AST_Binary)469base54.consider(node.operator);470else if (node instanceof AST_Dot)471base54.consider(node.property);472});473this.walk(tw);474base54.sort();475});476477var base54 = (function() {478var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";479var chars, frequency;480function reset() {481frequency = Object.create(null);482chars = string.split("").map(function(ch){ return ch.charCodeAt(0) });483chars.forEach(function(ch){ frequency[ch] = 0 });484}485base54.consider = function(str){486for (var i = str.length; --i >= 0;) {487var code = str.charCodeAt(i);488if (code in frequency) ++frequency[code];489}490};491base54.sort = function() {492chars = mergeSort(chars, function(a, b){493if (is_digit(a) && !is_digit(b)) return 1;494if (is_digit(b) && !is_digit(a)) return -1;495return frequency[b] - frequency[a];496});497};498base54.reset = reset;499reset();500base54.get = function(){ return chars };501base54.freq = function(){ return frequency };502function base54(num) {503var ret = "", base = 54;504num++;505do {506num--;507ret += String.fromCharCode(chars[num % base]);508num = Math.floor(num / base);509base = 64;510} while (num > 0);511return ret;512};513return base54;514})();515516AST_Toplevel.DEFMETHOD("scope_warnings", function(options){517options = defaults(options, {518undeclared : false, // this makes a lot of noise519unreferenced : true,520assign_to_global : true,521func_arguments : true,522nested_defuns : true,523eval : true524});525var tw = new TreeWalker(function(node){526if (options.undeclared527&& node instanceof AST_SymbolRef528&& node.undeclared())529{530// XXX: this also warns about JS standard names,531// i.e. Object, Array, parseInt etc. Should add a list of532// exceptions.533AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", {534name: node.name,535file: node.start.file,536line: node.start.line,537col: node.start.col538});539}540if (options.assign_to_global)541{542var sym = null;543if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef)544sym = node.left;545else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef)546sym = node.init;547if (sym548&& (sym.undeclared()549|| (sym.global() && sym.scope !== sym.definition().scope))) {550AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", {551msg: sym.undeclared() ? "Accidental global?" : "Assignment to global",552name: sym.name,553file: sym.start.file,554line: sym.start.line,555col: sym.start.col556});557}558}559if (options.eval560&& node instanceof AST_SymbolRef561&& node.undeclared()562&& node.name == "eval") {563AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start);564}565if (options.unreferenced566&& (node instanceof AST_SymbolDeclaration || node instanceof AST_Label)567&& !(node instanceof AST_SymbolCatch)568&& node.unreferenced()) {569AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", {570type: node instanceof AST_Label ? "Label" : "Symbol",571name: node.name,572file: node.start.file,573line: node.start.line,574col: node.start.col575});576}577if (options.func_arguments578&& node instanceof AST_Lambda579&& node.uses_arguments) {580AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", {581name: node.name ? node.name.name : "anonymous",582file: node.start.file,583line: node.start.line,584col: node.start.col585});586}587if (options.nested_defuns588&& node instanceof AST_Defun589&& !(tw.parent() instanceof AST_Scope)) {590AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", {591name: node.name.name,592type: tw.parent().TYPE,593file: node.start.file,594line: node.start.line,595col: node.start.col596});597}598});599this.walk(tw);600});601602603