react / react-0.13.3 / examples / basic-commonjs / node_modules / browserify / node_modules / umd / node_modules / ruglify / node_modules / uglify-js / lib / scope.js
80760 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) {59return this.global60|| this.undeclared61|| (!(options && options.eval) && (this.scope.uses_eval || this.scope.uses_with));62},63mangle: function(options) {64if (!this.mangled_name && !this.unmangleable(options))65this.mangled_name = this.scope.next_mangled(options);66}67};6869AST_Toplevel.DEFMETHOD("figure_out_scope", function(){70// This does what ast_add_scope did in UglifyJS v1.71//72// Part of it could be done at parse time, but it would complicate73// the parser (and it's already kinda complex). It's also worth74// having it separated because we might need to call it multiple75// times on the same tree.7677// pass 1: setup scope chaining and handle definitions78var self = this;79var scope = self.parent_scope = null;80var labels = new Dictionary();81var nesting = 0;82var tw = new TreeWalker(function(node, descend){83if (node instanceof AST_Scope) {84node.init_scope_vars(nesting);85var save_scope = node.parent_scope = scope;86var save_labels = labels;87++nesting;88scope = node;89labels = new Dictionary();90descend();91labels = save_labels;92scope = save_scope;93--nesting;94return true; // don't descend again in TreeWalker95}96if (node instanceof AST_Directive) {97node.scope = scope;98push_uniq(scope.directives, node.value);99return true;100}101if (node instanceof AST_With) {102for (var s = scope; s; s = s.parent_scope)103s.uses_with = true;104return;105}106if (node instanceof AST_LabeledStatement) {107var l = node.label;108if (labels.has(l.name))109throw new Error(string_template("Label {name} defined twice", l));110labels.set(l.name, l);111descend();112labels.del(l.name);113return true; // no descend again114}115if (node instanceof AST_Symbol) {116node.scope = scope;117}118if (node instanceof AST_Label) {119node.thedef = node;120node.init_scope_vars();121}122if (node instanceof AST_SymbolLambda) {123//scope.def_function(node);124//125// https://github.com/mishoo/UglifyJS2/issues/24 — MSIE126// leaks function expression names into the containing127// scope. Don't like this fix but seems we can't do any128// better. IE: please die. Please!129(node.scope = scope.parent_scope).def_function(node);130}131else if (node instanceof AST_SymbolDefun) {132// Careful here, the scope where this should be defined is133// the parent scope. The reason is that we enter a new134// scope when we encounter the AST_Defun node (which is135// instanceof AST_Scope) but we get to the symbol a bit136// later.137(node.scope = scope.parent_scope).def_function(node);138}139else if (node instanceof AST_SymbolVar140|| node instanceof AST_SymbolConst) {141var def = scope.def_variable(node);142def.constant = node instanceof AST_SymbolConst;143def.init = tw.parent().value;144}145else if (node instanceof AST_SymbolCatch) {146// XXX: this is wrong according to ECMA-262 (12.4). the147// `catch` argument name should be visible only inside the148// catch block. For a quick fix AST_Catch should inherit149// from AST_Scope. Keeping it this way because of IE,150// which doesn't obey the standard. (it introduces the151// identifier in the enclosing scope)152scope.def_variable(node);153}154if (node instanceof AST_LabelRef) {155var sym = labels.get(node.name);156if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {157name: node.name,158line: node.start.line,159col: node.start.col160}));161node.thedef = sym;162}163});164self.walk(tw);165166// pass 2: find back references and eval167var func = null;168var globals = self.globals = new Dictionary();169var tw = new TreeWalker(function(node, descend){170if (node instanceof AST_Lambda) {171var prev_func = func;172func = node;173descend();174func = prev_func;175return true;176}177if (node instanceof AST_LabelRef) {178node.reference();179return true;180}181if (node instanceof AST_SymbolRef) {182var name = node.name;183var sym = node.scope.find_variable(name);184if (!sym) {185var g;186if (globals.has(name)) {187g = globals.get(name);188} else {189g = new SymbolDef(self, globals.size(), node);190g.undeclared = true;191globals.set(name, g);192}193node.thedef = g;194if (name == "eval" && tw.parent() instanceof AST_Call) {195for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope)196s.uses_eval = true;197}198if (name == "arguments") {199func.uses_arguments = true;200}201} else {202node.thedef = sym;203}204node.reference();205return true;206}207});208self.walk(tw);209});210211AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){212this.directives = []; // contains the directives defined in this scope, i.e. "use strict"213this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)214this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)215this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement216this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`217this.parent_scope = null; // the parent scope218this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes219this.cname = -1; // the current index for mangling functions/variables220this.nesting = nesting; // the nesting level of this scope (0 means toplevel)221});222223AST_Scope.DEFMETHOD("strict", function(){224return this.has_directive("use strict");225});226227AST_Lambda.DEFMETHOD("init_scope_vars", function(){228AST_Scope.prototype.init_scope_vars.apply(this, arguments);229this.uses_arguments = false;230});231232AST_SymbolRef.DEFMETHOD("reference", function() {233var def = this.definition();234def.references.push(this);235var s = this.scope;236while (s) {237push_uniq(s.enclosed, def);238if (s === def.scope) break;239s = s.parent_scope;240}241this.frame = this.scope.nesting - def.scope.nesting;242});243244AST_Label.DEFMETHOD("init_scope_vars", function(){245this.references = [];246});247248AST_LabelRef.DEFMETHOD("reference", function(){249this.thedef.references.push(this);250});251252AST_Scope.DEFMETHOD("find_variable", function(name){253if (name instanceof AST_Symbol) name = name.name;254return this.variables.get(name)255|| (this.parent_scope && this.parent_scope.find_variable(name));256});257258AST_Scope.DEFMETHOD("has_directive", function(value){259return this.parent_scope && this.parent_scope.has_directive(value)260|| (this.directives.indexOf(value) >= 0 ? this : null);261});262263AST_Scope.DEFMETHOD("def_function", function(symbol){264this.functions.set(symbol.name, this.def_variable(symbol));265});266267AST_Scope.DEFMETHOD("def_variable", function(symbol){268var def;269if (!this.variables.has(symbol.name)) {270def = new SymbolDef(this, this.variables.size(), symbol);271this.variables.set(symbol.name, def);272def.global = !this.parent_scope;273} else {274def = this.variables.get(symbol.name);275def.orig.push(symbol);276}277return symbol.thedef = def;278});279280AST_Scope.DEFMETHOD("next_mangled", function(options){281var ext = this.enclosed, n = ext.length;282out: while (true) {283var m = base54(++this.cname);284if (!is_identifier(m)) continue; // skip over "do"285// we must ensure that the mangled name does not shadow a name286// from some parent scope that is referenced in this or in287// inner scopes.288for (var i = n; --i >= 0;) {289var sym = ext[i];290var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);291if (m == name) continue out;292}293return m;294}295});296297AST_Scope.DEFMETHOD("references", function(sym){298if (sym instanceof AST_Symbol) sym = sym.definition();299return this.enclosed.indexOf(sym) < 0 ? null : sym;300});301302AST_Symbol.DEFMETHOD("unmangleable", function(options){303return this.definition().unmangleable(options);304});305306// property accessors are not mangleable307AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){308return true;309});310311// labels are always mangleable312AST_Label.DEFMETHOD("unmangleable", function(){313return false;314});315316AST_Symbol.DEFMETHOD("unreferenced", function(){317return this.definition().references.length == 0318&& !(this.scope.uses_eval || this.scope.uses_with);319});320321AST_Symbol.DEFMETHOD("undeclared", function(){322return this.definition().undeclared;323});324325AST_LabelRef.DEFMETHOD("undeclared", function(){326return false;327});328329AST_Label.DEFMETHOD("undeclared", function(){330return false;331});332333AST_Symbol.DEFMETHOD("definition", function(){334return this.thedef;335});336337AST_Symbol.DEFMETHOD("global", function(){338return this.definition().global;339});340341AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){342return defaults(options, {343except : [],344eval : false,345sort : false346});347});348349AST_Toplevel.DEFMETHOD("mangle_names", function(options){350options = this._default_mangler_options(options);351// We only need to mangle declaration nodes. Special logic wired352// into the code generator will display the mangled name if it's353// present (and for AST_SymbolRef-s it'll use the mangled name of354// the AST_SymbolDeclaration that it points to).355var lname = -1;356var to_mangle = [];357var tw = new TreeWalker(function(node, descend){358if (node instanceof AST_LabeledStatement) {359// lname is incremented when we get to the AST_Label360var save_nesting = lname;361descend();362lname = save_nesting;363return true; // don't descend again in TreeWalker364}365if (node instanceof AST_Scope) {366var p = tw.parent(), a = [];367node.variables.each(function(symbol){368if (options.except.indexOf(symbol.name) < 0) {369a.push(symbol);370}371});372if (options.sort) a.sort(function(a, b){373return b.references.length - a.references.length;374});375to_mangle.push.apply(to_mangle, a);376return;377}378if (node instanceof AST_Label) {379var name;380do name = base54(++lname); while (!is_identifier(name));381node.mangled_name = name;382return true;383}384});385this.walk(tw);386to_mangle.forEach(function(def){ def.mangle(options) });387});388389AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){390options = this._default_mangler_options(options);391var tw = new TreeWalker(function(node){392if (node instanceof AST_Constant)393base54.consider(node.print_to_string());394else if (node instanceof AST_Return)395base54.consider("return");396else if (node instanceof AST_Throw)397base54.consider("throw");398else if (node instanceof AST_Continue)399base54.consider("continue");400else if (node instanceof AST_Break)401base54.consider("break");402else if (node instanceof AST_Debugger)403base54.consider("debugger");404else if (node instanceof AST_Directive)405base54.consider(node.value);406else if (node instanceof AST_While)407base54.consider("while");408else if (node instanceof AST_Do)409base54.consider("do while");410else if (node instanceof AST_If) {411base54.consider("if");412if (node.alternative) base54.consider("else");413}414else if (node instanceof AST_Var)415base54.consider("var");416else if (node instanceof AST_Const)417base54.consider("const");418else if (node instanceof AST_Lambda)419base54.consider("function");420else if (node instanceof AST_For)421base54.consider("for");422else if (node instanceof AST_ForIn)423base54.consider("for in");424else if (node instanceof AST_Switch)425base54.consider("switch");426else if (node instanceof AST_Case)427base54.consider("case");428else if (node instanceof AST_Default)429base54.consider("default");430else if (node instanceof AST_With)431base54.consider("with");432else if (node instanceof AST_ObjectSetter)433base54.consider("set" + node.key);434else if (node instanceof AST_ObjectGetter)435base54.consider("get" + node.key);436else if (node instanceof AST_ObjectKeyVal)437base54.consider(node.key);438else if (node instanceof AST_New)439base54.consider("new");440else if (node instanceof AST_This)441base54.consider("this");442else if (node instanceof AST_Try)443base54.consider("try");444else if (node instanceof AST_Catch)445base54.consider("catch");446else if (node instanceof AST_Finally)447base54.consider("finally");448else if (node instanceof AST_Symbol && node.unmangleable(options))449base54.consider(node.name);450else if (node instanceof AST_Unary || node instanceof AST_Binary)451base54.consider(node.operator);452else if (node instanceof AST_Dot)453base54.consider(node.property);454});455this.walk(tw);456base54.sort();457});458459var base54 = (function() {460var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";461var chars, frequency;462function reset() {463frequency = Object.create(null);464chars = string.split("").map(function(ch){ return ch.charCodeAt(0) });465chars.forEach(function(ch){ frequency[ch] = 0 });466}467base54.consider = function(str){468for (var i = str.length; --i >= 0;) {469var code = str.charCodeAt(i);470if (code in frequency) ++frequency[code];471}472};473base54.sort = function() {474chars = mergeSort(chars, function(a, b){475if (is_digit(a) && !is_digit(b)) return 1;476if (is_digit(b) && !is_digit(a)) return -1;477return frequency[b] - frequency[a];478});479};480base54.reset = reset;481reset();482base54.get = function(){ return chars };483base54.freq = function(){ return frequency };484function base54(num) {485var ret = "", base = 54;486do {487ret += String.fromCharCode(chars[num % base]);488num = Math.floor(num / base);489base = 64;490} while (num > 0);491return ret;492};493return base54;494})();495496AST_Toplevel.DEFMETHOD("scope_warnings", function(options){497options = defaults(options, {498undeclared : false, // this makes a lot of noise499unreferenced : true,500assign_to_global : true,501func_arguments : true,502nested_defuns : true,503eval : true504});505var tw = new TreeWalker(function(node){506if (options.undeclared507&& node instanceof AST_SymbolRef508&& node.undeclared())509{510// XXX: this also warns about JS standard names,511// i.e. Object, Array, parseInt etc. Should add a list of512// exceptions.513AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", {514name: node.name,515file: node.start.file,516line: node.start.line,517col: node.start.col518});519}520if (options.assign_to_global)521{522var sym = null;523if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef)524sym = node.left;525else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef)526sym = node.init;527if (sym528&& (sym.undeclared()529|| (sym.global() && sym.scope !== sym.definition().scope))) {530AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", {531msg: sym.undeclared() ? "Accidental global?" : "Assignment to global",532name: sym.name,533file: sym.start.file,534line: sym.start.line,535col: sym.start.col536});537}538}539if (options.eval540&& node instanceof AST_SymbolRef541&& node.undeclared()542&& node.name == "eval") {543AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start);544}545if (options.unreferenced546&& (node instanceof AST_SymbolDeclaration || node instanceof AST_Label)547&& node.unreferenced()) {548AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", {549type: node instanceof AST_Label ? "Label" : "Symbol",550name: node.name,551file: node.start.file,552line: node.start.line,553col: node.start.col554});555}556if (options.func_arguments557&& node instanceof AST_Lambda558&& node.uses_arguments) {559AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", {560name: node.name ? node.name.name : "anonymous",561file: node.start.file,562line: node.start.line,563col: node.start.col564});565}566if (options.nested_defuns567&& node instanceof AST_Defun568&& !(tw.parent() instanceof AST_Scope)) {569AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", {570name: node.name.name,571type: tw.parent().TYPE,572file: node.start.file,573line: node.start.line,574col: node.start.col575});576}577});578this.walk(tw);579});580581582