react / wstein / node_modules / jest-cli / node_modules / istanbul / node_modules / handlebars / node_modules / uglify-js / lib / compress.js
80713 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 Compressor(options, false_by_default) {46if (!(this instanceof Compressor))47return new Compressor(options, false_by_default);48TreeTransformer.call(this, this.before, this.after);49this.options = defaults(options, {50sequences : !false_by_default,51properties : !false_by_default,52dead_code : !false_by_default,53drop_debugger : !false_by_default,54unsafe : false,55unsafe_comps : false,56conditionals : !false_by_default,57comparisons : !false_by_default,58evaluate : !false_by_default,59booleans : !false_by_default,60loops : !false_by_default,61unused : !false_by_default,62hoist_funs : !false_by_default,63hoist_vars : false,64if_return : !false_by_default,65join_vars : !false_by_default,66cascade : !false_by_default,67side_effects : !false_by_default,68screw_ie8 : false,6970warnings : true,71global_defs : {}72}, true);73};7475Compressor.prototype = new TreeTransformer;76merge(Compressor.prototype, {77option: function(key) { return this.options[key] },78warn: function() {79if (this.options.warnings)80AST_Node.warn.apply(AST_Node, arguments);81},82before: function(node, descend, in_list) {83if (node._squeezed) return node;84if (node instanceof AST_Scope) {85node.drop_unused(this);86node = node.hoist_declarations(this);87}88descend(node, this);89node = node.optimize(this);90if (node instanceof AST_Scope) {91// dead code removal might leave further unused declarations.92// this'll usually save very few bytes, but the performance93// hit seems negligible so I'll just drop it here.9495// no point to repeat warnings.96var save_warnings = this.options.warnings;97this.options.warnings = false;98node.drop_unused(this);99this.options.warnings = save_warnings;100}101node._squeezed = true;102return node;103}104});105106(function(){107108function OPT(node, optimizer) {109node.DEFMETHOD("optimize", function(compressor){110var self = this;111if (self._optimized) return self;112var opt = optimizer(self, compressor);113opt._optimized = true;114if (opt === self) return opt;115return opt.transform(compressor);116});117};118119OPT(AST_Node, function(self, compressor){120return self;121});122123AST_Node.DEFMETHOD("equivalent_to", function(node){124// XXX: this is a rather expensive way to test two node's equivalence:125return this.print_to_string() == node.print_to_string();126});127128function make_node(ctor, orig, props) {129if (!props) props = {};130if (orig) {131if (!props.start) props.start = orig.start;132if (!props.end) props.end = orig.end;133}134return new ctor(props);135};136137function make_node_from_constant(compressor, val, orig) {138// XXX: WIP.139// if (val instanceof AST_Node) return val.transform(new TreeTransformer(null, function(node){140// if (node instanceof AST_SymbolRef) {141// var scope = compressor.find_parent(AST_Scope);142// var def = scope.find_variable(node);143// node.thedef = def;144// return node;145// }146// })).transform(compressor);147148if (val instanceof AST_Node) return val.transform(compressor);149switch (typeof val) {150case "string":151return make_node(AST_String, orig, {152value: val153}).optimize(compressor);154case "number":155return make_node(isNaN(val) ? AST_NaN : AST_Number, orig, {156value: val157}).optimize(compressor);158case "boolean":159return make_node(val ? AST_True : AST_False, orig).optimize(compressor);160case "undefined":161return make_node(AST_Undefined, orig).optimize(compressor);162default:163if (val === null) {164return make_node(AST_Null, orig).optimize(compressor);165}166if (val instanceof RegExp) {167return make_node(AST_RegExp, orig).optimize(compressor);168}169throw new Error(string_template("Can't handle constant of type: {type}", {170type: typeof val171}));172}173};174175function as_statement_array(thing) {176if (thing === null) return [];177if (thing instanceof AST_BlockStatement) return thing.body;178if (thing instanceof AST_EmptyStatement) return [];179if (thing instanceof AST_Statement) return [ thing ];180throw new Error("Can't convert thing to statement array");181};182183function is_empty(thing) {184if (thing === null) return true;185if (thing instanceof AST_EmptyStatement) return true;186if (thing instanceof AST_BlockStatement) return thing.body.length == 0;187return false;188};189190function loop_body(x) {191if (x instanceof AST_Switch) return x;192if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {193return (x.body instanceof AST_BlockStatement ? x.body : x);194}195return x;196};197198function tighten_body(statements, compressor) {199var CHANGED;200do {201CHANGED = false;202statements = eliminate_spurious_blocks(statements);203if (compressor.option("dead_code")) {204statements = eliminate_dead_code(statements, compressor);205}206if (compressor.option("if_return")) {207statements = handle_if_return(statements, compressor);208}209if (compressor.option("sequences")) {210statements = sequencesize(statements, compressor);211}212if (compressor.option("join_vars")) {213statements = join_consecutive_vars(statements, compressor);214}215} while (CHANGED);216return statements;217218function eliminate_spurious_blocks(statements) {219var seen_dirs = [];220return statements.reduce(function(a, stat){221if (stat instanceof AST_BlockStatement) {222CHANGED = true;223a.push.apply(a, eliminate_spurious_blocks(stat.body));224} else if (stat instanceof AST_EmptyStatement) {225CHANGED = true;226} else if (stat instanceof AST_Directive) {227if (seen_dirs.indexOf(stat.value) < 0) {228a.push(stat);229seen_dirs.push(stat.value);230} else {231CHANGED = true;232}233} else {234a.push(stat);235}236return a;237}, []);238};239240function handle_if_return(statements, compressor) {241var self = compressor.self();242var in_lambda = self instanceof AST_Lambda;243var ret = [];244loop: for (var i = statements.length; --i >= 0;) {245var stat = statements[i];246switch (true) {247case (in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0):248CHANGED = true;249// note, ret.length is probably always zero250// because we drop unreachable code before this251// step. nevertheless, it's good to check.252continue loop;253case stat instanceof AST_If:254if (stat.body instanceof AST_Return) {255//---256// pretty silly case, but:257// if (foo()) return; return; ==> foo(); return;258if (((in_lambda && ret.length == 0)259|| (ret[0] instanceof AST_Return && !ret[0].value))260&& !stat.body.value && !stat.alternative) {261CHANGED = true;262var cond = make_node(AST_SimpleStatement, stat.condition, {263body: stat.condition264});265ret.unshift(cond);266continue loop;267}268//---269// if (foo()) return x; return y; ==> return foo() ? x : y;270if (ret[0] instanceof AST_Return && stat.body.value && ret[0].value && !stat.alternative) {271CHANGED = true;272stat = stat.clone();273stat.alternative = ret[0];274ret[0] = stat.transform(compressor);275continue loop;276}277//---278// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;279if ((ret.length == 0 || ret[0] instanceof AST_Return) && stat.body.value && !stat.alternative && in_lambda) {280CHANGED = true;281stat = stat.clone();282stat.alternative = ret[0] || make_node(AST_Return, stat, {283value: make_node(AST_Undefined, stat)284});285ret[0] = stat.transform(compressor);286continue loop;287}288//---289// if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }290if (!stat.body.value && in_lambda) {291CHANGED = true;292stat = stat.clone();293stat.condition = stat.condition.negate(compressor);294stat.body = make_node(AST_BlockStatement, stat, {295body: as_statement_array(stat.alternative).concat(ret)296});297stat.alternative = null;298ret = [ stat.transform(compressor) ];299continue loop;300}301//---302if (ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement303&& (!stat.alternative || stat.alternative instanceof AST_SimpleStatement)) {304CHANGED = true;305ret.push(make_node(AST_Return, ret[0], {306value: make_node(AST_Undefined, ret[0])307}).transform(compressor));308ret = as_statement_array(stat.alternative).concat(ret);309ret.unshift(stat);310continue loop;311}312}313314var ab = aborts(stat.body);315var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;316if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)317|| (ab instanceof AST_Continue && self === loop_body(lct))318|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {319if (ab.label) {320remove(ab.label.thedef.references, ab.label);321}322CHANGED = true;323var body = as_statement_array(stat.body).slice(0, -1);324stat = stat.clone();325stat.condition = stat.condition.negate(compressor);326stat.body = make_node(AST_BlockStatement, stat, {327body: ret328});329stat.alternative = make_node(AST_BlockStatement, stat, {330body: body331});332ret = [ stat.transform(compressor) ];333continue loop;334}335336var ab = aborts(stat.alternative);337var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;338if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)339|| (ab instanceof AST_Continue && self === loop_body(lct))340|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {341if (ab.label) {342remove(ab.label.thedef.references, ab.label);343}344CHANGED = true;345stat = stat.clone();346stat.body = make_node(AST_BlockStatement, stat.body, {347body: as_statement_array(stat.body).concat(ret)348});349stat.alternative = make_node(AST_BlockStatement, stat.alternative, {350body: as_statement_array(stat.alternative).slice(0, -1)351});352ret = [ stat.transform(compressor) ];353continue loop;354}355356ret.unshift(stat);357break;358default:359ret.unshift(stat);360break;361}362}363return ret;364};365366function eliminate_dead_code(statements, compressor) {367var has_quit = false;368var orig = statements.length;369var self = compressor.self();370statements = statements.reduce(function(a, stat){371if (has_quit) {372extract_declarations_from_unreachable_code(compressor, stat, a);373} else {374if (stat instanceof AST_LoopControl) {375var lct = compressor.loopcontrol_target(stat.label);376if ((stat instanceof AST_Break377&& lct instanceof AST_BlockStatement378&& loop_body(lct) === self) || (stat instanceof AST_Continue379&& loop_body(lct) === self)) {380if (stat.label) {381remove(stat.label.thedef.references, stat.label);382}383} else {384a.push(stat);385}386} else {387a.push(stat);388}389if (aborts(stat)) has_quit = true;390}391return a;392}, []);393CHANGED = statements.length != orig;394return statements;395};396397function sequencesize(statements, compressor) {398if (statements.length < 2) return statements;399var seq = [], ret = [];400function push_seq() {401seq = AST_Seq.from_array(seq);402if (seq) ret.push(make_node(AST_SimpleStatement, seq, {403body: seq404}));405seq = [];406};407statements.forEach(function(stat){408if (stat instanceof AST_SimpleStatement) seq.push(stat.body);409else push_seq(), ret.push(stat);410});411push_seq();412ret = sequencesize_2(ret, compressor);413CHANGED = ret.length != statements.length;414return ret;415};416417function sequencesize_2(statements, compressor) {418function cons_seq(right) {419ret.pop();420var left = prev.body;421if (left instanceof AST_Seq) {422left.add(right);423} else {424left = AST_Seq.cons(left, right);425}426return left.transform(compressor);427};428var ret = [], prev = null;429statements.forEach(function(stat){430if (prev) {431if (stat instanceof AST_For) {432var opera = {};433try {434prev.body.walk(new TreeWalker(function(node){435if (node instanceof AST_Binary && node.operator == "in")436throw opera;437}));438if (stat.init && !(stat.init instanceof AST_Definitions)) {439stat.init = cons_seq(stat.init);440}441else if (!stat.init) {442stat.init = prev.body;443ret.pop();444}445} catch(ex) {446if (ex !== opera) throw ex;447}448}449else if (stat instanceof AST_If) {450stat.condition = cons_seq(stat.condition);451}452else if (stat instanceof AST_With) {453stat.expression = cons_seq(stat.expression);454}455else if (stat instanceof AST_Exit && stat.value) {456stat.value = cons_seq(stat.value);457}458else if (stat instanceof AST_Exit) {459stat.value = cons_seq(make_node(AST_Undefined, stat));460}461else if (stat instanceof AST_Switch) {462stat.expression = cons_seq(stat.expression);463}464}465ret.push(stat);466prev = stat instanceof AST_SimpleStatement ? stat : null;467});468return ret;469};470471function join_consecutive_vars(statements, compressor) {472var prev = null;473return statements.reduce(function(a, stat){474if (stat instanceof AST_Definitions && prev && prev.TYPE == stat.TYPE) {475prev.definitions = prev.definitions.concat(stat.definitions);476CHANGED = true;477}478else if (stat instanceof AST_For479&& prev instanceof AST_Definitions480&& (!stat.init || stat.init.TYPE == prev.TYPE)) {481CHANGED = true;482a.pop();483if (stat.init) {484stat.init.definitions = prev.definitions.concat(stat.init.definitions);485} else {486stat.init = prev;487}488a.push(stat);489prev = stat;490}491else {492prev = stat;493a.push(stat);494}495return a;496}, []);497};498499};500501function extract_declarations_from_unreachable_code(compressor, stat, target) {502compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);503stat.walk(new TreeWalker(function(node){504if (node instanceof AST_Definitions) {505compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);506node.remove_initializers();507target.push(node);508return true;509}510if (node instanceof AST_Defun) {511target.push(node);512return true;513}514if (node instanceof AST_Scope) {515return true;516}517}));518};519520/* -----[ boolean/negation helpers ]----- */521522// methods to determine whether an expression has a boolean result type523(function (def){524var unary_bool = [ "!", "delete" ];525var binary_bool = [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ];526def(AST_Node, function(){ return false });527def(AST_UnaryPrefix, function(){528return member(this.operator, unary_bool);529});530def(AST_Binary, function(){531return member(this.operator, binary_bool) ||532( (this.operator == "&&" || this.operator == "||") &&533this.left.is_boolean() && this.right.is_boolean() );534});535def(AST_Conditional, function(){536return this.consequent.is_boolean() && this.alternative.is_boolean();537});538def(AST_Assign, function(){539return this.operator == "=" && this.right.is_boolean();540});541def(AST_Seq, function(){542return this.cdr.is_boolean();543});544def(AST_True, function(){ return true });545def(AST_False, function(){ return true });546})(function(node, func){547node.DEFMETHOD("is_boolean", func);548});549550// methods to determine if an expression has a string result type551(function (def){552def(AST_Node, function(){ return false });553def(AST_String, function(){ return true });554def(AST_UnaryPrefix, function(){555return this.operator == "typeof";556});557def(AST_Binary, function(compressor){558return this.operator == "+" &&559(this.left.is_string(compressor) || this.right.is_string(compressor));560});561def(AST_Assign, function(compressor){562return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);563});564def(AST_Seq, function(compressor){565return this.cdr.is_string(compressor);566});567def(AST_Conditional, function(compressor){568return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);569});570def(AST_Call, function(compressor){571return compressor.option("unsafe")572&& this.expression instanceof AST_SymbolRef573&& this.expression.name == "String"574&& this.expression.undeclared();575});576})(function(node, func){577node.DEFMETHOD("is_string", func);578});579580function best_of(ast1, ast2) {581return ast1.print_to_string().length >582ast2.print_to_string().length583? ast2 : ast1;584};585586// methods to evaluate a constant expression587(function (def){588// The evaluate method returns an array with one or two589// elements. If the node has been successfully reduced to a590// constant, then the second element tells us the value;591// otherwise the second element is missing. The first element592// of the array is always an AST_Node descendant; when593// evaluation was successful it's a node that represents the594// constant; otherwise it's the original node.595AST_Node.DEFMETHOD("evaluate", function(compressor){596if (!compressor.option("evaluate")) return [ this ];597try {598var val = this._eval(), ast = make_node_from_constant(compressor, val, this);599return [ best_of(ast, this), val ];600} catch(ex) {601if (ex !== def) throw ex;602return [ this ];603}604});605def(AST_Statement, function(){606throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));607});608def(AST_Function, function(){609// XXX: AST_Function inherits from AST_Scope, which itself610// inherits from AST_Statement; however, an AST_Function611// isn't really a statement. This could byte in other612// places too. :-( Wish JS had multiple inheritance.613return [ this ];614});615function ev(node) {616return node._eval();617};618def(AST_Node, function(){619throw def; // not constant620});621def(AST_Constant, function(){622return this.getValue();623});624def(AST_UnaryPrefix, function(){625var e = this.expression;626switch (this.operator) {627case "!": return !ev(e);628case "typeof":629// Function would be evaluated to an array and so typeof would630// incorrectly return 'object'. Hence making is a special case.631if (e instanceof AST_Function) return typeof function(){};632633e = ev(e);634635// typeof <RegExp> returns "object" or "function" on different platforms636// so cannot evaluate reliably637if (e instanceof RegExp) throw def;638639return typeof e;640case "void": return void ev(e);641case "~": return ~ev(e);642case "-":643e = ev(e);644if (e === 0) throw def;645return -e;646case "+": return +ev(e);647}648throw def;649});650def(AST_Binary, function(){651var left = this.left, right = this.right;652switch (this.operator) {653case "&&" : return ev(left) && ev(right);654case "||" : return ev(left) || ev(right);655case "|" : return ev(left) | ev(right);656case "&" : return ev(left) & ev(right);657case "^" : return ev(left) ^ ev(right);658case "+" : return ev(left) + ev(right);659case "*" : return ev(left) * ev(right);660case "/" : return ev(left) / ev(right);661case "%" : return ev(left) % ev(right);662case "-" : return ev(left) - ev(right);663case "<<" : return ev(left) << ev(right);664case ">>" : return ev(left) >> ev(right);665case ">>>" : return ev(left) >>> ev(right);666case "==" : return ev(left) == ev(right);667case "===" : return ev(left) === ev(right);668case "!=" : return ev(left) != ev(right);669case "!==" : return ev(left) !== ev(right);670case "<" : return ev(left) < ev(right);671case "<=" : return ev(left) <= ev(right);672case ">" : return ev(left) > ev(right);673case ">=" : return ev(left) >= ev(right);674case "in" : return ev(left) in ev(right);675case "instanceof" : return ev(left) instanceof ev(right);676}677throw def;678});679def(AST_Conditional, function(){680return ev(this.condition)681? ev(this.consequent)682: ev(this.alternative);683});684def(AST_SymbolRef, function(){685var d = this.definition();686if (d && d.constant && d.init) return ev(d.init);687throw def;688});689})(function(node, func){690node.DEFMETHOD("_eval", func);691});692693// method to negate an expression694(function(def){695function basic_negation(exp) {696return make_node(AST_UnaryPrefix, exp, {697operator: "!",698expression: exp699});700};701def(AST_Node, function(){702return basic_negation(this);703});704def(AST_Statement, function(){705throw new Error("Cannot negate a statement");706});707def(AST_Function, function(){708return basic_negation(this);709});710def(AST_UnaryPrefix, function(){711if (this.operator == "!")712return this.expression;713return basic_negation(this);714});715def(AST_Seq, function(compressor){716var self = this.clone();717self.cdr = self.cdr.negate(compressor);718return self;719});720def(AST_Conditional, function(compressor){721var self = this.clone();722self.consequent = self.consequent.negate(compressor);723self.alternative = self.alternative.negate(compressor);724return best_of(basic_negation(this), self);725});726def(AST_Binary, function(compressor){727var self = this.clone(), op = this.operator;728if (compressor.option("unsafe_comps")) {729switch (op) {730case "<=" : self.operator = ">" ; return self;731case "<" : self.operator = ">=" ; return self;732case ">=" : self.operator = "<" ; return self;733case ">" : self.operator = "<=" ; return self;734}735}736switch (op) {737case "==" : self.operator = "!="; return self;738case "!=" : self.operator = "=="; return self;739case "===": self.operator = "!=="; return self;740case "!==": self.operator = "==="; return self;741case "&&":742self.operator = "||";743self.left = self.left.negate(compressor);744self.right = self.right.negate(compressor);745return best_of(basic_negation(this), self);746case "||":747self.operator = "&&";748self.left = self.left.negate(compressor);749self.right = self.right.negate(compressor);750return best_of(basic_negation(this), self);751}752return basic_negation(this);753});754})(function(node, func){755node.DEFMETHOD("negate", function(compressor){756return func.call(this, compressor);757});758});759760// determine if expression has side effects761(function(def){762def(AST_Node, function(){ return true });763764def(AST_EmptyStatement, function(){ return false });765def(AST_Constant, function(){ return false });766def(AST_This, function(){ return false });767768def(AST_Block, function(){769for (var i = this.body.length; --i >= 0;) {770if (this.body[i].has_side_effects())771return true;772}773return false;774});775776def(AST_SimpleStatement, function(){777return this.body.has_side_effects();778});779def(AST_Defun, function(){ return true });780def(AST_Function, function(){ return false });781def(AST_Binary, function(){782return this.left.has_side_effects()783|| this.right.has_side_effects();784});785def(AST_Assign, function(){ return true });786def(AST_Conditional, function(){787return this.condition.has_side_effects()788|| this.consequent.has_side_effects()789|| this.alternative.has_side_effects();790});791def(AST_Unary, function(){792return this.operator == "delete"793|| this.operator == "++"794|| this.operator == "--"795|| this.expression.has_side_effects();796});797def(AST_SymbolRef, function(){ return false });798def(AST_Object, function(){799for (var i = this.properties.length; --i >= 0;)800if (this.properties[i].has_side_effects())801return true;802return false;803});804def(AST_ObjectProperty, function(){805return this.value.has_side_effects();806});807def(AST_Array, function(){808for (var i = this.elements.length; --i >= 0;)809if (this.elements[i].has_side_effects())810return true;811return false;812});813// def(AST_Dot, function(){814// return this.expression.has_side_effects();815// });816// def(AST_Sub, function(){817// return this.expression.has_side_effects()818// || this.property.has_side_effects();819// });820def(AST_PropAccess, function(){821return true;822});823def(AST_Seq, function(){824return this.car.has_side_effects()825|| this.cdr.has_side_effects();826});827})(function(node, func){828node.DEFMETHOD("has_side_effects", func);829});830831// tell me if a statement aborts832function aborts(thing) {833return thing && thing.aborts();834};835(function(def){836def(AST_Statement, function(){ return null });837def(AST_Jump, function(){ return this });838function block_aborts(){839var n = this.body.length;840return n > 0 && aborts(this.body[n - 1]);841};842def(AST_BlockStatement, block_aborts);843def(AST_SwitchBranch, block_aborts);844def(AST_If, function(){845return this.alternative && aborts(this.body) && aborts(this.alternative);846});847})(function(node, func){848node.DEFMETHOD("aborts", func);849});850851/* -----[ optimizers ]----- */852853OPT(AST_Directive, function(self, compressor){854if (self.scope.has_directive(self.value) !== self.scope) {855return make_node(AST_EmptyStatement, self);856}857return self;858});859860OPT(AST_Debugger, function(self, compressor){861if (compressor.option("drop_debugger"))862return make_node(AST_EmptyStatement, self);863return self;864});865866OPT(AST_LabeledStatement, function(self, compressor){867if (self.body instanceof AST_Break868&& compressor.loopcontrol_target(self.body.label) === self.body) {869return make_node(AST_EmptyStatement, self);870}871return self.label.references.length == 0 ? self.body : self;872});873874OPT(AST_Block, function(self, compressor){875self.body = tighten_body(self.body, compressor);876return self;877});878879OPT(AST_BlockStatement, function(self, compressor){880self.body = tighten_body(self.body, compressor);881switch (self.body.length) {882case 1: return self.body[0];883case 0: return make_node(AST_EmptyStatement, self);884}885return self;886});887888AST_Scope.DEFMETHOD("drop_unused", function(compressor){889var self = this;890if (compressor.option("unused")891&& !(self instanceof AST_Toplevel)892&& !self.uses_eval893) {894var in_use = [];895var initializations = new Dictionary();896// pass 1: find out which symbols are directly used in897// this scope (not in nested scopes).898var scope = this;899var tw = new TreeWalker(function(node, descend){900if (node !== self) {901if (node instanceof AST_Defun) {902initializations.add(node.name.name, node);903return true; // don't go in nested scopes904}905if (node instanceof AST_Definitions && scope === self) {906node.definitions.forEach(function(def){907if (def.value) {908initializations.add(def.name.name, def.value);909if (def.value.has_side_effects()) {910def.value.walk(tw);911}912}913});914return true;915}916if (node instanceof AST_SymbolRef) {917push_uniq(in_use, node.definition());918return true;919}920if (node instanceof AST_Scope) {921var save_scope = scope;922scope = node;923descend();924scope = save_scope;925return true;926}927}928});929self.walk(tw);930// pass 2: for every used symbol we need to walk its931// initialization code to figure out if it uses other932// symbols (that may not be in_use).933for (var i = 0; i < in_use.length; ++i) {934in_use[i].orig.forEach(function(decl){935// undeclared globals will be instanceof AST_SymbolRef936var init = initializations.get(decl.name);937if (init) init.forEach(function(init){938var tw = new TreeWalker(function(node){939if (node instanceof AST_SymbolRef) {940push_uniq(in_use, node.definition());941}942});943init.walk(tw);944});945});946}947// pass 3: we should drop declarations not in_use948var tt = new TreeTransformer(949function before(node, descend, in_list) {950if (node instanceof AST_Lambda) {951for (var a = node.argnames, i = a.length; --i >= 0;) {952var sym = a[i];953if (sym.unreferenced()) {954a.pop();955compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {956name : sym.name,957file : sym.start.file,958line : sym.start.line,959col : sym.start.col960});961}962else break;963}964}965if (node instanceof AST_Defun && node !== self) {966if (!member(node.name.definition(), in_use)) {967compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {968name : node.name.name,969file : node.name.start.file,970line : node.name.start.line,971col : node.name.start.col972});973return make_node(AST_EmptyStatement, node);974}975return node;976}977if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {978var def = node.definitions.filter(function(def){979if (member(def.name.definition(), in_use)) return true;980var w = {981name : def.name.name,982file : def.name.start.file,983line : def.name.start.line,984col : def.name.start.col985};986if (def.value && def.value.has_side_effects()) {987def._unused_side_effects = true;988compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);989return true;990}991compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w);992return false;993});994// place uninitialized names at the start995def = mergeSort(def, function(a, b){996if (!a.value && b.value) return -1;997if (!b.value && a.value) return 1;998return 0;999});1000// for unused names whose initialization has1001// side effects, we can cascade the init. code1002// into the next one, or next statement.1003var side_effects = [];1004for (var i = 0; i < def.length;) {1005var x = def[i];1006if (x._unused_side_effects) {1007side_effects.push(x.value);1008def.splice(i, 1);1009} else {1010if (side_effects.length > 0) {1011side_effects.push(x.value);1012x.value = AST_Seq.from_array(side_effects);1013side_effects = [];1014}1015++i;1016}1017}1018if (side_effects.length > 0) {1019side_effects = make_node(AST_BlockStatement, node, {1020body: [ make_node(AST_SimpleStatement, node, {1021body: AST_Seq.from_array(side_effects)1022}) ]1023});1024} else {1025side_effects = null;1026}1027if (def.length == 0 && !side_effects) {1028return make_node(AST_EmptyStatement, node);1029}1030if (def.length == 0) {1031return side_effects;1032}1033node.definitions = def;1034if (side_effects) {1035side_effects.body.unshift(node);1036node = side_effects;1037}1038return node;1039}1040if (node instanceof AST_For && node.init instanceof AST_BlockStatement) {1041descend(node, this);1042// certain combination of unused name + side effect leads to:1043// https://github.com/mishoo/UglifyJS2/issues/441044// that's an invalid AST.1045// We fix it at this stage by moving the `var` outside the `for`.1046var body = node.init.body.slice(0, -1);1047node.init = node.init.body.slice(-1)[0].body;1048body.push(node);1049return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {1050body: body1051});1052}1053if (node instanceof AST_Scope && node !== self)1054return node;1055}1056);1057self.transform(tt);1058}1059});10601061AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){1062var hoist_funs = compressor.option("hoist_funs");1063var hoist_vars = compressor.option("hoist_vars");1064var self = this;1065if (hoist_funs || hoist_vars) {1066var dirs = [];1067var hoisted = [];1068var vars = new Dictionary(), vars_found = 0, var_decl = 0;1069// let's count var_decl first, we seem to waste a lot of1070// space if we hoist `var` when there's only one.1071self.walk(new TreeWalker(function(node){1072if (node instanceof AST_Scope && node !== self)1073return true;1074if (node instanceof AST_Var) {1075++var_decl;1076return true;1077}1078}));1079hoist_vars = hoist_vars && var_decl > 1;1080var tt = new TreeTransformer(1081function before(node) {1082if (node !== self) {1083if (node instanceof AST_Directive) {1084dirs.push(node);1085return make_node(AST_EmptyStatement, node);1086}1087if (node instanceof AST_Defun && hoist_funs) {1088hoisted.push(node);1089return make_node(AST_EmptyStatement, node);1090}1091if (node instanceof AST_Var && hoist_vars) {1092node.definitions.forEach(function(def){1093vars.set(def.name.name, def);1094++vars_found;1095});1096var seq = node.to_assignments();1097var p = tt.parent();1098if (p instanceof AST_ForIn && p.init === node) {1099if (seq == null) return node.definitions[0].name;1100return seq;1101}1102if (p instanceof AST_For && p.init === node) {1103return seq;1104}1105if (!seq) return make_node(AST_EmptyStatement, node);1106return make_node(AST_SimpleStatement, node, {1107body: seq1108});1109}1110if (node instanceof AST_Scope)1111return node; // to avoid descending in nested scopes1112}1113}1114);1115self = self.transform(tt);1116if (vars_found > 0) {1117// collect only vars which don't show up in self's arguments list1118var defs = [];1119vars.each(function(def, name){1120if (self instanceof AST_Lambda1121&& find_if(function(x){ return x.name == def.name.name },1122self.argnames)) {1123vars.del(name);1124} else {1125def = def.clone();1126def.value = null;1127defs.push(def);1128vars.set(name, def);1129}1130});1131if (defs.length > 0) {1132// try to merge in assignments1133for (var i = 0; i < self.body.length;) {1134if (self.body[i] instanceof AST_SimpleStatement) {1135var expr = self.body[i].body, sym, assign;1136if (expr instanceof AST_Assign1137&& expr.operator == "="1138&& (sym = expr.left) instanceof AST_Symbol1139&& vars.has(sym.name))1140{1141var def = vars.get(sym.name);1142if (def.value) break;1143def.value = expr.right;1144remove(defs, def);1145defs.push(def);1146self.body.splice(i, 1);1147continue;1148}1149if (expr instanceof AST_Seq1150&& (assign = expr.car) instanceof AST_Assign1151&& assign.operator == "="1152&& (sym = assign.left) instanceof AST_Symbol1153&& vars.has(sym.name))1154{1155var def = vars.get(sym.name);1156if (def.value) break;1157def.value = assign.right;1158remove(defs, def);1159defs.push(def);1160self.body[i].body = expr.cdr;1161continue;1162}1163}1164if (self.body[i] instanceof AST_EmptyStatement) {1165self.body.splice(i, 1);1166continue;1167}1168if (self.body[i] instanceof AST_BlockStatement) {1169var tmp = [ i, 1 ].concat(self.body[i].body);1170self.body.splice.apply(self.body, tmp);1171continue;1172}1173break;1174}1175defs = make_node(AST_Var, self, {1176definitions: defs1177});1178hoisted.push(defs);1179};1180}1181self.body = dirs.concat(hoisted, self.body);1182}1183return self;1184});11851186OPT(AST_SimpleStatement, function(self, compressor){1187if (compressor.option("side_effects")) {1188if (!self.body.has_side_effects()) {1189compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);1190return make_node(AST_EmptyStatement, self);1191}1192}1193return self;1194});11951196OPT(AST_DWLoop, function(self, compressor){1197var cond = self.condition.evaluate(compressor);1198self.condition = cond[0];1199if (!compressor.option("loops")) return self;1200if (cond.length > 1) {1201if (cond[1]) {1202return make_node(AST_For, self, {1203body: self.body1204});1205} else if (self instanceof AST_While) {1206if (compressor.option("dead_code")) {1207var a = [];1208extract_declarations_from_unreachable_code(compressor, self.body, a);1209return make_node(AST_BlockStatement, self, { body: a });1210}1211}1212}1213return self;1214});12151216function if_break_in_loop(self, compressor) {1217function drop_it(rest) {1218rest = as_statement_array(rest);1219if (self.body instanceof AST_BlockStatement) {1220self.body = self.body.clone();1221self.body.body = rest.concat(self.body.body.slice(1));1222self.body = self.body.transform(compressor);1223} else {1224self.body = make_node(AST_BlockStatement, self.body, {1225body: rest1226}).transform(compressor);1227}1228if_break_in_loop(self, compressor);1229}1230var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;1231if (first instanceof AST_If) {1232if (first.body instanceof AST_Break1233&& compressor.loopcontrol_target(first.body.label) === self) {1234if (self.condition) {1235self.condition = make_node(AST_Binary, self.condition, {1236left: self.condition,1237operator: "&&",1238right: first.condition.negate(compressor),1239});1240} else {1241self.condition = first.condition.negate(compressor);1242}1243drop_it(first.alternative);1244}1245else if (first.alternative instanceof AST_Break1246&& compressor.loopcontrol_target(first.alternative.label) === self) {1247if (self.condition) {1248self.condition = make_node(AST_Binary, self.condition, {1249left: self.condition,1250operator: "&&",1251right: first.condition,1252});1253} else {1254self.condition = first.condition;1255}1256drop_it(first.body);1257}1258}1259};12601261OPT(AST_While, function(self, compressor) {1262if (!compressor.option("loops")) return self;1263self = AST_DWLoop.prototype.optimize.call(self, compressor);1264if (self instanceof AST_While) {1265if_break_in_loop(self, compressor);1266self = make_node(AST_For, self, self).transform(compressor);1267}1268return self;1269});12701271OPT(AST_For, function(self, compressor){1272var cond = self.condition;1273if (cond) {1274cond = cond.evaluate(compressor);1275self.condition = cond[0];1276}1277if (!compressor.option("loops")) return self;1278if (cond) {1279if (cond.length > 1 && !cond[1]) {1280if (compressor.option("dead_code")) {1281var a = [];1282if (self.init instanceof AST_Statement) {1283a.push(self.init);1284}1285else if (self.init) {1286a.push(make_node(AST_SimpleStatement, self.init, {1287body: self.init1288}));1289}1290extract_declarations_from_unreachable_code(compressor, self.body, a);1291return make_node(AST_BlockStatement, self, { body: a });1292}1293}1294}1295if_break_in_loop(self, compressor);1296return self;1297});12981299OPT(AST_If, function(self, compressor){1300if (!compressor.option("conditionals")) return self;1301// if condition can be statically determined, warn and drop1302// one of the blocks. note, statically determined implies1303// “has no side effects”; also it doesn't work for cases like1304// `x && true`, though it probably should.1305var cond = self.condition.evaluate(compressor);1306self.condition = cond[0];1307if (cond.length > 1) {1308if (cond[1]) {1309compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);1310if (compressor.option("dead_code")) {1311var a = [];1312if (self.alternative) {1313extract_declarations_from_unreachable_code(compressor, self.alternative, a);1314}1315a.push(self.body);1316return make_node(AST_BlockStatement, self, { body: a }).transform(compressor);1317}1318} else {1319compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);1320if (compressor.option("dead_code")) {1321var a = [];1322extract_declarations_from_unreachable_code(compressor, self.body, a);1323if (self.alternative) a.push(self.alternative);1324return make_node(AST_BlockStatement, self, { body: a }).transform(compressor);1325}1326}1327}1328if (is_empty(self.alternative)) self.alternative = null;1329var negated = self.condition.negate(compressor);1330var negated_is_best = best_of(self.condition, negated) === negated;1331if (self.alternative && negated_is_best) {1332negated_is_best = false; // because we already do the switch here.1333self.condition = negated;1334var tmp = self.body;1335self.body = self.alternative || make_node(AST_EmptyStatement);1336self.alternative = tmp;1337}1338if (is_empty(self.body) && is_empty(self.alternative)) {1339return make_node(AST_SimpleStatement, self.condition, {1340body: self.condition1341}).transform(compressor);1342}1343if (self.body instanceof AST_SimpleStatement1344&& self.alternative instanceof AST_SimpleStatement) {1345return make_node(AST_SimpleStatement, self, {1346body: make_node(AST_Conditional, self, {1347condition : self.condition,1348consequent : self.body.body,1349alternative : self.alternative.body1350})1351}).transform(compressor);1352}1353if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {1354if (negated_is_best) return make_node(AST_SimpleStatement, self, {1355body: make_node(AST_Binary, self, {1356operator : "||",1357left : negated,1358right : self.body.body1359})1360}).transform(compressor);1361return make_node(AST_SimpleStatement, self, {1362body: make_node(AST_Binary, self, {1363operator : "&&",1364left : self.condition,1365right : self.body.body1366})1367}).transform(compressor);1368}1369if (self.body instanceof AST_EmptyStatement1370&& self.alternative1371&& self.alternative instanceof AST_SimpleStatement) {1372return make_node(AST_SimpleStatement, self, {1373body: make_node(AST_Binary, self, {1374operator : "||",1375left : self.condition,1376right : self.alternative.body1377})1378}).transform(compressor);1379}1380if (self.body instanceof AST_Exit1381&& self.alternative instanceof AST_Exit1382&& self.body.TYPE == self.alternative.TYPE) {1383return make_node(self.body.CTOR, self, {1384value: make_node(AST_Conditional, self, {1385condition : self.condition,1386consequent : self.body.value || make_node(AST_Undefined, self.body).optimize(compressor),1387alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).optimize(compressor)1388})1389}).transform(compressor);1390}1391if (self.body instanceof AST_If1392&& !self.body.alternative1393&& !self.alternative) {1394self.condition = make_node(AST_Binary, self.condition, {1395operator: "&&",1396left: self.condition,1397right: self.body.condition1398}).transform(compressor);1399self.body = self.body.body;1400}1401if (aborts(self.body)) {1402if (self.alternative) {1403var alt = self.alternative;1404self.alternative = null;1405return make_node(AST_BlockStatement, self, {1406body: [ self, alt ]1407}).transform(compressor);1408}1409}1410if (aborts(self.alternative)) {1411var body = self.body;1412self.body = self.alternative;1413self.condition = negated_is_best ? negated : self.condition.negate(compressor);1414self.alternative = null;1415return make_node(AST_BlockStatement, self, {1416body: [ self, body ]1417}).transform(compressor);1418}1419return self;1420});14211422OPT(AST_Switch, function(self, compressor){1423if (self.body.length == 0 && compressor.option("conditionals")) {1424return make_node(AST_SimpleStatement, self, {1425body: self.expression1426}).transform(compressor);1427}1428for(;;) {1429var last_branch = self.body[self.body.length - 1];1430if (last_branch) {1431var stat = last_branch.body[last_branch.body.length - 1]; // last statement1432if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)1433last_branch.body.pop();1434if (last_branch instanceof AST_Default && last_branch.body.length == 0) {1435self.body.pop();1436continue;1437}1438}1439break;1440}1441var exp = self.expression.evaluate(compressor);1442out: if (exp.length == 2) try {1443// constant expression1444self.expression = exp[0];1445if (!compressor.option("dead_code")) break out;1446var value = exp[1];1447var in_if = false;1448var in_block = false;1449var started = false;1450var stopped = false;1451var ruined = false;1452var tt = new TreeTransformer(function(node, descend, in_list){1453if (node instanceof AST_Lambda || node instanceof AST_SimpleStatement) {1454// no need to descend these node types1455return node;1456}1457else if (node instanceof AST_Switch && node === self) {1458node = node.clone();1459descend(node, this);1460return ruined ? node : make_node(AST_BlockStatement, node, {1461body: node.body.reduce(function(a, branch){1462return a.concat(branch.body);1463}, [])1464}).transform(compressor);1465}1466else if (node instanceof AST_If || node instanceof AST_Try) {1467var save = in_if;1468in_if = !in_block;1469descend(node, this);1470in_if = save;1471return node;1472}1473else if (node instanceof AST_StatementWithBody || node instanceof AST_Switch) {1474var save = in_block;1475in_block = true;1476descend(node, this);1477in_block = save;1478return node;1479}1480else if (node instanceof AST_Break && this.loopcontrol_target(node.label) === self) {1481if (in_if) {1482ruined = true;1483return node;1484}1485if (in_block) return node;1486stopped = true;1487return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);1488}1489else if (node instanceof AST_SwitchBranch && this.parent() === self) {1490if (stopped) return MAP.skip;1491if (node instanceof AST_Case) {1492var exp = node.expression.evaluate(compressor);1493if (exp.length < 2) {1494// got a case with non-constant expression, baling out1495throw self;1496}1497if (exp[1] === value || started) {1498started = true;1499if (aborts(node)) stopped = true;1500descend(node, this);1501return node;1502}1503return MAP.skip;1504}1505descend(node, this);1506return node;1507}1508});1509tt.stack = compressor.stack.slice(); // so that's able to see parent nodes1510self = self.transform(tt);1511} catch(ex) {1512if (ex !== self) throw ex;1513}1514return self;1515});15161517OPT(AST_Case, function(self, compressor){1518self.body = tighten_body(self.body, compressor);1519return self;1520});15211522OPT(AST_Try, function(self, compressor){1523self.body = tighten_body(self.body, compressor);1524return self;1525});15261527AST_Definitions.DEFMETHOD("remove_initializers", function(){1528this.definitions.forEach(function(def){ def.value = null });1529});15301531AST_Definitions.DEFMETHOD("to_assignments", function(){1532var assignments = this.definitions.reduce(function(a, def){1533if (def.value) {1534var name = make_node(AST_SymbolRef, def.name, def.name);1535a.push(make_node(AST_Assign, def, {1536operator : "=",1537left : name,1538right : def.value1539}));1540}1541return a;1542}, []);1543if (assignments.length == 0) return null;1544return AST_Seq.from_array(assignments);1545});15461547OPT(AST_Definitions, function(self, compressor){1548if (self.definitions.length == 0)1549return make_node(AST_EmptyStatement, self);1550return self;1551});15521553OPT(AST_Function, function(self, compressor){1554self = AST_Lambda.prototype.optimize.call(self, compressor);1555if (compressor.option("unused")) {1556if (self.name && self.name.unreferenced()) {1557self.name = null;1558}1559}1560return self;1561});15621563OPT(AST_Call, function(self, compressor){1564if (compressor.option("unsafe")) {1565var exp = self.expression;1566if (exp instanceof AST_SymbolRef && exp.undeclared()) {1567switch (exp.name) {1568case "Array":1569if (self.args.length != 1) {1570return make_node(AST_Array, self, {1571elements: self.args1572});1573}1574break;1575case "Object":1576if (self.args.length == 0) {1577return make_node(AST_Object, self, {1578properties: []1579});1580}1581break;1582case "String":1583if (self.args.length == 0) return make_node(AST_String, self, {1584value: ""1585});1586return make_node(AST_Binary, self, {1587left: self.args[0],1588operator: "+",1589right: make_node(AST_String, self, { value: "" })1590});1591case "Function":1592if (all(self.args, function(x){ return x instanceof AST_String })) {1593// quite a corner-case, but we can handle it:1594// https://github.com/mishoo/UglifyJS2/issues/2031595// if the code argument is a constant, then we can minify it.1596try {1597var code = "(function(" + self.args.slice(0, -1).map(function(arg){1598return arg.value;1599}).join(",") + "){" + self.args[self.args.length - 1].value + "})()";1600var ast = parse(code);1601ast.figure_out_scope();1602var comp = new Compressor(compressor.options);1603ast = ast.transform(comp);1604ast.figure_out_scope();1605ast.mangle_names();1606var fun = ast.body[0].body.expression;1607var args = fun.argnames.map(function(arg, i){1608return make_node(AST_String, self.args[i], {1609value: arg.print_to_string()1610});1611});1612var code = OutputStream();1613AST_BlockStatement.prototype._codegen.call(fun, fun, code);1614code = code.toString().replace(/^\{|\}$/g, "");1615args.push(make_node(AST_String, self.args[self.args.length - 1], {1616value: code1617}));1618self.args = args;1619return self;1620} catch(ex) {1621if (ex instanceof JS_Parse_Error) {1622compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);1623compressor.warn(ex.toString());1624} else {1625console.log(ex);1626}1627}1628}1629break;1630}1631}1632else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {1633return make_node(AST_Binary, self, {1634left: make_node(AST_String, self, { value: "" }),1635operator: "+",1636right: exp.expression1637}).transform(compressor);1638}1639}1640if (compressor.option("side_effects")) {1641if (self.expression instanceof AST_Function1642&& self.args.length == 01643&& !AST_Block.prototype.has_side_effects.call(self.expression)) {1644return make_node(AST_Undefined, self).transform(compressor);1645}1646}1647return self;1648});16491650OPT(AST_New, function(self, compressor){1651if (compressor.option("unsafe")) {1652var exp = self.expression;1653if (exp instanceof AST_SymbolRef && exp.undeclared()) {1654switch (exp.name) {1655case "Object":1656case "RegExp":1657case "Function":1658case "Error":1659case "Array":1660return make_node(AST_Call, self, self).transform(compressor);1661}1662}1663}1664return self;1665});16661667OPT(AST_Seq, function(self, compressor){1668if (!compressor.option("side_effects"))1669return self;1670if (!self.car.has_side_effects()) {1671// we shouldn't compress (1,eval)(something) to1672// eval(something) because that changes the meaning of1673// eval (becomes lexical instead of global).1674var p;1675if (!(self.cdr instanceof AST_SymbolRef1676&& self.cdr.name == "eval"1677&& self.cdr.undeclared()1678&& (p = compressor.parent()) instanceof AST_Call1679&& p.expression === self)) {1680return self.cdr;1681}1682}1683if (compressor.option("cascade")) {1684if (self.car instanceof AST_Assign1685&& !self.car.left.has_side_effects()1686&& self.car.left.equivalent_to(self.cdr)) {1687return self.car;1688}1689if (!self.car.has_side_effects()1690&& !self.cdr.has_side_effects()1691&& self.car.equivalent_to(self.cdr)) {1692return self.car;1693}1694}1695return self;1696});16971698AST_Unary.DEFMETHOD("lift_sequences", function(compressor){1699if (compressor.option("sequences")) {1700if (this.expression instanceof AST_Seq) {1701var seq = this.expression;1702var x = seq.to_array();1703this.expression = x.pop();1704x.push(this);1705seq = AST_Seq.from_array(x).transform(compressor);1706return seq;1707}1708}1709return this;1710});17111712OPT(AST_UnaryPostfix, function(self, compressor){1713return self.lift_sequences(compressor);1714});17151716OPT(AST_UnaryPrefix, function(self, compressor){1717self = self.lift_sequences(compressor);1718var e = self.expression;1719if (compressor.option("booleans") && compressor.in_boolean_context()) {1720switch (self.operator) {1721case "!":1722if (e instanceof AST_UnaryPrefix && e.operator == "!") {1723// !!foo ==> foo, if we're in boolean context1724return e.expression;1725}1726break;1727case "typeof":1728// typeof always returns a non-empty string, thus it's1729// always true in booleans1730compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);1731return make_node(AST_True, self);1732}1733if (e instanceof AST_Binary && self.operator == "!") {1734self = best_of(self, e.negate(compressor));1735}1736}1737return self.evaluate(compressor)[0];1738});17391740AST_Binary.DEFMETHOD("lift_sequences", function(compressor){1741if (compressor.option("sequences")) {1742if (this.left instanceof AST_Seq) {1743var seq = this.left;1744var x = seq.to_array();1745this.left = x.pop();1746x.push(this);1747seq = AST_Seq.from_array(x).transform(compressor);1748return seq;1749}1750if (this.right instanceof AST_Seq1751&& !(this.operator == "||" || this.operator == "&&")1752&& !this.left.has_side_effects()) {1753var seq = this.right;1754var x = seq.to_array();1755this.right = x.pop();1756x.push(this);1757seq = AST_Seq.from_array(x).transform(compressor);1758return seq;1759}1760}1761return this;1762});17631764var commutativeOperators = makePredicate("== === != !== * & | ^");17651766OPT(AST_Binary, function(self, compressor){1767function reverse(op, force) {1768if (force || !(self.left.has_side_effects() || self.right.has_side_effects())) {1769if (op) self.operator = op;1770var tmp = self.left;1771self.left = self.right;1772self.right = tmp;1773}1774};1775if (commutativeOperators(self.operator)) {1776if (self.right instanceof AST_Constant1777&& !(self.left instanceof AST_Constant)) {1778// if right is a constant, whatever side effects the1779// left side might have could not influence the1780// result. hence, force switch.1781reverse(null, true);1782}1783}1784self = self.lift_sequences(compressor);1785if (compressor.option("comparisons")) switch (self.operator) {1786case "===":1787case "!==":1788if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||1789(self.left.is_boolean() && self.right.is_boolean())) {1790self.operator = self.operator.substr(0, 2);1791}1792// XXX: intentionally falling down to the next case1793case "==":1794case "!=":1795if (self.left instanceof AST_String1796&& self.left.value == "undefined"1797&& self.right instanceof AST_UnaryPrefix1798&& self.right.operator == "typeof"1799&& compressor.option("unsafe")) {1800if (!(self.right.expression instanceof AST_SymbolRef)1801|| !self.right.expression.undeclared()) {1802self.right = self.right.expression;1803self.left = make_node(AST_Undefined, self.left).optimize(compressor);1804if (self.operator.length == 2) self.operator += "=";1805}1806}1807break;1808}1809if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) {1810case "&&":1811var ll = self.left.evaluate(compressor);1812var rr = self.right.evaluate(compressor);1813if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) {1814compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);1815return make_node(AST_False, self);1816}1817if (ll.length > 1 && ll[1]) {1818return rr[0];1819}1820if (rr.length > 1 && rr[1]) {1821return ll[0];1822}1823break;1824case "||":1825var ll = self.left.evaluate(compressor);1826var rr = self.right.evaluate(compressor);1827if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) {1828compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);1829return make_node(AST_True, self);1830}1831if (ll.length > 1 && !ll[1]) {1832return rr[0];1833}1834if (rr.length > 1 && !rr[1]) {1835return ll[0];1836}1837break;1838case "+":1839var ll = self.left.evaluate(compressor);1840var rr = self.right.evaluate(compressor);1841if ((ll.length > 1 && ll[0] instanceof AST_String && ll[1]) ||1842(rr.length > 1 && rr[0] instanceof AST_String && rr[1])) {1843compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);1844return make_node(AST_True, self);1845}1846break;1847}1848var exp = self.evaluate(compressor);1849if (exp.length > 1) {1850if (best_of(exp[0], self) !== self)1851return exp[0];1852}1853if (compressor.option("comparisons")) {1854if (!(compressor.parent() instanceof AST_Binary)1855|| compressor.parent() instanceof AST_Assign) {1856var negated = make_node(AST_UnaryPrefix, self, {1857operator: "!",1858expression: self.negate(compressor)1859});1860self = best_of(self, negated);1861}1862switch (self.operator) {1863case "<": reverse(">"); break;1864case "<=": reverse(">="); break;1865}1866}1867if (self.operator == "+" && self.right instanceof AST_String1868&& self.right.getValue() === "" && self.left instanceof AST_Binary1869&& self.left.operator == "+" && self.left.is_string(compressor)) {1870return self.left;1871}1872return self;1873});18741875OPT(AST_SymbolRef, function(self, compressor){1876if (self.undeclared()) {1877var defines = compressor.option("global_defs");1878if (defines && defines.hasOwnProperty(self.name)) {1879return make_node_from_constant(compressor, defines[self.name], self);1880}1881switch (self.name) {1882case "undefined":1883return make_node(AST_Undefined, self);1884case "NaN":1885return make_node(AST_NaN, self);1886case "Infinity":1887return make_node(AST_Infinity, self);1888}1889}1890return self;1891});18921893OPT(AST_Undefined, function(self, compressor){1894if (compressor.option("unsafe")) {1895var scope = compressor.find_parent(AST_Scope);1896var undef = scope.find_variable("undefined");1897if (undef) {1898var ref = make_node(AST_SymbolRef, self, {1899name : "undefined",1900scope : scope,1901thedef : undef1902});1903ref.reference();1904return ref;1905}1906}1907return self;1908});19091910var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];1911OPT(AST_Assign, function(self, compressor){1912self = self.lift_sequences(compressor);1913if (self.operator == "="1914&& self.left instanceof AST_SymbolRef1915&& self.right instanceof AST_Binary1916&& self.right.left instanceof AST_SymbolRef1917&& self.right.left.name == self.left.name1918&& member(self.right.operator, ASSIGN_OPS)) {1919self.operator = self.right.operator + "=";1920self.right = self.right.right;1921}1922return self;1923});19241925OPT(AST_Conditional, function(self, compressor){1926if (!compressor.option("conditionals")) return self;1927if (self.condition instanceof AST_Seq) {1928var car = self.condition.car;1929self.condition = self.condition.cdr;1930return AST_Seq.cons(car, self);1931}1932var cond = self.condition.evaluate(compressor);1933if (cond.length > 1) {1934if (cond[1]) {1935compressor.warn("Condition always true [{file}:{line},{col}]", self.start);1936return self.consequent;1937} else {1938compressor.warn("Condition always false [{file}:{line},{col}]", self.start);1939return self.alternative;1940}1941}1942var negated = cond[0].negate(compressor);1943if (best_of(cond[0], negated) === negated) {1944self = make_node(AST_Conditional, self, {1945condition: negated,1946consequent: self.alternative,1947alternative: self.consequent1948});1949}1950var consequent = self.consequent;1951var alternative = self.alternative;1952if (consequent instanceof AST_Assign1953&& alternative instanceof AST_Assign1954&& consequent.operator == alternative.operator1955&& consequent.left.equivalent_to(alternative.left)1956) {1957/*1958* Stuff like this:1959* if (foo) exp = something; else exp = something_else;1960* ==>1961* exp = foo ? something : something_else;1962*/1963self = make_node(AST_Assign, self, {1964operator: consequent.operator,1965left: consequent.left,1966right: make_node(AST_Conditional, self, {1967condition: self.condition,1968consequent: consequent.right,1969alternative: alternative.right1970})1971});1972}1973return self;1974});19751976OPT(AST_Boolean, function(self, compressor){1977if (compressor.option("booleans")) {1978var p = compressor.parent();1979if (p instanceof AST_Binary && (p.operator == "=="1980|| p.operator == "!=")) {1981compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {1982operator : p.operator,1983value : self.value,1984file : p.start.file,1985line : p.start.line,1986col : p.start.col,1987});1988return make_node(AST_Number, self, {1989value: +self.value1990});1991}1992return make_node(AST_UnaryPrefix, self, {1993operator: "!",1994expression: make_node(AST_Number, self, {1995value: 1 - self.value1996})1997});1998}1999return self;2000});20012002OPT(AST_Sub, function(self, compressor){2003var prop = self.property;2004if (prop instanceof AST_String && compressor.option("properties")) {2005prop = prop.getValue();2006if ((compressor.option("screw_ie8") && RESERVED_WORDS(prop))2007|| (!(RESERVED_WORDS(prop)) && is_identifier_string(prop))) {2008return make_node(AST_Dot, self, {2009expression : self.expression,2010property : prop2011});2012}2013}2014return self;2015});20162017function literals_in_boolean_context(self, compressor) {2018if (compressor.option("booleans") && compressor.in_boolean_context()) {2019return make_node(AST_True, self);2020}2021return self;2022};2023OPT(AST_Array, literals_in_boolean_context);2024OPT(AST_Object, literals_in_boolean_context);2025OPT(AST_RegExp, literals_in_boolean_context);20262027})();202820292030