| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529 |
- /***********************************************************************
- A JavaScript tokenizer / parser / beautifier / compressor.
- https://github.com/mishoo/UglifyJS2
- -------------------------------- (C) ---------------------------------
- Author: Mihai Bazon
- <mihai.bazon@gmail.com>
- http://mihai.bazon.net/blog
- Distributed under the BSD license:
- Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- * Redistributions of source code must retain the above
- copyright notice, this list of conditions and the following
- disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials
- provided with the distribution.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
- ***********************************************************************/
- "use strict";
- function Compressor(options, false_by_default) {
- if (!(this instanceof Compressor))
- return new Compressor(options, false_by_default);
- TreeTransformer.call(this, this.before, this.after);
- this.options = defaults(options, {
- sequences : !false_by_default,
- properties : !false_by_default,
- dead_code : !false_by_default,
- drop_debugger : !false_by_default,
- unsafe : false,
- unsafe_comps : false,
- conditionals : !false_by_default,
- comparisons : !false_by_default,
- evaluate : !false_by_default,
- booleans : !false_by_default,
- loops : !false_by_default,
- unused : !false_by_default,
- hoist_funs : !false_by_default,
- keep_fargs : true,
- keep_fnames : false,
- hoist_vars : false,
- if_return : !false_by_default,
- join_vars : !false_by_default,
- cascade : !false_by_default,
- side_effects : !false_by_default,
- pure_getters : false,
- pure_funcs : null,
- negate_iife : !false_by_default,
- screw_ie8 : false,
- drop_console : false,
- angular : false,
- warnings : true,
- global_defs : {}
- }, true);
- };
- Compressor.prototype = new TreeTransformer;
- merge(Compressor.prototype, {
- option: function(key) { return this.options[key] },
- warn: function() {
- if (this.options.warnings)
- AST_Node.warn.apply(AST_Node, arguments);
- },
- before: function(node, descend, in_list) {
- if (node._squeezed) return node;
- var was_scope = false;
- if (node instanceof AST_Scope) {
- node = node.hoist_declarations(this);
- was_scope = true;
- }
- descend(node, this);
- node = node.optimize(this);
- if (was_scope && node instanceof AST_Scope) {
- node.drop_unused(this);
- descend(node, this);
- }
- node._squeezed = true;
- return node;
- }
- });
- (function(){
- function OPT(node, optimizer) {
- node.DEFMETHOD("optimize", function(compressor){
- var self = this;
- if (self._optimized) return self;
- if (compressor.has_directive("use asm")) return self;
- var opt = optimizer(self, compressor);
- opt._optimized = true;
- if (opt === self) return opt;
- return opt.transform(compressor);
- });
- };
- OPT(AST_Node, function(self, compressor){
- return self;
- });
- AST_Node.DEFMETHOD("equivalent_to", function(node){
- // XXX: this is a rather expensive way to test two node's equivalence:
- return this.print_to_string() == node.print_to_string();
- });
- function make_node(ctor, orig, props) {
- if (!props) props = {};
- if (orig) {
- if (!props.start) props.start = orig.start;
- if (!props.end) props.end = orig.end;
- }
- return new ctor(props);
- };
- function make_node_from_constant(compressor, val, orig) {
- // XXX: WIP.
- // if (val instanceof AST_Node) return val.transform(new TreeTransformer(null, function(node){
- // if (node instanceof AST_SymbolRef) {
- // var scope = compressor.find_parent(AST_Scope);
- // var def = scope.find_variable(node);
- // node.thedef = def;
- // return node;
- // }
- // })).transform(compressor);
- if (val instanceof AST_Node) return val.transform(compressor);
- switch (typeof val) {
- case "string":
- return make_node(AST_String, orig, {
- value: val
- }).optimize(compressor);
- case "number":
- return make_node(isNaN(val) ? AST_NaN : AST_Number, orig, {
- value: val
- }).optimize(compressor);
- case "boolean":
- return make_node(val ? AST_True : AST_False, orig).optimize(compressor);
- case "undefined":
- return make_node(AST_Undefined, orig).optimize(compressor);
- default:
- if (val === null) {
- return make_node(AST_Null, orig, { value: null }).optimize(compressor);
- }
- if (val instanceof RegExp) {
- return make_node(AST_RegExp, orig, { value: val }).optimize(compressor);
- }
- throw new Error(string_template("Can't handle constant of type: {type}", {
- type: typeof val
- }));
- }
- };
- function as_statement_array(thing) {
- if (thing === null) return [];
- if (thing instanceof AST_BlockStatement) return thing.body;
- if (thing instanceof AST_EmptyStatement) return [];
- if (thing instanceof AST_Statement) return [ thing ];
- throw new Error("Can't convert thing to statement array");
- };
- function is_empty(thing) {
- if (thing === null) return true;
- if (thing instanceof AST_EmptyStatement) return true;
- if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
- return false;
- };
- function loop_body(x) {
- if (x instanceof AST_Switch) return x;
- if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
- return (x.body instanceof AST_BlockStatement ? x.body : x);
- }
- return x;
- };
- function tighten_body(statements, compressor) {
- var CHANGED;
- do {
- CHANGED = false;
- if (compressor.option("angular")) {
- statements = process_for_angular(statements);
- }
- statements = eliminate_spurious_blocks(statements);
- if (compressor.option("dead_code")) {
- statements = eliminate_dead_code(statements, compressor);
- }
- if (compressor.option("if_return")) {
- statements = handle_if_return(statements, compressor);
- }
- if (compressor.option("sequences")) {
- statements = sequencesize(statements, compressor);
- }
- if (compressor.option("join_vars")) {
- statements = join_consecutive_vars(statements, compressor);
- }
- } while (CHANGED);
- if (compressor.option("negate_iife")) {
- negate_iifes(statements, compressor);
- }
- return statements;
- function process_for_angular(statements) {
- function has_inject(comment) {
- return /@ngInject/.test(comment.value);
- }
- function make_arguments_names_list(func) {
- return func.argnames.map(function(sym){
- return make_node(AST_String, sym, { value: sym.name });
- });
- }
- function make_array(orig, elements) {
- return make_node(AST_Array, orig, { elements: elements });
- }
- function make_injector(func, name) {
- return make_node(AST_SimpleStatement, func, {
- body: make_node(AST_Assign, func, {
- operator: "=",
- left: make_node(AST_Dot, name, {
- expression: make_node(AST_SymbolRef, name, name),
- property: "$inject"
- }),
- right: make_array(func, make_arguments_names_list(func))
- })
- });
- }
- function check_expression(body) {
- if (body && body.args) {
- // if this is a function call check all of arguments passed
- body.args.forEach(function(argument, index, array) {
- var comments = argument.start.comments_before;
- // if the argument is function preceded by @ngInject
- if (argument instanceof AST_Lambda && comments.length && has_inject(comments[0])) {
- // replace the function with an array of names of its parameters and function at the end
- array[index] = make_array(argument, make_arguments_names_list(argument).concat(argument));
- }
- });
- // if this is chained call check previous one recursively
- if (body.expression && body.expression.expression) {
- check_expression(body.expression.expression);
- }
- }
- }
- return statements.reduce(function(a, stat){
- a.push(stat);
- if (stat.body && stat.body.args) {
- check_expression(stat.body);
- } else {
- var token = stat.start;
- var comments = token.comments_before;
- if (comments && comments.length > 0) {
- var last = comments.pop();
- if (has_inject(last)) {
- // case 1: defun
- if (stat instanceof AST_Defun) {
- a.push(make_injector(stat, stat.name));
- }
- else if (stat instanceof AST_Definitions) {
- stat.definitions.forEach(function(def) {
- if (def.value && def.value instanceof AST_Lambda) {
- a.push(make_injector(def.value, def.name));
- }
- });
- }
- else {
- compressor.warn("Unknown statement marked with @ngInject [{file}:{line},{col}]", token);
- }
- }
- }
- }
- return a;
- }, []);
- }
- function eliminate_spurious_blocks(statements) {
- var seen_dirs = [];
- return statements.reduce(function(a, stat){
- if (stat instanceof AST_BlockStatement) {
- CHANGED = true;
- a.push.apply(a, eliminate_spurious_blocks(stat.body));
- } else if (stat instanceof AST_EmptyStatement) {
- CHANGED = true;
- } else if (stat instanceof AST_Directive) {
- if (seen_dirs.indexOf(stat.value) < 0) {
- a.push(stat);
- seen_dirs.push(stat.value);
- } else {
- CHANGED = true;
- }
- } else {
- a.push(stat);
- }
- return a;
- }, []);
- };
- function handle_if_return(statements, compressor) {
- var self = compressor.self();
- var in_lambda = self instanceof AST_Lambda;
- var ret = [];
- loop: for (var i = statements.length; --i >= 0;) {
- var stat = statements[i];
- switch (true) {
- case (in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0):
- CHANGED = true;
- // note, ret.length is probably always zero
- // because we drop unreachable code before this
- // step. nevertheless, it's good to check.
- continue loop;
- case stat instanceof AST_If:
- if (stat.body instanceof AST_Return) {
- //---
- // pretty silly case, but:
- // if (foo()) return; return; ==> foo(); return;
- if (((in_lambda && ret.length == 0)
- || (ret[0] instanceof AST_Return && !ret[0].value))
- && !stat.body.value && !stat.alternative) {
- CHANGED = true;
- var cond = make_node(AST_SimpleStatement, stat.condition, {
- body: stat.condition
- });
- ret.unshift(cond);
- continue loop;
- }
- //---
- // if (foo()) return x; return y; ==> return foo() ? x : y;
- if (ret[0] instanceof AST_Return && stat.body.value && ret[0].value && !stat.alternative) {
- CHANGED = true;
- stat = stat.clone();
- stat.alternative = ret[0];
- ret[0] = stat.transform(compressor);
- continue loop;
- }
- //---
- // if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
- if ((ret.length == 0 || ret[0] instanceof AST_Return) && stat.body.value && !stat.alternative && in_lambda) {
- CHANGED = true;
- stat = stat.clone();
- stat.alternative = ret[0] || make_node(AST_Return, stat, {
- value: make_node(AST_Undefined, stat)
- });
- ret[0] = stat.transform(compressor);
- continue loop;
- }
- //---
- // if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
- if (!stat.body.value && in_lambda) {
- CHANGED = true;
- stat = stat.clone();
- stat.condition = stat.condition.negate(compressor);
- stat.body = make_node(AST_BlockStatement, stat, {
- body: as_statement_array(stat.alternative).concat(ret)
- });
- stat.alternative = null;
- ret = [ stat.transform(compressor) ];
- continue loop;
- }
- //---
- if (ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
- && (!stat.alternative || stat.alternative instanceof AST_SimpleStatement)) {
- CHANGED = true;
- ret.push(make_node(AST_Return, ret[0], {
- value: make_node(AST_Undefined, ret[0])
- }).transform(compressor));
- ret = as_statement_array(stat.alternative).concat(ret);
- ret.unshift(stat);
- continue loop;
- }
- }
- var ab = aborts(stat.body);
- var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
- if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
- || (ab instanceof AST_Continue && self === loop_body(lct))
- || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
- if (ab.label) {
- remove(ab.label.thedef.references, ab);
- }
- CHANGED = true;
- var body = as_statement_array(stat.body).slice(0, -1);
- stat = stat.clone();
- stat.condition = stat.condition.negate(compressor);
- stat.body = make_node(AST_BlockStatement, stat, {
- body: as_statement_array(stat.alternative).concat(ret)
- });
- stat.alternative = make_node(AST_BlockStatement, stat, {
- body: body
- });
- ret = [ stat.transform(compressor) ];
- continue loop;
- }
- var ab = aborts(stat.alternative);
- var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
- if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
- || (ab instanceof AST_Continue && self === loop_body(lct))
- || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
- if (ab.label) {
- remove(ab.label.thedef.references, ab);
- }
- CHANGED = true;
- stat = stat.clone();
- stat.body = make_node(AST_BlockStatement, stat.body, {
- body: as_statement_array(stat.body).concat(ret)
- });
- stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
- body: as_statement_array(stat.alternative).slice(0, -1)
- });
- ret = [ stat.transform(compressor) ];
- continue loop;
- }
- ret.unshift(stat);
- break;
- default:
- ret.unshift(stat);
- break;
- }
- }
- return ret;
- };
- function eliminate_dead_code(statements, compressor) {
- var has_quit = false;
- var orig = statements.length;
- var self = compressor.self();
- statements = statements.reduce(function(a, stat){
- if (has_quit) {
- extract_declarations_from_unreachable_code(compressor, stat, a);
- } else {
- if (stat instanceof AST_LoopControl) {
- var lct = compressor.loopcontrol_target(stat.label);
- if ((stat instanceof AST_Break
- && lct instanceof AST_BlockStatement
- && loop_body(lct) === self) || (stat instanceof AST_Continue
- && loop_body(lct) === self)) {
- if (stat.label) {
- remove(stat.label.thedef.references, stat);
- }
- } else {
- a.push(stat);
- }
- } else {
- a.push(stat);
- }
- if (aborts(stat)) has_quit = true;
- }
- return a;
- }, []);
- CHANGED = statements.length != orig;
- return statements;
- };
- function sequencesize(statements, compressor) {
- if (statements.length < 2) return statements;
- var seq = [], ret = [];
- function push_seq() {
- seq = AST_Seq.from_array(seq);
- if (seq) ret.push(make_node(AST_SimpleStatement, seq, {
- body: seq
- }));
- seq = [];
- };
- statements.forEach(function(stat){
- if (stat instanceof AST_SimpleStatement && seq.length < 2000) seq.push(stat.body);
- else push_seq(), ret.push(stat);
- });
- push_seq();
- ret = sequencesize_2(ret, compressor);
- CHANGED = ret.length != statements.length;
- return ret;
- };
- function sequencesize_2(statements, compressor) {
- function cons_seq(right) {
- ret.pop();
- var left = prev.body;
- if (left instanceof AST_Seq) {
- left.add(right);
- } else {
- left = AST_Seq.cons(left, right);
- }
- return left.transform(compressor);
- };
- var ret = [], prev = null;
- statements.forEach(function(stat){
- if (prev) {
- if (stat instanceof AST_For) {
- var opera = {};
- try {
- prev.body.walk(new TreeWalker(function(node){
- if (node instanceof AST_Binary && node.operator == "in")
- throw opera;
- }));
- if (stat.init && !(stat.init instanceof AST_Definitions)) {
- stat.init = cons_seq(stat.init);
- }
- else if (!stat.init) {
- stat.init = prev.body;
- ret.pop();
- }
- } catch(ex) {
- if (ex !== opera) throw ex;
- }
- }
- else if (stat instanceof AST_If) {
- stat.condition = cons_seq(stat.condition);
- }
- else if (stat instanceof AST_With) {
- stat.expression = cons_seq(stat.expression);
- }
- else if (stat instanceof AST_Exit && stat.value) {
- stat.value = cons_seq(stat.value);
- }
- else if (stat instanceof AST_Exit) {
- stat.value = cons_seq(make_node(AST_Undefined, stat));
- }
- else if (stat instanceof AST_Switch) {
- stat.expression = cons_seq(stat.expression);
- }
- }
- ret.push(stat);
- prev = stat instanceof AST_SimpleStatement ? stat : null;
- });
- return ret;
- };
- function join_consecutive_vars(statements, compressor) {
- var prev = null;
- return statements.reduce(function(a, stat){
- if (stat instanceof AST_Definitions && prev && prev.TYPE == stat.TYPE) {
- prev.definitions = prev.definitions.concat(stat.definitions);
- CHANGED = true;
- }
- else if (stat instanceof AST_For
- && prev instanceof AST_Definitions
- && (!stat.init || stat.init.TYPE == prev.TYPE)) {
- CHANGED = true;
- a.pop();
- if (stat.init) {
- stat.init.definitions = prev.definitions.concat(stat.init.definitions);
- } else {
- stat.init = prev;
- }
- a.push(stat);
- prev = stat;
- }
- else {
- prev = stat;
- a.push(stat);
- }
- return a;
- }, []);
- };
- function negate_iifes(statements, compressor) {
- statements.forEach(function(stat){
- if (stat instanceof AST_SimpleStatement) {
- stat.body = (function transform(thing) {
- return thing.transform(new TreeTransformer(function(node){
- if (node instanceof AST_Call && node.expression instanceof AST_Function) {
- return make_node(AST_UnaryPrefix, node, {
- operator: "!",
- expression: node
- });
- }
- else if (node instanceof AST_Call) {
- node.expression = transform(node.expression);
- }
- else if (node instanceof AST_Seq) {
- node.car = transform(node.car);
- }
- else if (node instanceof AST_Conditional) {
- var expr = transform(node.condition);
- if (expr !== node.condition) {
- // it has been negated, reverse
- node.condition = expr;
- var tmp = node.consequent;
- node.consequent = node.alternative;
- node.alternative = tmp;
- }
- }
- return node;
- }));
- })(stat.body);
- }
- });
- };
- };
- function extract_declarations_from_unreachable_code(compressor, stat, target) {
- compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
- stat.walk(new TreeWalker(function(node){
- if (node instanceof AST_Definitions) {
- compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
- node.remove_initializers();
- target.push(node);
- return true;
- }
- if (node instanceof AST_Defun) {
- target.push(node);
- return true;
- }
- if (node instanceof AST_Scope) {
- return true;
- }
- }));
- };
- /* -----[ boolean/negation helpers ]----- */
- // methods to determine whether an expression has a boolean result type
- (function (def){
- var unary_bool = [ "!", "delete" ];
- var binary_bool = [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ];
- def(AST_Node, function(){ return false });
- def(AST_UnaryPrefix, function(){
- return member(this.operator, unary_bool);
- });
- def(AST_Binary, function(){
- return member(this.operator, binary_bool) ||
- ( (this.operator == "&&" || this.operator == "||") &&
- this.left.is_boolean() && this.right.is_boolean() );
- });
- def(AST_Conditional, function(){
- return this.consequent.is_boolean() && this.alternative.is_boolean();
- });
- def(AST_Assign, function(){
- return this.operator == "=" && this.right.is_boolean();
- });
- def(AST_Seq, function(){
- return this.cdr.is_boolean();
- });
- def(AST_True, function(){ return true });
- def(AST_False, function(){ return true });
- })(function(node, func){
- node.DEFMETHOD("is_boolean", func);
- });
- // methods to determine if an expression has a string result type
- (function (def){
- def(AST_Node, function(){ return false });
- def(AST_String, function(){ return true });
- def(AST_UnaryPrefix, function(){
- return this.operator == "typeof";
- });
- def(AST_Binary, function(compressor){
- return this.operator == "+" &&
- (this.left.is_string(compressor) || this.right.is_string(compressor));
- });
- def(AST_Assign, function(compressor){
- return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
- });
- def(AST_Seq, function(compressor){
- return this.cdr.is_string(compressor);
- });
- def(AST_Conditional, function(compressor){
- return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
- });
- def(AST_Call, function(compressor){
- return compressor.option("unsafe")
- && this.expression instanceof AST_SymbolRef
- && this.expression.name == "String"
- && this.expression.undeclared();
- });
- })(function(node, func){
- node.DEFMETHOD("is_string", func);
- });
- function best_of(ast1, ast2) {
- return ast1.print_to_string().length >
- ast2.print_to_string().length
- ? ast2 : ast1;
- };
- // methods to evaluate a constant expression
- (function (def){
- // The evaluate method returns an array with one or two
- // elements. If the node has been successfully reduced to a
- // constant, then the second element tells us the value;
- // otherwise the second element is missing. The first element
- // of the array is always an AST_Node descendant; if
- // evaluation was successful it's a node that represents the
- // constant; otherwise it's the original or a replacement node.
- AST_Node.DEFMETHOD("evaluate", function(compressor){
- if (!compressor.option("evaluate")) return [ this ];
- try {
- var val = this._eval(compressor);
- return [ best_of(make_node_from_constant(compressor, val, this), this), val ];
- } catch(ex) {
- if (ex !== def) throw ex;
- return [ this ];
- }
- });
- def(AST_Statement, function(){
- throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
- });
- def(AST_Function, function(){
- // XXX: AST_Function inherits from AST_Scope, which itself
- // inherits from AST_Statement; however, an AST_Function
- // isn't really a statement. This could byte in other
- // places too. :-( Wish JS had multiple inheritance.
- throw def;
- });
- function ev(node, compressor) {
- if (!compressor) throw new Error("Compressor must be passed");
- return node._eval(compressor);
- };
- def(AST_Node, function(){
- throw def; // not constant
- });
- def(AST_Constant, function(){
- return this.getValue();
- });
- def(AST_UnaryPrefix, function(compressor){
- var e = this.expression;
- switch (this.operator) {
- case "!": return !ev(e, compressor);
- case "typeof":
- // Function would be evaluated to an array and so typeof would
- // incorrectly return 'object'. Hence making is a special case.
- if (e instanceof AST_Function) return typeof function(){};
- e = ev(e, compressor);
- // typeof <RegExp> returns "object" or "function" on different platforms
- // so cannot evaluate reliably
- if (e instanceof RegExp) throw def;
- return typeof e;
- case "void": return void ev(e, compressor);
- case "~": return ~ev(e, compressor);
- case "-":
- e = ev(e, compressor);
- if (e === 0) throw def;
- return -e;
- case "+": return +ev(e, compressor);
- }
- throw def;
- });
- def(AST_Binary, function(c){
- var left = this.left, right = this.right;
- switch (this.operator) {
- case "&&" : return ev(left, c) && ev(right, c);
- case "||" : return ev(left, c) || ev(right, c);
- case "|" : return ev(left, c) | ev(right, c);
- case "&" : return ev(left, c) & ev(right, c);
- case "^" : return ev(left, c) ^ ev(right, c);
- case "+" : return ev(left, c) + ev(right, c);
- case "*" : return ev(left, c) * ev(right, c);
- case "/" : return ev(left, c) / ev(right, c);
- case "%" : return ev(left, c) % ev(right, c);
- case "-" : return ev(left, c) - ev(right, c);
- case "<<" : return ev(left, c) << ev(right, c);
- case ">>" : return ev(left, c) >> ev(right, c);
- case ">>>" : return ev(left, c) >>> ev(right, c);
- case "==" : return ev(left, c) == ev(right, c);
- case "===" : return ev(left, c) === ev(right, c);
- case "!=" : return ev(left, c) != ev(right, c);
- case "!==" : return ev(left, c) !== ev(right, c);
- case "<" : return ev(left, c) < ev(right, c);
- case "<=" : return ev(left, c) <= ev(right, c);
- case ">" : return ev(left, c) > ev(right, c);
- case ">=" : return ev(left, c) >= ev(right, c);
- case "in" : return ev(left, c) in ev(right, c);
- case "instanceof" : return ev(left, c) instanceof ev(right, c);
- }
- throw def;
- });
- def(AST_Conditional, function(compressor){
- return ev(this.condition, compressor)
- ? ev(this.consequent, compressor)
- : ev(this.alternative, compressor);
- });
- def(AST_SymbolRef, function(compressor){
- var d = this.definition();
- if (d && d.constant && d.init) return ev(d.init, compressor);
- throw def;
- });
- def(AST_Dot, function(compressor){
- if (compressor.option("unsafe") && this.property == "length") {
- var str = ev(this.expression, compressor);
- if (typeof str == "string")
- return str.length;
- }
- throw def;
- });
- })(function(node, func){
- node.DEFMETHOD("_eval", func);
- });
- // method to negate an expression
- (function(def){
- function basic_negation(exp) {
- return make_node(AST_UnaryPrefix, exp, {
- operator: "!",
- expression: exp
- });
- };
- def(AST_Node, function(){
- return basic_negation(this);
- });
- def(AST_Statement, function(){
- throw new Error("Cannot negate a statement");
- });
- def(AST_Function, function(){
- return basic_negation(this);
- });
- def(AST_UnaryPrefix, function(){
- if (this.operator == "!")
- return this.expression;
- return basic_negation(this);
- });
- def(AST_Seq, function(compressor){
- var self = this.clone();
- self.cdr = self.cdr.negate(compressor);
- return self;
- });
- def(AST_Conditional, function(compressor){
- var self = this.clone();
- self.consequent = self.consequent.negate(compressor);
- self.alternative = self.alternative.negate(compressor);
- return best_of(basic_negation(this), self);
- });
- def(AST_Binary, function(compressor){
- var self = this.clone(), op = this.operator;
- if (compressor.option("unsafe_comps")) {
- switch (op) {
- case "<=" : self.operator = ">" ; return self;
- case "<" : self.operator = ">=" ; return self;
- case ">=" : self.operator = "<" ; return self;
- case ">" : self.operator = "<=" ; return self;
- }
- }
- switch (op) {
- case "==" : self.operator = "!="; return self;
- case "!=" : self.operator = "=="; return self;
- case "===": self.operator = "!=="; return self;
- case "!==": self.operator = "==="; return self;
- case "&&":
- self.operator = "||";
- self.left = self.left.negate(compressor);
- self.right = self.right.negate(compressor);
- return best_of(basic_negation(this), self);
- case "||":
- self.operator = "&&";
- self.left = self.left.negate(compressor);
- self.right = self.right.negate(compressor);
- return best_of(basic_negation(this), self);
- }
- return basic_negation(this);
- });
- })(function(node, func){
- node.DEFMETHOD("negate", function(compressor){
- return func.call(this, compressor);
- });
- });
- // determine if expression has side effects
- (function(def){
- def(AST_Node, function(compressor){ return true });
- def(AST_EmptyStatement, function(compressor){ return false });
- def(AST_Constant, function(compressor){ return false });
- def(AST_This, function(compressor){ return false });
- def(AST_Call, function(compressor){
- var pure = compressor.option("pure_funcs");
- if (!pure) return true;
- if (typeof pure == "function") return pure(this);
- return pure.indexOf(this.expression.print_to_string()) < 0;
- });
- def(AST_Block, function(compressor){
- for (var i = this.body.length; --i >= 0;) {
- if (this.body[i].has_side_effects(compressor))
- return true;
- }
- return false;
- });
- def(AST_SimpleStatement, function(compressor){
- return this.body.has_side_effects(compressor);
- });
- def(AST_Defun, function(compressor){ return true });
- def(AST_Function, function(compressor){ return false });
- def(AST_Binary, function(compressor){
- return this.left.has_side_effects(compressor)
- || this.right.has_side_effects(compressor);
- });
- def(AST_Assign, function(compressor){ return true });
- def(AST_Conditional, function(compressor){
- return this.condition.has_side_effects(compressor)
- || this.consequent.has_side_effects(compressor)
- || this.alternative.has_side_effects(compressor);
- });
- def(AST_Unary, function(compressor){
- return this.operator == "delete"
- || this.operator == "++"
- || this.operator == "--"
- || this.expression.has_side_effects(compressor);
- });
- def(AST_SymbolRef, function(compressor){
- return this.global() && this.undeclared();
- });
- def(AST_Object, function(compressor){
- for (var i = this.properties.length; --i >= 0;)
- if (this.properties[i].has_side_effects(compressor))
- return true;
- return false;
- });
- def(AST_ObjectProperty, function(compressor){
- return this.value.has_side_effects(compressor);
- });
- def(AST_Array, function(compressor){
- for (var i = this.elements.length; --i >= 0;)
- if (this.elements[i].has_side_effects(compressor))
- return true;
- return false;
- });
- def(AST_Dot, function(compressor){
- if (!compressor.option("pure_getters")) return true;
- return this.expression.has_side_effects(compressor);
- });
- def(AST_Sub, function(compressor){
- if (!compressor.option("pure_getters")) return true;
- return this.expression.has_side_effects(compressor)
- || this.property.has_side_effects(compressor);
- });
- def(AST_PropAccess, function(compressor){
- return !compressor.option("pure_getters");
- });
- def(AST_Seq, function(compressor){
- return this.car.has_side_effects(compressor)
- || this.cdr.has_side_effects(compressor);
- });
- })(function(node, func){
- node.DEFMETHOD("has_side_effects", func);
- });
- // tell me if a statement aborts
- function aborts(thing) {
- return thing && thing.aborts();
- };
- (function(def){
- def(AST_Statement, function(){ return null });
- def(AST_Jump, function(){ return this });
- function block_aborts(){
- var n = this.body.length;
- return n > 0 && aborts(this.body[n - 1]);
- };
- def(AST_BlockStatement, block_aborts);
- def(AST_SwitchBranch, block_aborts);
- def(AST_If, function(){
- return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
- });
- })(function(node, func){
- node.DEFMETHOD("aborts", func);
- });
- /* -----[ optimizers ]----- */
- OPT(AST_Directive, function(self, compressor){
- if (compressor.has_directive(self.value) === "up") {
- return make_node(AST_EmptyStatement, self);
- }
- return self;
- });
- OPT(AST_Debugger, function(self, compressor){
- if (compressor.option("drop_debugger"))
- return make_node(AST_EmptyStatement, self);
- return self;
- });
- OPT(AST_LabeledStatement, function(self, compressor){
- if (self.body instanceof AST_Break
- && compressor.loopcontrol_target(self.body.label) === self.body) {
- return make_node(AST_EmptyStatement, self);
- }
- return self.label.references.length == 0 ? self.body : self;
- });
- OPT(AST_Block, function(self, compressor){
- self.body = tighten_body(self.body, compressor);
- return self;
- });
- OPT(AST_BlockStatement, function(self, compressor){
- self.body = tighten_body(self.body, compressor);
- switch (self.body.length) {
- case 1: return self.body[0];
- case 0: return make_node(AST_EmptyStatement, self);
- }
- return self;
- });
- AST_Scope.DEFMETHOD("drop_unused", function(compressor){
- var self = this;
- if (compressor.has_directive("use asm")) return self;
- if (compressor.option("unused")
- && !(self instanceof AST_Toplevel)
- && !self.uses_eval
- ) {
- var in_use = [];
- var initializations = new Dictionary();
- // pass 1: find out which symbols are directly used in
- // this scope (not in nested scopes).
- var scope = this;
- var tw = new TreeWalker(function(node, descend){
- if (node !== self) {
- if (node instanceof AST_Defun) {
- initializations.add(node.name.name, node);
- return true; // don't go in nested scopes
- }
- if (node instanceof AST_Definitions && scope === self) {
- node.definitions.forEach(function(def){
- if (def.value) {
- initializations.add(def.name.name, def.value);
- if (def.value.has_side_effects(compressor)) {
- def.value.walk(tw);
- }
- }
- });
- return true;
- }
- if (node instanceof AST_SymbolRef) {
- push_uniq(in_use, node.definition());
- return true;
- }
- if (node instanceof AST_Scope) {
- var save_scope = scope;
- scope = node;
- descend();
- scope = save_scope;
- return true;
- }
- }
- });
- self.walk(tw);
- // pass 2: for every used symbol we need to walk its
- // initialization code to figure out if it uses other
- // symbols (that may not be in_use).
- for (var i = 0; i < in_use.length; ++i) {
- in_use[i].orig.forEach(function(decl){
- // undeclared globals will be instanceof AST_SymbolRef
- var init = initializations.get(decl.name);
- if (init) init.forEach(function(init){
- var tw = new TreeWalker(function(node){
- if (node instanceof AST_SymbolRef) {
- push_uniq(in_use, node.definition());
- }
- });
- init.walk(tw);
- });
- });
- }
- // pass 3: we should drop declarations not in_use
- var tt = new TreeTransformer(
- function before(node, descend, in_list) {
- if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
- if (!compressor.option("keep_fargs")) {
- for (var a = node.argnames, i = a.length; --i >= 0;) {
- var sym = a[i];
- if (sym.unreferenced()) {
- a.pop();
- compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
- name : sym.name,
- file : sym.start.file,
- line : sym.start.line,
- col : sym.start.col
- });
- }
- else break;
- }
- }
- }
- if (node instanceof AST_Defun && node !== self) {
- if (!member(node.name.definition(), in_use)) {
- compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
- name : node.name.name,
- file : node.name.start.file,
- line : node.name.start.line,
- col : node.name.start.col
- });
- return make_node(AST_EmptyStatement, node);
- }
- return node;
- }
- if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
- var def = node.definitions.filter(function(def){
- if (member(def.name.definition(), in_use)) return true;
- var w = {
- name : def.name.name,
- file : def.name.start.file,
- line : def.name.start.line,
- col : def.name.start.col
- };
- if (def.value && def.value.has_side_effects(compressor)) {
- def._unused_side_effects = true;
- compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
- return true;
- }
- compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w);
- return false;
- });
- // place uninitialized names at the start
- def = mergeSort(def, function(a, b){
- if (!a.value && b.value) return -1;
- if (!b.value && a.value) return 1;
- return 0;
- });
- // for unused names whose initialization has
- // side effects, we can cascade the init. code
- // into the next one, or next statement.
- var side_effects = [];
- for (var i = 0; i < def.length;) {
- var x = def[i];
- if (x._unused_side_effects) {
- side_effects.push(x.value);
- def.splice(i, 1);
- } else {
- if (side_effects.length > 0) {
- side_effects.push(x.value);
- x.value = AST_Seq.from_array(side_effects);
- side_effects = [];
- }
- ++i;
- }
- }
- if (side_effects.length > 0) {
- side_effects = make_node(AST_BlockStatement, node, {
- body: [ make_node(AST_SimpleStatement, node, {
- body: AST_Seq.from_array(side_effects)
- }) ]
- });
- } else {
- side_effects = null;
- }
- if (def.length == 0 && !side_effects) {
- return make_node(AST_EmptyStatement, node);
- }
- if (def.length == 0) {
- return in_list ? MAP.splice(side_effects.body) : side_effects;
- }
- node.definitions = def;
- if (side_effects) {
- side_effects.body.unshift(node);
- return in_list ? MAP.splice(side_effects.body) : side_effects;
- }
- return node;
- }
- if (node instanceof AST_For) {
- descend(node, this);
- if (node.init instanceof AST_BlockStatement) {
- // certain combination of unused name + side effect leads to:
- // https://github.com/mishoo/UglifyJS2/issues/44
- // that's an invalid AST.
- // We fix it at this stage by moving the `var` outside the `for`.
- var body = node.init.body.slice(0, -1);
- node.init = node.init.body.slice(-1)[0].body;
- body.push(node);
- return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
- body: body
- });
- }
- }
- if (node instanceof AST_Scope && node !== self)
- return node;
- }
- );
- self.transform(tt);
- }
- });
- AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
- var self = this;
- if (compressor.has_directive("use asm")) return self;
- var hoist_funs = compressor.option("hoist_funs");
- var hoist_vars = compressor.option("hoist_vars");
- if (hoist_funs || hoist_vars) {
- var dirs = [];
- var hoisted = [];
- var vars = new Dictionary(), vars_found = 0, var_decl = 0;
- // let's count var_decl first, we seem to waste a lot of
- // space if we hoist `var` when there's only one.
- self.walk(new TreeWalker(function(node){
- if (node instanceof AST_Scope && node !== self)
- return true;
- if (node instanceof AST_Var) {
- ++var_decl;
- return true;
- }
- }));
- hoist_vars = hoist_vars && var_decl > 1;
- var tt = new TreeTransformer(
- function before(node) {
- if (node !== self) {
- if (node instanceof AST_Directive) {
- dirs.push(node);
- return make_node(AST_EmptyStatement, node);
- }
- if (node instanceof AST_Defun && hoist_funs) {
- hoisted.push(node);
- return make_node(AST_EmptyStatement, node);
- }
- if (node instanceof AST_Var && hoist_vars) {
- node.definitions.forEach(function(def){
- vars.set(def.name.name, def);
- ++vars_found;
- });
- var seq = node.to_assignments();
- var p = tt.parent();
- if (p instanceof AST_ForIn && p.init === node) {
- if (seq == null) return node.definitions[0].name;
- return seq;
- }
- if (p instanceof AST_For && p.init === node) {
- return seq;
- }
- if (!seq) return make_node(AST_EmptyStatement, node);
- return make_node(AST_SimpleStatement, node, {
- body: seq
- });
- }
- if (node instanceof AST_Scope)
- return node; // to avoid descending in nested scopes
- }
- }
- );
- self = self.transform(tt);
- if (vars_found > 0) {
- // collect only vars which don't show up in self's arguments list
- var defs = [];
- vars.each(function(def, name){
- if (self instanceof AST_Lambda
- && find_if(function(x){ return x.name == def.name.name },
- self.argnames)) {
- vars.del(name);
- } else {
- def = def.clone();
- def.value = null;
- defs.push(def);
- vars.set(name, def);
- }
- });
- if (defs.length > 0) {
- // try to merge in assignments
- for (var i = 0; i < self.body.length;) {
- if (self.body[i] instanceof AST_SimpleStatement) {
- var expr = self.body[i].body, sym, assign;
- if (expr instanceof AST_Assign
- && expr.operator == "="
- && (sym = expr.left) instanceof AST_Symbol
- && vars.has(sym.name))
- {
- var def = vars.get(sym.name);
- if (def.value) break;
- def.value = expr.right;
- remove(defs, def);
- defs.push(def);
- self.body.splice(i, 1);
- continue;
- }
- if (expr instanceof AST_Seq
- && (assign = expr.car) instanceof AST_Assign
- && assign.operator == "="
- && (sym = assign.left) instanceof AST_Symbol
- && vars.has(sym.name))
- {
- var def = vars.get(sym.name);
- if (def.value) break;
- def.value = assign.right;
- remove(defs, def);
- defs.push(def);
- self.body[i].body = expr.cdr;
- continue;
- }
- }
- if (self.body[i] instanceof AST_EmptyStatement) {
- self.body.splice(i, 1);
- continue;
- }
- if (self.body[i] instanceof AST_BlockStatement) {
- var tmp = [ i, 1 ].concat(self.body[i].body);
- self.body.splice.apply(self.body, tmp);
- continue;
- }
- break;
- }
- defs = make_node(AST_Var, self, {
- definitions: defs
- });
- hoisted.push(defs);
- };
- }
- self.body = dirs.concat(hoisted, self.body);
- }
- return self;
- });
- OPT(AST_SimpleStatement, function(self, compressor){
- if (compressor.option("side_effects")) {
- if (!self.body.has_side_effects(compressor)) {
- compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
- return make_node(AST_EmptyStatement, self);
- }
- }
- return self;
- });
- OPT(AST_DWLoop, function(self, compressor){
- var cond = self.condition.evaluate(compressor);
- self.condition = cond[0];
- if (!compressor.option("loops")) return self;
- if (cond.length > 1) {
- if (cond[1]) {
- return make_node(AST_For, self, {
- body: self.body
- });
- } else if (self instanceof AST_While) {
- if (compressor.option("dead_code")) {
- var a = [];
- extract_declarations_from_unreachable_code(compressor, self.body, a);
- return make_node(AST_BlockStatement, self, { body: a });
- }
- }
- }
- return self;
- });
- function if_break_in_loop(self, compressor) {
- function drop_it(rest) {
- rest = as_statement_array(rest);
- if (self.body instanceof AST_BlockStatement) {
- self.body = self.body.clone();
- self.body.body = rest.concat(self.body.body.slice(1));
- self.body = self.body.transform(compressor);
- } else {
- self.body = make_node(AST_BlockStatement, self.body, {
- body: rest
- }).transform(compressor);
- }
- if_break_in_loop(self, compressor);
- }
- var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
- if (first instanceof AST_If) {
- if (first.body instanceof AST_Break
- && compressor.loopcontrol_target(first.body.label) === self) {
- if (self.condition) {
- self.condition = make_node(AST_Binary, self.condition, {
- left: self.condition,
- operator: "&&",
- right: first.condition.negate(compressor),
- });
- } else {
- self.condition = first.condition.negate(compressor);
- }
- drop_it(first.alternative);
- }
- else if (first.alternative instanceof AST_Break
- && compressor.loopcontrol_target(first.alternative.label) === self) {
- if (self.condition) {
- self.condition = make_node(AST_Binary, self.condition, {
- left: self.condition,
- operator: "&&",
- right: first.condition,
- });
- } else {
- self.condition = first.condition;
- }
- drop_it(first.body);
- }
- }
- };
- OPT(AST_While, function(self, compressor) {
- if (!compressor.option("loops")) return self;
- self = AST_DWLoop.prototype.optimize.call(self, compressor);
- if (self instanceof AST_While) {
- if_break_in_loop(self, compressor);
- self = make_node(AST_For, self, self).transform(compressor);
- }
- return self;
- });
- OPT(AST_For, function(self, compressor){
- var cond = self.condition;
- if (cond) {
- cond = cond.evaluate(compressor);
- self.condition = cond[0];
- }
- if (!compressor.option("loops")) return self;
- if (cond) {
- if (cond.length > 1 && !cond[1]) {
- if (compressor.option("dead_code")) {
- var a = [];
- if (self.init instanceof AST_Statement) {
- a.push(self.init);
- }
- else if (self.init) {
- a.push(make_node(AST_SimpleStatement, self.init, {
- body: self.init
- }));
- }
- extract_declarations_from_unreachable_code(compressor, self.body, a);
- return make_node(AST_BlockStatement, self, { body: a });
- }
- }
- }
- if_break_in_loop(self, compressor);
- return self;
- });
- OPT(AST_If, function(self, compressor){
- if (!compressor.option("conditionals")) return self;
- // if condition can be statically determined, warn and drop
- // one of the blocks. note, statically determined implies
- // “has no side effects”; also it doesn't work for cases like
- // `x && true`, though it probably should.
- var cond = self.condition.evaluate(compressor);
- self.condition = cond[0];
- if (cond.length > 1) {
- if (cond[1]) {
- compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
- if (compressor.option("dead_code")) {
- var a = [];
- if (self.alternative) {
- extract_declarations_from_unreachable_code(compressor, self.alternative, a);
- }
- a.push(self.body);
- return make_node(AST_BlockStatement, self, { body: a }).transform(compressor);
- }
- } else {
- compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
- if (compressor.option("dead_code")) {
- var a = [];
- extract_declarations_from_unreachable_code(compressor, self.body, a);
- if (self.alternative) a.push(self.alternative);
- return make_node(AST_BlockStatement, self, { body: a }).transform(compressor);
- }
- }
- }
- if (is_empty(self.alternative)) self.alternative = null;
- var negated = self.condition.negate(compressor);
- var negated_is_best = best_of(self.condition, negated) === negated;
- if (self.alternative && negated_is_best) {
- negated_is_best = false; // because we already do the switch here.
- self.condition = negated;
- var tmp = self.body;
- self.body = self.alternative || make_node(AST_EmptyStatement);
- self.alternative = tmp;
- }
- if (is_empty(self.body) && is_empty(self.alternative)) {
- return make_node(AST_SimpleStatement, self.condition, {
- body: self.condition
- }).transform(compressor);
- }
- if (self.body instanceof AST_SimpleStatement
- && self.alternative instanceof AST_SimpleStatement) {
- return make_node(AST_SimpleStatement, self, {
- body: make_node(AST_Conditional, self, {
- condition : self.condition,
- consequent : self.body.body,
- alternative : self.alternative.body
- })
- }).transform(compressor);
- }
- if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
- if (negated_is_best) return make_node(AST_SimpleStatement, self, {
- body: make_node(AST_Binary, self, {
- operator : "||",
- left : negated,
- right : self.body.body
- })
- }).transform(compressor);
- return make_node(AST_SimpleStatement, self, {
- body: make_node(AST_Binary, self, {
- operator : "&&",
- left : self.condition,
- right : self.body.body
- })
- }).transform(compressor);
- }
- if (self.body instanceof AST_EmptyStatement
- && self.alternative
- && self.alternative instanceof AST_SimpleStatement) {
- return make_node(AST_SimpleStatement, self, {
- body: make_node(AST_Binary, self, {
- operator : "||",
- left : self.condition,
- right : self.alternative.body
- })
- }).transform(compressor);
- }
- if (self.body instanceof AST_Exit
- && self.alternative instanceof AST_Exit
- && self.body.TYPE == self.alternative.TYPE) {
- return make_node(self.body.CTOR, self, {
- value: make_node(AST_Conditional, self, {
- condition : self.condition,
- consequent : self.body.value || make_node(AST_Undefined, self.body).optimize(compressor),
- alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).optimize(compressor)
- })
- }).transform(compressor);
- }
- if (self.body instanceof AST_If
- && !self.body.alternative
- && !self.alternative) {
- self.condition = make_node(AST_Binary, self.condition, {
- operator: "&&",
- left: self.condition,
- right: self.body.condition
- }).transform(compressor);
- self.body = self.body.body;
- }
- if (aborts(self.body)) {
- if (self.alternative) {
- var alt = self.alternative;
- self.alternative = null;
- return make_node(AST_BlockStatement, self, {
- body: [ self, alt ]
- }).transform(compressor);
- }
- }
- if (aborts(self.alternative)) {
- var body = self.body;
- self.body = self.alternative;
- self.condition = negated_is_best ? negated : self.condition.negate(compressor);
- self.alternative = null;
- return make_node(AST_BlockStatement, self, {
- body: [ self, body ]
- }).transform(compressor);
- }
- return self;
- });
- OPT(AST_Switch, function(self, compressor){
- if (self.body.length == 0 && compressor.option("conditionals")) {
- return make_node(AST_SimpleStatement, self, {
- body: self.expression
- }).transform(compressor);
- }
- for(;;) {
- var last_branch = self.body[self.body.length - 1];
- if (last_branch) {
- var stat = last_branch.body[last_branch.body.length - 1]; // last statement
- if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
- last_branch.body.pop();
- if (last_branch instanceof AST_Default && last_branch.body.length == 0) {
- self.body.pop();
- continue;
- }
- }
- break;
- }
- var exp = self.expression.evaluate(compressor);
- out: if (exp.length == 2) try {
- // constant expression
- self.expression = exp[0];
- if (!compressor.option("dead_code")) break out;
- var value = exp[1];
- var in_if = false;
- var in_block = false;
- var started = false;
- var stopped = false;
- var ruined = false;
- var tt = new TreeTransformer(function(node, descend, in_list){
- if (node instanceof AST_Lambda || node instanceof AST_SimpleStatement) {
- // no need to descend these node types
- return node;
- }
- else if (node instanceof AST_Switch && node === self) {
- node = node.clone();
- descend(node, this);
- return ruined ? node : make_node(AST_BlockStatement, node, {
- body: node.body.reduce(function(a, branch){
- return a.concat(branch.body);
- }, [])
- }).transform(compressor);
- }
- else if (node instanceof AST_If || node instanceof AST_Try) {
- var save = in_if;
- in_if = !in_block;
- descend(node, this);
- in_if = save;
- return node;
- }
- else if (node instanceof AST_StatementWithBody || node instanceof AST_Switch) {
- var save = in_block;
- in_block = true;
- descend(node, this);
- in_block = save;
- return node;
- }
- else if (node instanceof AST_Break && this.loopcontrol_target(node.label) === self) {
- if (in_if) {
- ruined = true;
- return node;
- }
- if (in_block) return node;
- stopped = true;
- return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
- }
- else if (node instanceof AST_SwitchBranch && this.parent() === self) {
- if (stopped) return MAP.skip;
- if (node instanceof AST_Case) {
- var exp = node.expression.evaluate(compressor);
- if (exp.length < 2) {
- // got a case with non-constant expression, baling out
- throw self;
- }
- if (exp[1] === value || started) {
- started = true;
- if (aborts(node)) stopped = true;
- descend(node, this);
- return node;
- }
- return MAP.skip;
- }
- descend(node, this);
- return node;
- }
- });
- tt.stack = compressor.stack.slice(); // so that's able to see parent nodes
- self = self.transform(tt);
- } catch(ex) {
- if (ex !== self) throw ex;
- }
- return self;
- });
- OPT(AST_Case, function(self, compressor){
- self.body = tighten_body(self.body, compressor);
- return self;
- });
- OPT(AST_Try, function(self, compressor){
- self.body = tighten_body(self.body, compressor);
- return self;
- });
- AST_Definitions.DEFMETHOD("remove_initializers", function(){
- this.definitions.forEach(function(def){ def.value = null });
- });
- AST_Definitions.DEFMETHOD("to_assignments", function(){
- var assignments = this.definitions.reduce(function(a, def){
- if (def.value) {
- var name = make_node(AST_SymbolRef, def.name, def.name);
- a.push(make_node(AST_Assign, def, {
- operator : "=",
- left : name,
- right : def.value
- }));
- }
- return a;
- }, []);
- if (assignments.length == 0) return null;
- return AST_Seq.from_array(assignments);
- });
- OPT(AST_Definitions, function(self, compressor){
- if (self.definitions.length == 0)
- return make_node(AST_EmptyStatement, self);
- return self;
- });
- OPT(AST_Function, function(self, compressor){
- self = AST_Lambda.prototype.optimize.call(self, compressor);
- if (compressor.option("unused") && !compressor.option("keep_fnames")) {
- if (self.name && self.name.unreferenced()) {
- self.name = null;
- }
- }
- return self;
- });
- OPT(AST_Call, function(self, compressor){
- if (compressor.option("unsafe")) {
- var exp = self.expression;
- if (exp instanceof AST_SymbolRef && exp.undeclared()) {
- switch (exp.name) {
- case "Array":
- if (self.args.length != 1) {
- return make_node(AST_Array, self, {
- elements: self.args
- }).transform(compressor);
- }
- break;
- case "Object":
- if (self.args.length == 0) {
- return make_node(AST_Object, self, {
- properties: []
- });
- }
- break;
- case "String":
- if (self.args.length == 0) return make_node(AST_String, self, {
- value: ""
- });
- if (self.args.length <= 1) return make_node(AST_Binary, self, {
- left: self.args[0],
- operator: "+",
- right: make_node(AST_String, self, { value: "" })
- }).transform(compressor);
- break;
- case "Number":
- if (self.args.length == 0) return make_node(AST_Number, self, {
- value: 0
- });
- if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
- expression: self.args[0],
- operator: "+"
- }).transform(compressor);
- case "Boolean":
- if (self.args.length == 0) return make_node(AST_False, self);
- if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
- expression: make_node(AST_UnaryPrefix, null, {
- expression: self.args[0],
- operator: "!"
- }),
- operator: "!"
- }).transform(compressor);
- break;
- case "Function":
- // new Function() => function(){}
- if (self.args.length == 0) return make_node(AST_Function, self, {
- argnames: [],
- body: []
- });
- if (all(self.args, function(x){ return x instanceof AST_String })) {
- // quite a corner-case, but we can handle it:
- // https://github.com/mishoo/UglifyJS2/issues/203
- // if the code argument is a constant, then we can minify it.
- try {
- var code = "(function(" + self.args.slice(0, -1).map(function(arg){
- return arg.value;
- }).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
- var ast = parse(code);
- ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
- var comp = new Compressor(compressor.options);
- ast = ast.transform(comp);
- ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
- ast.mangle_names();
- var fun;
- try {
- ast.walk(new TreeWalker(function(node){
- if (node instanceof AST_Lambda) {
- fun = node;
- throw ast;
- }
- }));
- } catch(ex) {
- if (ex !== ast) throw ex;
- };
- if (!fun) return self;
- var args = fun.argnames.map(function(arg, i){
- return make_node(AST_String, self.args[i], {
- value: arg.print_to_string()
- });
- });
- var code = OutputStream();
- AST_BlockStatement.prototype._codegen.call(fun, fun, code);
- code = code.toString().replace(/^\{|\}$/g, "");
- args.push(make_node(AST_String, self.args[self.args.length - 1], {
- value: code
- }));
- self.args = args;
- return self;
- } catch(ex) {
- if (ex instanceof JS_Parse_Error) {
- compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
- compressor.warn(ex.toString());
- } else {
- console.log(ex);
- throw ex;
- }
- }
- }
- break;
- }
- }
- else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
- return make_node(AST_Binary, self, {
- left: make_node(AST_String, self, { value: "" }),
- operator: "+",
- right: exp.expression
- }).transform(compressor);
- }
- else if (exp instanceof AST_Dot && exp.expression instanceof AST_Array && exp.property == "join") EXIT: {
- var separator = self.args.length == 0 ? "," : self.args[0].evaluate(compressor)[1];
- if (separator == null) break EXIT; // not a constant
- var elements = exp.expression.elements.reduce(function(a, el){
- el = el.evaluate(compressor);
- if (a.length == 0 || el.length == 1) {
- a.push(el);
- } else {
- var last = a[a.length - 1];
- if (last.length == 2) {
- // it's a constant
- var val = "" + last[1] + separator + el[1];
- a[a.length - 1] = [ make_node_from_constant(compressor, val, last[0]), val ];
- } else {
- a.push(el);
- }
- }
- return a;
- }, []);
- if (elements.length == 0) return make_node(AST_String, self, { value: "" });
- if (elements.length == 1) return elements[0][0];
- if (separator == "") {
- var first;
- if (elements[0][0] instanceof AST_String
- || elements[1][0] instanceof AST_String) {
- first = elements.shift()[0];
- } else {
- first = make_node(AST_String, self, { value: "" });
- }
- return elements.reduce(function(prev, el){
- return make_node(AST_Binary, el[0], {
- operator : "+",
- left : prev,
- right : el[0],
- });
- }, first).transform(compressor);
- }
- // need this awkward cloning to not affect original element
- // best_of will decide which one to get through.
- var node = self.clone();
- node.expression = node.expression.clone();
- node.expression.expression = node.expression.expression.clone();
- node.expression.expression.elements = elements.map(function(el){
- return el[0];
- });
- return best_of(self, node);
- }
- }
- if (compressor.option("side_effects")) {
- if (self.expression instanceof AST_Function
- && self.args.length == 0
- && !AST_Block.prototype.has_side_effects.call(self.expression, compressor)) {
- return make_node(AST_Undefined, self).transform(compressor);
- }
- }
- if (compressor.option("drop_console")) {
- if (self.expression instanceof AST_PropAccess) {
- var name = self.expression.expression;
- while (name.expression) {
- name = name.expression;
- }
- if (name instanceof AST_SymbolRef
- && name.name == "console"
- && name.undeclared()) {
- return make_node(AST_Undefined, self).transform(compressor);
- }
- }
- }
- return self.evaluate(compressor)[0];
- });
- OPT(AST_New, function(self, compressor){
- if (compressor.option("unsafe")) {
- var exp = self.expression;
- if (exp instanceof AST_SymbolRef && exp.undeclared()) {
- switch (exp.name) {
- case "Object":
- case "RegExp":
- case "Function":
- case "Error":
- case "Array":
- return make_node(AST_Call, self, self).transform(compressor);
- }
- }
- }
- return self;
- });
- OPT(AST_Seq, function(self, compressor){
- if (!compressor.option("side_effects"))
- return self;
- if (!self.car.has_side_effects(compressor)) {
- // we shouldn't compress (1,func)(something) to
- // func(something) because that changes the meaning of
- // the func (becomes lexical instead of global).
- var p = compressor.parent();
- if (!(p instanceof AST_Call && p.expression === self)) {
- return self.cdr;
- }
- }
- if (compressor.option("cascade")) {
- if (self.car instanceof AST_Assign
- && !self.car.left.has_side_effects(compressor)) {
- if (self.car.left.equivalent_to(self.cdr)) {
- return self.car;
- }
- if (self.cdr instanceof AST_Call
- && self.cdr.expression.equivalent_to(self.car.left)) {
- self.cdr.expression = self.car;
- return self.cdr;
- }
- }
- if (!self.car.has_side_effects(compressor)
- && !self.cdr.has_side_effects(compressor)
- && self.car.equivalent_to(self.cdr)) {
- return self.car;
- }
- }
- if (self.cdr instanceof AST_UnaryPrefix
- && self.cdr.operator == "void"
- && !self.cdr.expression.has_side_effects(compressor)) {
- self.cdr.expression = self.car;
- return self.cdr;
- }
- if (self.cdr instanceof AST_Undefined) {
- return make_node(AST_UnaryPrefix, self, {
- operator : "void",
- expression : self.car
- });
- }
- return self;
- });
- AST_Unary.DEFMETHOD("lift_sequences", function(compressor){
- if (compressor.option("sequences")) {
- if (this.expression instanceof AST_Seq) {
- var seq = this.expression;
- var x = seq.to_array();
- this.expression = x.pop();
- x.push(this);
- seq = AST_Seq.from_array(x).transform(compressor);
- return seq;
- }
- }
- return this;
- });
- OPT(AST_UnaryPostfix, function(self, compressor){
- return self.lift_sequences(compressor);
- });
- OPT(AST_UnaryPrefix, function(self, compressor){
- self = self.lift_sequences(compressor);
- var e = self.expression;
- if (compressor.option("booleans") && compressor.in_boolean_context()) {
- switch (self.operator) {
- case "!":
- if (e instanceof AST_UnaryPrefix && e.operator == "!") {
- // !!foo ==> foo, if we're in boolean context
- return e.expression;
- }
- break;
- case "typeof":
- // typeof always returns a non-empty string, thus it's
- // always true in booleans
- compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
- return make_node(AST_True, self);
- }
- if (e instanceof AST_Binary && self.operator == "!") {
- self = best_of(self, e.negate(compressor));
- }
- }
- return self.evaluate(compressor)[0];
- });
- function has_side_effects_or_prop_access(node, compressor) {
- var save_pure_getters = compressor.option("pure_getters");
- compressor.options.pure_getters = false;
- var ret = node.has_side_effects(compressor);
- compressor.options.pure_getters = save_pure_getters;
- return ret;
- }
- AST_Binary.DEFMETHOD("lift_sequences", function(compressor){
- if (compressor.option("sequences")) {
- if (this.left instanceof AST_Seq) {
- var seq = this.left;
- var x = seq.to_array();
- this.left = x.pop();
- x.push(this);
- seq = AST_Seq.from_array(x).transform(compressor);
- return seq;
- }
- if (this.right instanceof AST_Seq
- && this instanceof AST_Assign
- && !has_side_effects_or_prop_access(this.left, compressor)) {
- var seq = this.right;
- var x = seq.to_array();
- this.right = x.pop();
- x.push(this);
- seq = AST_Seq.from_array(x).transform(compressor);
- return seq;
- }
- }
- return this;
- });
- var commutativeOperators = makePredicate("== === != !== * & | ^");
- OPT(AST_Binary, function(self, compressor){
- function reverse(op, force) {
- if (force || !(self.left.has_side_effects(compressor) || self.right.has_side_effects(compressor))) {
- if (op) self.operator = op;
- var tmp = self.left;
- self.left = self.right;
- self.right = tmp;
- }
- }
- if (commutativeOperators(self.operator)) {
- if (self.right instanceof AST_Constant
- && !(self.left instanceof AST_Constant)) {
- // if right is a constant, whatever side effects the
- // left side might have could not influence the
- // result. hence, force switch.
- if (!(self.left instanceof AST_Binary
- && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
- reverse(null, true);
- }
- }
- if (/^[!=]==?$/.test(self.operator)) {
- if (self.left instanceof AST_SymbolRef && self.right instanceof AST_Conditional) {
- if (self.right.consequent instanceof AST_SymbolRef
- && self.right.consequent.definition() === self.left.definition()) {
- if (/^==/.test(self.operator)) return self.right.condition;
- if (/^!=/.test(self.operator)) return self.right.condition.negate(compressor);
- }
- if (self.right.alternative instanceof AST_SymbolRef
- && self.right.alternative.definition() === self.left.definition()) {
- if (/^==/.test(self.operator)) return self.right.condition.negate(compressor);
- if (/^!=/.test(self.operator)) return self.right.condition;
- }
- }
- if (self.right instanceof AST_SymbolRef && self.left instanceof AST_Conditional) {
- if (self.left.consequent instanceof AST_SymbolRef
- && self.left.consequent.definition() === self.right.definition()) {
- if (/^==/.test(self.operator)) return self.left.condition;
- if (/^!=/.test(self.operator)) return self.left.condition.negate(compressor);
- }
- if (self.left.alternative instanceof AST_SymbolRef
- && self.left.alternative.definition() === self.right.definition()) {
- if (/^==/.test(self.operator)) return self.left.condition.negate(compressor);
- if (/^!=/.test(self.operator)) return self.left.condition;
- }
- }
- }
- }
- self = self.lift_sequences(compressor);
- if (compressor.option("comparisons")) switch (self.operator) {
- case "===":
- case "!==":
- if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
- (self.left.is_boolean() && self.right.is_boolean())) {
- self.operator = self.operator.substr(0, 2);
- }
- // XXX: intentionally falling down to the next case
- case "==":
- case "!=":
- if (self.left instanceof AST_String
- && self.left.value == "undefined"
- && self.right instanceof AST_UnaryPrefix
- && self.right.operator == "typeof"
- && compressor.option("unsafe")) {
- if (!(self.right.expression instanceof AST_SymbolRef)
- || !self.right.expression.undeclared()) {
- self.right = self.right.expression;
- self.left = make_node(AST_Undefined, self.left).optimize(compressor);
- if (self.operator.length == 2) self.operator += "=";
- }
- }
- break;
- }
- if (compressor.option("conditionals")) {
- if (self.operator == "&&") {
- var ll = self.left.evaluate(compressor);
- if (ll.length > 1) {
- if (ll[1]) {
- compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
- var rr = self.right.evaluate(compressor);
- return rr[0];
- } else {
- compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
- return ll[0];
- }
- }
- }
- else if (self.operator == "||") {
- var ll = self.left.evaluate(compressor);
- if (ll.length > 1) {
- if (ll[1]) {
- compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
- return ll[0];
- } else {
- compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
- var rr = self.right.evaluate(compressor);
- return rr[0];
- }
- }
- }
- }
- if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) {
- case "&&":
- var ll = self.left.evaluate(compressor);
- var rr = self.right.evaluate(compressor);
- if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) {
- compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
- if (self.left.has_side_effects(compressor)) {
- return make_node(AST_Seq, self, {
- car: self.left,
- cdr: make_node(AST_False)
- }).optimize(compressor);
- }
- return make_node(AST_False, self);
- }
- if (ll.length > 1 && ll[1]) {
- return rr[0];
- }
- if (rr.length > 1 && rr[1]) {
- return ll[0];
- }
- break;
- case "||":
- var ll = self.left.evaluate(compressor);
- var rr = self.right.evaluate(compressor);
- if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) {
- compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
- if (self.left.has_side_effects(compressor)) {
- return make_node(AST_Seq, self, {
- car: self.left,
- cdr: make_node(AST_True)
- }).optimize(compressor);
- }
- return make_node(AST_True, self);
- }
- if (ll.length > 1 && !ll[1]) {
- return rr[0];
- }
- if (rr.length > 1 && !rr[1]) {
- return ll[0];
- }
- break;
- case "+":
- var ll = self.left.evaluate(compressor);
- var rr = self.right.evaluate(compressor);
- if ((ll.length > 1 && ll[0] instanceof AST_String && ll[1]) ||
- (rr.length > 1 && rr[0] instanceof AST_String && rr[1])) {
- compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
- return make_node(AST_True, self);
- }
- break;
- }
- if (compressor.option("comparisons") && self.is_boolean()) {
- if (!(compressor.parent() instanceof AST_Binary)
- || compressor.parent() instanceof AST_Assign) {
- var negated = make_node(AST_UnaryPrefix, self, {
- operator: "!",
- expression: self.negate(compressor)
- });
- self = best_of(self, negated);
- }
- switch (self.operator) {
- case "<": reverse(">"); break;
- case "<=": reverse(">="); break;
- }
- }
- if (self.operator == "+" && self.right instanceof AST_String
- && self.right.getValue() === "" && self.left instanceof AST_Binary
- && self.left.operator == "+" && self.left.is_string(compressor)) {
- return self.left;
- }
- if (compressor.option("evaluate")) {
- if (self.operator == "+") {
- if (self.left instanceof AST_Constant
- && self.right instanceof AST_Binary
- && self.right.operator == "+"
- && self.right.left instanceof AST_Constant
- && self.right.is_string(compressor)) {
- self = make_node(AST_Binary, self, {
- operator: "+",
- left: make_node(AST_String, null, {
- value: "" + self.left.getValue() + self.right.left.getValue(),
- start: self.left.start,
- end: self.right.left.end
- }),
- right: self.right.right
- });
- }
- if (self.right instanceof AST_Constant
- && self.left instanceof AST_Binary
- && self.left.operator == "+"
- && self.left.right instanceof AST_Constant
- && self.left.is_string(compressor)) {
- self = make_node(AST_Binary, self, {
- operator: "+",
- left: self.left.left,
- right: make_node(AST_String, null, {
- value: "" + self.left.right.getValue() + self.right.getValue(),
- start: self.left.right.start,
- end: self.right.end
- })
- });
- }
- if (self.left instanceof AST_Binary
- && self.left.operator == "+"
- && self.left.is_string(compressor)
- && self.left.right instanceof AST_Constant
- && self.right instanceof AST_Binary
- && self.right.operator == "+"
- && self.right.left instanceof AST_Constant
- && self.right.is_string(compressor)) {
- self = make_node(AST_Binary, self, {
- operator: "+",
- left: make_node(AST_Binary, self.left, {
- operator: "+",
- left: self.left.left,
- right: make_node(AST_String, null, {
- value: "" + self.left.right.getValue() + self.right.left.getValue(),
- start: self.left.right.start,
- end: self.right.left.end
- })
- }),
- right: self.right.right
- });
- }
- }
- }
- // x && (y && z) ==> x && y && z
- // x || (y || z) ==> x || y || z
- if (self.right instanceof AST_Binary
- && self.right.operator == self.operator
- && (self.operator == "&&" || self.operator == "||"))
- {
- self.left = make_node(AST_Binary, self.left, {
- operator : self.operator,
- left : self.left,
- right : self.right.left
- });
- self.right = self.right.right;
- return self.transform(compressor);
- }
- return self.evaluate(compressor)[0];
- });
- OPT(AST_SymbolRef, function(self, compressor){
- function isLHS(symbol, parent) {
- return (
- parent instanceof AST_Binary &&
- parent.operator === '=' &&
- parent.left === symbol
- );
- }
- if (self.undeclared() && !isLHS(self, compressor.parent())) {
- var defines = compressor.option("global_defs");
- if (defines && defines.hasOwnProperty(self.name)) {
- return make_node_from_constant(compressor, defines[self.name], self);
- }
- switch (self.name) {
- case "undefined":
- return make_node(AST_Undefined, self);
- case "NaN":
- return make_node(AST_NaN, self).transform(compressor);
- case "Infinity":
- return make_node(AST_Infinity, self).transform(compressor);
- }
- }
- return self;
- });
- OPT(AST_Infinity, function (self, compressor) {
- return make_node(AST_Binary, self, {
- operator : '/',
- left : make_node(AST_Number, self, {value: 1}),
- right : make_node(AST_Number, self, {value: 0})
- });
- });
- OPT(AST_Undefined, function(self, compressor){
- if (compressor.option("unsafe")) {
- var scope = compressor.find_parent(AST_Scope);
- var undef = scope.find_variable("undefined");
- if (undef) {
- var ref = make_node(AST_SymbolRef, self, {
- name : "undefined",
- scope : scope,
- thedef : undef
- });
- ref.reference();
- return ref;
- }
- }
- return self;
- });
- var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
- OPT(AST_Assign, function(self, compressor){
- self = self.lift_sequences(compressor);
- if (self.operator == "="
- && self.left instanceof AST_SymbolRef
- && self.right instanceof AST_Binary
- && self.right.left instanceof AST_SymbolRef
- && self.right.left.name == self.left.name
- && member(self.right.operator, ASSIGN_OPS)) {
- self.operator = self.right.operator + "=";
- self.right = self.right.right;
- }
- return self;
- });
- OPT(AST_Conditional, function(self, compressor){
- if (!compressor.option("conditionals")) return self;
- if (self.condition instanceof AST_Seq) {
- var car = self.condition.car;
- self.condition = self.condition.cdr;
- return AST_Seq.cons(car, self);
- }
- var cond = self.condition.evaluate(compressor);
- if (cond.length > 1) {
- if (cond[1]) {
- compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
- return self.consequent;
- } else {
- compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
- return self.alternative;
- }
- }
- var negated = cond[0].negate(compressor);
- if (best_of(cond[0], negated) === negated) {
- self = make_node(AST_Conditional, self, {
- condition: negated,
- consequent: self.alternative,
- alternative: self.consequent
- });
- }
- var consequent = self.consequent;
- var alternative = self.alternative;
- if (consequent instanceof AST_Assign
- && alternative instanceof AST_Assign
- && consequent.operator == alternative.operator
- && consequent.left.equivalent_to(alternative.left)
- && !consequent.left.has_side_effects(compressor)
- ) {
- /*
- * Stuff like this:
- * if (foo) exp = something; else exp = something_else;
- * ==>
- * exp = foo ? something : something_else;
- */
- return make_node(AST_Assign, self, {
- operator: consequent.operator,
- left: consequent.left,
- right: make_node(AST_Conditional, self, {
- condition: self.condition,
- consequent: consequent.right,
- alternative: alternative.right
- })
- });
- }
- if (consequent instanceof AST_Call
- && alternative.TYPE === consequent.TYPE
- && consequent.args.length == alternative.args.length
- && !consequent.expression.has_side_effects(compressor)
- && consequent.expression.equivalent_to(alternative.expression)) {
- if (consequent.args.length == 0) {
- return make_node(AST_Seq, self, {
- car: self.condition,
- cdr: consequent
- });
- }
- if (consequent.args.length == 1) {
- consequent.args[0] = make_node(AST_Conditional, self, {
- condition: self.condition,
- consequent: consequent.args[0],
- alternative: alternative.args[0]
- });
- return consequent;
- }
- }
- // x?y?z:a:a --> x&&y?z:a
- if (consequent instanceof AST_Conditional
- && consequent.alternative.equivalent_to(alternative)) {
- return make_node(AST_Conditional, self, {
- condition: make_node(AST_Binary, self, {
- left: self.condition,
- operator: "&&",
- right: consequent.condition
- }),
- consequent: consequent.consequent,
- alternative: alternative
- });
- }
- // x=y?1:1 --> x=1
- if (consequent instanceof AST_Constant
- && alternative instanceof AST_Constant
- && consequent.equivalent_to(alternative)) {
- if (self.condition.has_side_effects(compressor)) {
- return AST_Seq.from_array([self.condition, make_node_from_constant(compressor, consequent.value, self)]);
- } else {
- return make_node_from_constant(compressor, consequent.value, self);
- }
- }
- // x=y?true:false --> x=!!y
- if (consequent instanceof AST_True
- && alternative instanceof AST_False) {
- self.condition = self.condition.negate(compressor);
- return make_node(AST_UnaryPrefix, self.condition, {
- operator: "!",
- expression: self.condition
- });
- }
- // x=y?false:true --> x=!y
- if (consequent instanceof AST_False
- && alternative instanceof AST_True) {
- return self.condition.negate(compressor)
- }
- return self;
- });
- OPT(AST_Boolean, function(self, compressor){
- if (compressor.option("booleans")) {
- var p = compressor.parent();
- if (p instanceof AST_Binary && (p.operator == "=="
- || p.operator == "!=")) {
- compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
- operator : p.operator,
- value : self.value,
- file : p.start.file,
- line : p.start.line,
- col : p.start.col,
- });
- return make_node(AST_Number, self, {
- value: +self.value
- });
- }
- return make_node(AST_UnaryPrefix, self, {
- operator: "!",
- expression: make_node(AST_Number, self, {
- value: 1 - self.value
- })
- });
- }
- return self;
- });
- OPT(AST_Sub, function(self, compressor){
- var prop = self.property;
- if (prop instanceof AST_String && compressor.option("properties")) {
- prop = prop.getValue();
- if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) {
- return make_node(AST_Dot, self, {
- expression : self.expression,
- property : prop
- }).optimize(compressor);
- }
- var v = parseFloat(prop);
- if (!isNaN(v) && v.toString() == prop) {
- self.property = make_node(AST_Number, self.property, {
- value: v
- });
- }
- }
- return self;
- });
- OPT(AST_Dot, function(self, compressor){
- var prop = self.property;
- if (RESERVED_WORDS(prop) && !compressor.option("screw_ie8")) {
- return make_node(AST_Sub, self, {
- expression : self.expression,
- property : make_node(AST_String, self, {
- value: prop
- })
- }).optimize(compressor);
- }
- return self.evaluate(compressor)[0];
- });
- function literals_in_boolean_context(self, compressor) {
- if (compressor.option("booleans") && compressor.in_boolean_context() && !self.has_side_effects(compressor)) {
- return make_node(AST_True, self);
- }
- return self;
- };
- OPT(AST_Array, literals_in_boolean_context);
- OPT(AST_Object, literals_in_boolean_context);
- OPT(AST_RegExp, literals_in_boolean_context);
- OPT(AST_Return, function(self, compressor){
- if (self.value instanceof AST_Undefined) {
- self.value = null;
- }
- return self;
- });
- })();
|