compress.js 175 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101
  1. /***********************************************************************
  2. A JavaScript tokenizer / parser / beautifier / compressor.
  3. https://github.com/mishoo/UglifyJS2
  4. -------------------------------- (C) ---------------------------------
  5. Author: Mihai Bazon
  6. <mihai.bazon@gmail.com>
  7. http://mihai.bazon.net/blog
  8. Distributed under the BSD license:
  9. Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
  10. Redistribution and use in source and binary forms, with or without
  11. modification, are permitted provided that the following conditions
  12. are met:
  13. * Redistributions of source code must retain the above
  14. copyright notice, this list of conditions and the following
  15. disclaimer.
  16. * Redistributions in binary form must reproduce the above
  17. copyright notice, this list of conditions and the following
  18. disclaimer in the documentation and/or other materials
  19. provided with the distribution.
  20. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
  21. EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  23. PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
  24. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  25. OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26. PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  27. PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  29. TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  30. THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. SUCH DAMAGE.
  32. ***********************************************************************/
  33. "use strict";
  34. function Compressor(options, false_by_default) {
  35. if (!(this instanceof Compressor))
  36. return new Compressor(options, false_by_default);
  37. TreeTransformer.call(this, this.before, this.after);
  38. this.options = defaults(options, {
  39. angular : false,
  40. booleans : !false_by_default,
  41. cascade : !false_by_default,
  42. collapse_vars : !false_by_default,
  43. comparisons : !false_by_default,
  44. conditionals : !false_by_default,
  45. dead_code : !false_by_default,
  46. drop_console : false,
  47. drop_debugger : !false_by_default,
  48. evaluate : !false_by_default,
  49. expression : false,
  50. global_defs : {},
  51. hoist_funs : !false_by_default,
  52. hoist_vars : false,
  53. if_return : !false_by_default,
  54. join_vars : !false_by_default,
  55. keep_fargs : true,
  56. keep_fnames : false,
  57. keep_infinity : false,
  58. loops : !false_by_default,
  59. negate_iife : !false_by_default,
  60. passes : 1,
  61. properties : !false_by_default,
  62. pure_getters : !false_by_default && "strict",
  63. pure_funcs : null,
  64. reduce_vars : !false_by_default,
  65. screw_ie8 : true,
  66. sequences : !false_by_default,
  67. side_effects : !false_by_default,
  68. switches : !false_by_default,
  69. top_retain : null,
  70. toplevel : !!(options && options["top_retain"]),
  71. unsafe : false,
  72. unsafe_comps : false,
  73. unsafe_math : false,
  74. unsafe_proto : false,
  75. unsafe_regexp : false,
  76. unused : !false_by_default,
  77. warnings : true,
  78. }, true);
  79. var pure_funcs = this.options["pure_funcs"];
  80. if (typeof pure_funcs == "function") {
  81. this.pure_funcs = pure_funcs;
  82. } else {
  83. this.pure_funcs = pure_funcs ? function(node) {
  84. return pure_funcs.indexOf(node.expression.print_to_string()) < 0;
  85. } : return_true;
  86. }
  87. var top_retain = this.options["top_retain"];
  88. if (top_retain instanceof RegExp) {
  89. this.top_retain = function(def) {
  90. return top_retain.test(def.name);
  91. };
  92. } else if (typeof top_retain == "function") {
  93. this.top_retain = top_retain;
  94. } else if (top_retain) {
  95. if (typeof top_retain == "string") {
  96. top_retain = top_retain.split(/,/);
  97. }
  98. this.top_retain = function(def) {
  99. return top_retain.indexOf(def.name) >= 0;
  100. };
  101. }
  102. var sequences = this.options["sequences"];
  103. this.sequences_limit = sequences == 1 ? 200 : sequences | 0;
  104. this.warnings_produced = {};
  105. };
  106. Compressor.prototype = new TreeTransformer;
  107. merge(Compressor.prototype, {
  108. option: function(key) { return this.options[key] },
  109. compress: function(node) {
  110. if (this.option("expression")) {
  111. node = node.process_expression(true);
  112. }
  113. var passes = +this.options.passes || 1;
  114. for (var pass = 0; pass < passes && pass < 3; ++pass) {
  115. if (pass > 0 || this.option("reduce_vars"))
  116. node.reset_opt_flags(this, true);
  117. node = node.transform(this);
  118. }
  119. if (this.option("expression")) {
  120. node = node.process_expression(false);
  121. }
  122. return node;
  123. },
  124. info: function() {
  125. if (this.options.warnings == "verbose") {
  126. AST_Node.warn.apply(AST_Node, arguments);
  127. }
  128. },
  129. warn: function(text, props) {
  130. if (this.options.warnings) {
  131. // only emit unique warnings
  132. var message = string_template(text, props);
  133. if (!(message in this.warnings_produced)) {
  134. this.warnings_produced[message] = true;
  135. AST_Node.warn.apply(AST_Node, arguments);
  136. }
  137. }
  138. },
  139. clear_warnings: function() {
  140. this.warnings_produced = {};
  141. },
  142. before: function(node, descend, in_list) {
  143. if (node._squeezed) return node;
  144. var was_scope = false;
  145. if (node instanceof AST_Scope) {
  146. node = node.hoist_declarations(this);
  147. was_scope = true;
  148. }
  149. // Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
  150. // would call AST_Node.transform() if a different instance of AST_Node is
  151. // produced after OPT().
  152. // This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
  153. // Migrate and defer all children's AST_Node.transform() to below, which
  154. // will now happen after this parent AST_Node has been properly substituted
  155. // thus gives a consistent AST snapshot.
  156. descend(node, this);
  157. // Existing code relies on how AST_Node.optimize() worked, and omitting the
  158. // following replacement call would result in degraded efficiency of both
  159. // output and performance.
  160. descend(node, this);
  161. var opt = node.optimize(this);
  162. if (was_scope && opt instanceof AST_Scope) {
  163. opt.drop_unused(this);
  164. descend(opt, this);
  165. }
  166. if (opt === node) opt._squeezed = true;
  167. return opt;
  168. }
  169. });
  170. (function(){
  171. function OPT(node, optimizer) {
  172. node.DEFMETHOD("optimize", function(compressor){
  173. var self = this;
  174. if (self._optimized) return self;
  175. if (compressor.has_directive("use asm")) return self;
  176. var opt = optimizer(self, compressor);
  177. opt._optimized = true;
  178. return opt;
  179. });
  180. };
  181. OPT(AST_Node, function(self, compressor){
  182. return self;
  183. });
  184. AST_Node.DEFMETHOD("equivalent_to", function(node){
  185. return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string();
  186. });
  187. AST_Node.DEFMETHOD("process_expression", function(insert, compressor) {
  188. var self = this;
  189. var tt = new TreeTransformer(function(node) {
  190. if (insert && node instanceof AST_SimpleStatement) {
  191. return make_node(AST_Return, node, {
  192. value: node.body
  193. });
  194. }
  195. if (!insert && node instanceof AST_Return) {
  196. if (compressor) {
  197. var value = node.value && node.value.drop_side_effect_free(compressor, true);
  198. return value ? make_node(AST_SimpleStatement, node, {
  199. body: value
  200. }) : make_node(AST_EmptyStatement, node);
  201. }
  202. return make_node(AST_SimpleStatement, node, {
  203. body: node.value || make_node(AST_UnaryPrefix, node, {
  204. operator: "void",
  205. expression: make_node(AST_Number, node, {
  206. value: 0
  207. })
  208. })
  209. });
  210. }
  211. if (node instanceof AST_Lambda && node !== self) {
  212. return node;
  213. }
  214. if (node instanceof AST_Block) {
  215. var index = node.body.length - 1;
  216. if (index >= 0) {
  217. node.body[index] = node.body[index].transform(tt);
  218. }
  219. }
  220. if (node instanceof AST_If) {
  221. node.body = node.body.transform(tt);
  222. if (node.alternative) {
  223. node.alternative = node.alternative.transform(tt);
  224. }
  225. }
  226. if (node instanceof AST_With) {
  227. node.body = node.body.transform(tt);
  228. }
  229. return node;
  230. });
  231. return self.transform(tt);
  232. });
  233. AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
  234. var reduce_vars = rescan && compressor.option("reduce_vars");
  235. var toplevel = compressor.option("toplevel");
  236. var safe_ids = Object.create(null);
  237. var suppressor = new TreeWalker(function(node) {
  238. if (node instanceof AST_Symbol) {
  239. var d = node.definition();
  240. if (node instanceof AST_SymbolRef) d.references.push(node);
  241. d.fixed = false;
  242. }
  243. });
  244. var tw = new TreeWalker(function(node, descend){
  245. node._squeezed = false;
  246. node._optimized = false;
  247. if (reduce_vars) {
  248. if (node instanceof AST_Toplevel) node.globals.each(reset_def);
  249. if (node instanceof AST_Scope) node.variables.each(reset_def);
  250. if (node instanceof AST_SymbolRef) {
  251. var d = node.definition();
  252. d.references.push(node);
  253. if (d.fixed === undefined || !is_safe(d)
  254. || is_modified(node, 0, node.fixed_value() instanceof AST_Lambda)) {
  255. d.fixed = false;
  256. } else {
  257. var parent = tw.parent();
  258. if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
  259. || parent instanceof AST_Call && node !== parent.expression
  260. || parent instanceof AST_Return && node === parent.value && node.scope !== d.scope
  261. || parent instanceof AST_VarDef && node === parent.value) {
  262. d.escaped = true;
  263. }
  264. }
  265. }
  266. if (node instanceof AST_SymbolCatch) {
  267. node.definition().fixed = false;
  268. }
  269. if (node instanceof AST_VarDef) {
  270. var d = node.name.definition();
  271. if (d.fixed == null) {
  272. if (node.value) {
  273. d.fixed = function() {
  274. return node.value;
  275. };
  276. mark(d, false);
  277. descend();
  278. } else {
  279. d.fixed = null;
  280. }
  281. mark(d, true);
  282. return true;
  283. } else if (node.value) {
  284. d.fixed = false;
  285. }
  286. }
  287. if (node instanceof AST_Defun) {
  288. var d = node.name.definition();
  289. if (!toplevel && d.global || is_safe(d)) {
  290. d.fixed = false;
  291. } else {
  292. d.fixed = node;
  293. mark(d, true);
  294. }
  295. var save_ids = safe_ids;
  296. safe_ids = Object.create(null);
  297. descend();
  298. safe_ids = save_ids;
  299. return true;
  300. }
  301. if (node instanceof AST_Function) {
  302. push();
  303. var iife;
  304. if (!node.name
  305. && (iife = tw.parent()) instanceof AST_Call
  306. && iife.expression === node) {
  307. // Virtually turn IIFE parameters into variable definitions:
  308. // (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
  309. // So existing transformation rules can work on them.
  310. node.argnames.forEach(function(arg, i) {
  311. var d = arg.definition();
  312. if (!node.uses_arguments && d.fixed === undefined) {
  313. d.fixed = function() {
  314. return iife.args[i] || make_node(AST_Undefined, iife);
  315. };
  316. mark(d, true);
  317. } else {
  318. d.fixed = false;
  319. }
  320. });
  321. }
  322. descend();
  323. pop();
  324. return true;
  325. }
  326. if (node instanceof AST_Accessor) {
  327. var save_ids = safe_ids;
  328. safe_ids = Object.create(null);
  329. descend();
  330. safe_ids = save_ids;
  331. return true;
  332. }
  333. if (node instanceof AST_Binary
  334. && (node.operator == "&&" || node.operator == "||")) {
  335. node.left.walk(tw);
  336. push();
  337. node.right.walk(tw);
  338. pop();
  339. return true;
  340. }
  341. if (node instanceof AST_Conditional) {
  342. node.condition.walk(tw);
  343. push();
  344. node.consequent.walk(tw);
  345. pop();
  346. push();
  347. node.alternative.walk(tw);
  348. pop();
  349. return true;
  350. }
  351. if (node instanceof AST_If || node instanceof AST_DWLoop) {
  352. node.condition.walk(tw);
  353. push();
  354. node.body.walk(tw);
  355. pop();
  356. if (node.alternative) {
  357. push();
  358. node.alternative.walk(tw);
  359. pop();
  360. }
  361. return true;
  362. }
  363. if (node instanceof AST_LabeledStatement) {
  364. push();
  365. node.body.walk(tw);
  366. pop();
  367. return true;
  368. }
  369. if (node instanceof AST_For) {
  370. if (node.init) node.init.walk(tw);
  371. push();
  372. if (node.condition) node.condition.walk(tw);
  373. node.body.walk(tw);
  374. if (node.step) node.step.walk(tw);
  375. pop();
  376. return true;
  377. }
  378. if (node instanceof AST_ForIn) {
  379. node.init.walk(suppressor);
  380. node.object.walk(tw);
  381. push();
  382. node.body.walk(tw);
  383. pop();
  384. return true;
  385. }
  386. if (node instanceof AST_Try) {
  387. push();
  388. walk_body(node, tw);
  389. pop();
  390. if (node.bcatch) {
  391. push();
  392. node.bcatch.walk(tw);
  393. pop();
  394. }
  395. if (node.bfinally) node.bfinally.walk(tw);
  396. return true;
  397. }
  398. if (node instanceof AST_SwitchBranch) {
  399. push();
  400. descend();
  401. pop();
  402. return true;
  403. }
  404. }
  405. });
  406. this.walk(tw);
  407. function mark(def, safe) {
  408. safe_ids[def.id] = safe;
  409. }
  410. function is_safe(def) {
  411. if (safe_ids[def.id]) {
  412. if (def.fixed == null) {
  413. var orig = def.orig[0];
  414. if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false;
  415. def.fixed = make_node(AST_Undefined, orig);
  416. }
  417. return true;
  418. }
  419. }
  420. function push() {
  421. safe_ids = Object.create(safe_ids);
  422. }
  423. function pop() {
  424. safe_ids = Object.getPrototypeOf(safe_ids);
  425. }
  426. function reset_def(def) {
  427. def.escaped = false;
  428. if (def.scope.uses_eval) {
  429. def.fixed = false;
  430. } else if (toplevel || !def.global || def.orig[0] instanceof AST_SymbolConst) {
  431. def.fixed = undefined;
  432. } else {
  433. def.fixed = false;
  434. }
  435. def.references = [];
  436. def.should_replace = undefined;
  437. }
  438. function is_modified(node, level, func) {
  439. var parent = tw.parent(level);
  440. if (is_lhs(node, parent)
  441. || !func && parent instanceof AST_Call && parent.expression === node) {
  442. return true;
  443. } else if (parent instanceof AST_PropAccess && parent.expression === node) {
  444. return !func && is_modified(parent, level + 1);
  445. }
  446. }
  447. });
  448. AST_SymbolRef.DEFMETHOD("fixed_value", function() {
  449. var fixed = this.definition().fixed;
  450. if (!fixed || fixed instanceof AST_Node) return fixed;
  451. return fixed();
  452. });
  453. function is_reference_const(ref) {
  454. if (!(ref instanceof AST_SymbolRef)) return false;
  455. var orig = ref.definition().orig;
  456. for (var i = orig.length; --i >= 0;) {
  457. if (orig[i] instanceof AST_SymbolConst) return true;
  458. }
  459. }
  460. function find_variable(compressor, name) {
  461. var scope, i = 0;
  462. while (scope = compressor.parent(i++)) {
  463. if (scope instanceof AST_Scope) break;
  464. if (scope instanceof AST_Catch) {
  465. scope = scope.argname.definition().scope;
  466. break;
  467. }
  468. }
  469. return scope.find_variable(name);
  470. }
  471. function make_node(ctor, orig, props) {
  472. if (!props) props = {};
  473. if (orig) {
  474. if (!props.start) props.start = orig.start;
  475. if (!props.end) props.end = orig.end;
  476. }
  477. return new ctor(props);
  478. };
  479. function make_node_from_constant(val, orig) {
  480. switch (typeof val) {
  481. case "string":
  482. return make_node(AST_String, orig, {
  483. value: val
  484. });
  485. case "number":
  486. if (isNaN(val)) return make_node(AST_NaN, orig);
  487. if (isFinite(val)) {
  488. return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
  489. operator: "-",
  490. expression: make_node(AST_Number, orig, { value: -val })
  491. }) : make_node(AST_Number, orig, { value: val });
  492. }
  493. return val < 0 ? make_node(AST_UnaryPrefix, orig, {
  494. operator: "-",
  495. expression: make_node(AST_Infinity, orig)
  496. }) : make_node(AST_Infinity, orig);
  497. case "boolean":
  498. return make_node(val ? AST_True : AST_False, orig);
  499. case "undefined":
  500. return make_node(AST_Undefined, orig);
  501. default:
  502. if (val === null) {
  503. return make_node(AST_Null, orig, { value: null });
  504. }
  505. if (val instanceof RegExp) {
  506. return make_node(AST_RegExp, orig, { value: val });
  507. }
  508. throw new Error(string_template("Can't handle constant of type: {type}", {
  509. type: typeof val
  510. }));
  511. }
  512. };
  513. // we shouldn't compress (1,func)(something) to
  514. // func(something) because that changes the meaning of
  515. // the func (becomes lexical instead of global).
  516. function maintain_this_binding(parent, orig, val) {
  517. if (parent instanceof AST_UnaryPrefix && parent.operator == "delete"
  518. || parent instanceof AST_Call && parent.expression === orig
  519. && (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name == "eval")) {
  520. return make_node(AST_Seq, orig, {
  521. car: make_node(AST_Number, orig, {
  522. value: 0
  523. }),
  524. cdr: val
  525. });
  526. }
  527. return val;
  528. }
  529. function as_statement_array(thing) {
  530. if (thing === null) return [];
  531. if (thing instanceof AST_BlockStatement) return thing.body;
  532. if (thing instanceof AST_EmptyStatement) return [];
  533. if (thing instanceof AST_Statement) return [ thing ];
  534. throw new Error("Can't convert thing to statement array");
  535. };
  536. function is_empty(thing) {
  537. if (thing === null) return true;
  538. if (thing instanceof AST_EmptyStatement) return true;
  539. if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
  540. return false;
  541. };
  542. function loop_body(x) {
  543. if (x instanceof AST_Switch) return x;
  544. if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
  545. return (x.body instanceof AST_BlockStatement ? x.body : x);
  546. }
  547. return x;
  548. };
  549. function is_iife_call(node) {
  550. if (node instanceof AST_Call && !(node instanceof AST_New)) {
  551. return node.expression instanceof AST_Function || is_iife_call(node.expression);
  552. }
  553. return false;
  554. }
  555. function tighten_body(statements, compressor) {
  556. var CHANGED, max_iter = 10;
  557. do {
  558. CHANGED = false;
  559. if (compressor.option("angular")) {
  560. statements = process_for_angular(statements);
  561. }
  562. statements = eliminate_spurious_blocks(statements);
  563. if (compressor.option("dead_code")) {
  564. statements = eliminate_dead_code(statements, compressor);
  565. }
  566. if (compressor.option("if_return")) {
  567. statements = handle_if_return(statements, compressor);
  568. }
  569. if (compressor.sequences_limit > 0) {
  570. statements = sequencesize(statements, compressor);
  571. }
  572. if (compressor.option("join_vars")) {
  573. statements = join_consecutive_vars(statements, compressor);
  574. }
  575. if (compressor.option("collapse_vars")) {
  576. statements = collapse_single_use_vars(statements, compressor);
  577. }
  578. } while (CHANGED && max_iter-- > 0);
  579. return statements;
  580. function collapse_single_use_vars(statements, compressor) {
  581. // Iterate statements backwards looking for a statement with a var/const
  582. // declaration immediately preceding it. Grab the rightmost var definition
  583. // and if it has exactly one reference then attempt to replace its reference
  584. // in the statement with the var value and then erase the var definition.
  585. var self = compressor.self();
  586. var var_defs_removed = false;
  587. var toplevel = compressor.option("toplevel");
  588. for (var stat_index = statements.length; --stat_index >= 0;) {
  589. var stat = statements[stat_index];
  590. if (stat instanceof AST_Definitions) continue;
  591. // Process child blocks of statement if present.
  592. [stat, stat.body, stat.alternative, stat.bcatch, stat.bfinally].forEach(function(node) {
  593. node && node.body && collapse_single_use_vars(node.body, compressor);
  594. });
  595. // The variable definition must precede a statement.
  596. if (stat_index <= 0) break;
  597. var prev_stat_index = stat_index - 1;
  598. var prev_stat = statements[prev_stat_index];
  599. if (!(prev_stat instanceof AST_Definitions)) continue;
  600. var var_defs = prev_stat.definitions;
  601. if (var_defs == null) continue;
  602. var var_names_seen = {};
  603. var side_effects_encountered = false;
  604. var lvalues_encountered = false;
  605. var lvalues = {};
  606. // Scan variable definitions from right to left.
  607. for (var var_defs_index = var_defs.length; --var_defs_index >= 0;) {
  608. // Obtain var declaration and var name with basic sanity check.
  609. var var_decl = var_defs[var_defs_index];
  610. if (var_decl.value == null) break;
  611. var var_name = var_decl.name.name;
  612. if (!var_name || !var_name.length) break;
  613. // Bail if we've seen a var definition of same name before.
  614. if (var_name in var_names_seen) break;
  615. var_names_seen[var_name] = true;
  616. // Only interested in cases with just one reference to the variable.
  617. var def = self.find_variable && self.find_variable(var_name);
  618. if (!def || !def.references || def.references.length !== 1
  619. || var_name == "arguments" || (!toplevel && def.global)) {
  620. side_effects_encountered = true;
  621. continue;
  622. }
  623. var ref = def.references[0];
  624. // Don't replace ref if eval() or with statement in scope.
  625. if (ref.scope.uses_eval || ref.scope.uses_with) break;
  626. // Constant single use vars can be replaced in any scope.
  627. if (var_decl.value.is_constant()) {
  628. var ctt = new TreeTransformer(function(node) {
  629. var parent = ctt.parent();
  630. if (parent instanceof AST_IterationStatement
  631. && (parent.condition === node || parent.init === node)) {
  632. return node;
  633. }
  634. if (node === ref)
  635. return replace_var(node, parent, true);
  636. });
  637. stat.transform(ctt);
  638. continue;
  639. }
  640. // Restrict var replacement to constants if side effects encountered.
  641. if (side_effects_encountered |= lvalues_encountered) continue;
  642. var value_has_side_effects = var_decl.value.has_side_effects(compressor);
  643. // Non-constant single use vars can only be replaced in same scope.
  644. if (ref.scope !== self) {
  645. side_effects_encountered |= value_has_side_effects;
  646. continue;
  647. }
  648. // Detect lvalues in var value.
  649. var tw = new TreeWalker(function(node){
  650. if (node instanceof AST_SymbolRef && is_lvalue(node, tw.parent())) {
  651. lvalues[node.name] = lvalues_encountered = true;
  652. }
  653. });
  654. var_decl.value.walk(tw);
  655. // Replace the non-constant single use var in statement if side effect free.
  656. var unwind = false;
  657. var tt = new TreeTransformer(
  658. function preorder(node) {
  659. if (unwind) return node;
  660. var parent = tt.parent();
  661. if (node instanceof AST_Lambda
  662. || node instanceof AST_Try
  663. || node instanceof AST_With
  664. || node instanceof AST_Case
  665. || node instanceof AST_IterationStatement
  666. || (parent instanceof AST_If && node !== parent.condition)
  667. || (parent instanceof AST_Conditional && node !== parent.condition)
  668. || (node instanceof AST_SymbolRef
  669. && value_has_side_effects
  670. && !are_references_in_scope(node.definition(), self))
  671. || (parent instanceof AST_Binary
  672. && (parent.operator == "&&" || parent.operator == "||")
  673. && node === parent.right)
  674. || (parent instanceof AST_Switch && node !== parent.expression)) {
  675. return side_effects_encountered = unwind = true, node;
  676. }
  677. function are_references_in_scope(def, scope) {
  678. if (def.orig.length === 1
  679. && def.orig[0] instanceof AST_SymbolDefun) return true;
  680. if (def.scope !== scope) return false;
  681. var refs = def.references;
  682. for (var i = 0, len = refs.length; i < len; i++) {
  683. if (refs[i].scope !== scope) return false;
  684. }
  685. return true;
  686. }
  687. },
  688. function postorder(node) {
  689. if (unwind) return node;
  690. if (node === ref)
  691. return unwind = true, replace_var(node, tt.parent(), false);
  692. if (side_effects_encountered |= node.has_side_effects(compressor))
  693. return unwind = true, node;
  694. if (lvalues_encountered && node instanceof AST_SymbolRef && node.name in lvalues) {
  695. side_effects_encountered = true;
  696. return unwind = true, node;
  697. }
  698. }
  699. );
  700. stat.transform(tt);
  701. }
  702. }
  703. // Remove extraneous empty statments in block after removing var definitions.
  704. // Leave at least one statement in `statements`.
  705. if (var_defs_removed) for (var i = statements.length; --i >= 0;) {
  706. if (statements.length > 1 && statements[i] instanceof AST_EmptyStatement)
  707. statements.splice(i, 1);
  708. }
  709. return statements;
  710. function is_lvalue(node, parent) {
  711. return node instanceof AST_SymbolRef && is_lhs(node, parent);
  712. }
  713. function replace_var(node, parent, is_constant) {
  714. if (is_lvalue(node, parent)) return node;
  715. // Remove var definition and return its value to the TreeTransformer to replace.
  716. var value = maintain_this_binding(parent, node, var_decl.value);
  717. var_decl.value = null;
  718. var_defs.splice(var_defs_index, 1);
  719. if (var_defs.length === 0) {
  720. statements[prev_stat_index] = make_node(AST_EmptyStatement, self);
  721. var_defs_removed = true;
  722. }
  723. // Further optimize statement after substitution.
  724. stat.reset_opt_flags(compressor);
  725. compressor.info("Collapsing " + (is_constant ? "constant" : "variable") +
  726. " " + var_name + " [{file}:{line},{col}]", node.start);
  727. CHANGED = true;
  728. return value;
  729. }
  730. }
  731. function process_for_angular(statements) {
  732. function has_inject(comment) {
  733. return /@ngInject/.test(comment.value);
  734. }
  735. function make_arguments_names_list(func) {
  736. return func.argnames.map(function(sym){
  737. return make_node(AST_String, sym, { value: sym.name });
  738. });
  739. }
  740. function make_array(orig, elements) {
  741. return make_node(AST_Array, orig, { elements: elements });
  742. }
  743. function make_injector(func, name) {
  744. return make_node(AST_SimpleStatement, func, {
  745. body: make_node(AST_Assign, func, {
  746. operator: "=",
  747. left: make_node(AST_Dot, name, {
  748. expression: make_node(AST_SymbolRef, name, name),
  749. property: "$inject"
  750. }),
  751. right: make_array(func, make_arguments_names_list(func))
  752. })
  753. });
  754. }
  755. function check_expression(body) {
  756. if (body && body.args) {
  757. // if this is a function call check all of arguments passed
  758. body.args.forEach(function(argument, index, array) {
  759. var comments = argument.start.comments_before;
  760. // if the argument is function preceded by @ngInject
  761. if (argument instanceof AST_Lambda && comments.length && has_inject(comments[0])) {
  762. // replace the function with an array of names of its parameters and function at the end
  763. array[index] = make_array(argument, make_arguments_names_list(argument).concat(argument));
  764. }
  765. });
  766. // if this is chained call check previous one recursively
  767. if (body.expression && body.expression.expression) {
  768. check_expression(body.expression.expression);
  769. }
  770. }
  771. }
  772. return statements.reduce(function(a, stat){
  773. a.push(stat);
  774. if (stat.body && stat.body.args) {
  775. check_expression(stat.body);
  776. } else {
  777. var token = stat.start;
  778. var comments = token.comments_before;
  779. if (comments && comments.length > 0) {
  780. var last = comments.pop();
  781. if (has_inject(last)) {
  782. // case 1: defun
  783. if (stat instanceof AST_Defun) {
  784. a.push(make_injector(stat, stat.name));
  785. }
  786. else if (stat instanceof AST_Definitions) {
  787. stat.definitions.forEach(function(def) {
  788. if (def.value && def.value instanceof AST_Lambda) {
  789. a.push(make_injector(def.value, def.name));
  790. }
  791. });
  792. }
  793. else {
  794. compressor.warn("Unknown statement marked with @ngInject [{file}:{line},{col}]", token);
  795. }
  796. }
  797. }
  798. }
  799. return a;
  800. }, []);
  801. }
  802. function eliminate_spurious_blocks(statements) {
  803. var seen_dirs = [];
  804. return statements.reduce(function(a, stat){
  805. if (stat instanceof AST_BlockStatement) {
  806. CHANGED = true;
  807. a.push.apply(a, eliminate_spurious_blocks(stat.body));
  808. } else if (stat instanceof AST_EmptyStatement) {
  809. CHANGED = true;
  810. } else if (stat instanceof AST_Directive) {
  811. if (seen_dirs.indexOf(stat.value) < 0) {
  812. a.push(stat);
  813. seen_dirs.push(stat.value);
  814. } else {
  815. CHANGED = true;
  816. }
  817. } else {
  818. a.push(stat);
  819. }
  820. return a;
  821. }, []);
  822. };
  823. function handle_if_return(statements, compressor) {
  824. var self = compressor.self();
  825. var multiple_if_returns = has_multiple_if_returns(statements);
  826. var in_lambda = self instanceof AST_Lambda;
  827. var ret = []; // Optimized statements, build from tail to front
  828. loop: for (var i = statements.length; --i >= 0;) {
  829. var stat = statements[i];
  830. switch (true) {
  831. case (in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0):
  832. CHANGED = true;
  833. // note, ret.length is probably always zero
  834. // because we drop unreachable code before this
  835. // step. nevertheless, it's good to check.
  836. continue loop;
  837. case stat instanceof AST_If:
  838. if (stat.body instanceof AST_Return) {
  839. //---
  840. // pretty silly case, but:
  841. // if (foo()) return; return; ==> foo(); return;
  842. if (((in_lambda && ret.length == 0)
  843. || (ret[0] instanceof AST_Return && !ret[0].value))
  844. && !stat.body.value && !stat.alternative) {
  845. CHANGED = true;
  846. var cond = make_node(AST_SimpleStatement, stat.condition, {
  847. body: stat.condition
  848. });
  849. ret.unshift(cond);
  850. continue loop;
  851. }
  852. //---
  853. // if (foo()) return x; return y; ==> return foo() ? x : y;
  854. if (ret[0] instanceof AST_Return && stat.body.value && ret[0].value && !stat.alternative) {
  855. CHANGED = true;
  856. stat = stat.clone();
  857. stat.alternative = ret[0];
  858. ret[0] = stat.transform(compressor);
  859. continue loop;
  860. }
  861. //---
  862. // if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
  863. if (multiple_if_returns && (ret.length == 0 || ret[0] instanceof AST_Return)
  864. && stat.body.value && !stat.alternative && in_lambda) {
  865. CHANGED = true;
  866. stat = stat.clone();
  867. stat.alternative = ret[0] || make_node(AST_Return, stat, {
  868. value: null
  869. });
  870. ret[0] = stat.transform(compressor);
  871. continue loop;
  872. }
  873. //---
  874. // if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
  875. if (!stat.body.value && in_lambda) {
  876. CHANGED = true;
  877. stat = stat.clone();
  878. stat.condition = stat.condition.negate(compressor);
  879. var body = as_statement_array(stat.alternative).concat(ret);
  880. var funs = extract_functions_from_statement_array(body);
  881. stat.body = make_node(AST_BlockStatement, stat, {
  882. body: body
  883. });
  884. stat.alternative = null;
  885. ret = funs.concat([ stat.transform(compressor) ]);
  886. continue loop;
  887. }
  888. //---
  889. // if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
  890. //
  891. // if sequences is not enabled, this can lead to an endless loop (issue #866).
  892. // however, with sequences on this helps producing slightly better output for
  893. // the example code.
  894. if (compressor.option("sequences")
  895. && i > 0 && statements[i - 1] instanceof AST_If && statements[i - 1].body instanceof AST_Return
  896. && ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
  897. && !stat.alternative) {
  898. CHANGED = true;
  899. ret.push(make_node(AST_Return, ret[0], {
  900. value: null
  901. }).transform(compressor));
  902. ret.unshift(stat);
  903. continue loop;
  904. }
  905. }
  906. var ab = aborts(stat.body);
  907. var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
  908. if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
  909. || (ab instanceof AST_Continue && self === loop_body(lct))
  910. || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
  911. if (ab.label) {
  912. remove(ab.label.thedef.references, ab);
  913. }
  914. CHANGED = true;
  915. var body = as_statement_array(stat.body).slice(0, -1);
  916. stat = stat.clone();
  917. stat.condition = stat.condition.negate(compressor);
  918. stat.body = make_node(AST_BlockStatement, stat, {
  919. body: as_statement_array(stat.alternative).concat(ret)
  920. });
  921. stat.alternative = make_node(AST_BlockStatement, stat, {
  922. body: body
  923. });
  924. ret = [ stat.transform(compressor) ];
  925. continue loop;
  926. }
  927. var ab = aborts(stat.alternative);
  928. var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
  929. if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
  930. || (ab instanceof AST_Continue && self === loop_body(lct))
  931. || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
  932. if (ab.label) {
  933. remove(ab.label.thedef.references, ab);
  934. }
  935. CHANGED = true;
  936. stat = stat.clone();
  937. stat.body = make_node(AST_BlockStatement, stat.body, {
  938. body: as_statement_array(stat.body).concat(ret)
  939. });
  940. stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
  941. body: as_statement_array(stat.alternative).slice(0, -1)
  942. });
  943. ret = [ stat.transform(compressor) ];
  944. continue loop;
  945. }
  946. ret.unshift(stat);
  947. break;
  948. default:
  949. ret.unshift(stat);
  950. break;
  951. }
  952. }
  953. return ret;
  954. function has_multiple_if_returns(statements) {
  955. var n = 0;
  956. for (var i = statements.length; --i >= 0;) {
  957. var stat = statements[i];
  958. if (stat instanceof AST_If && stat.body instanceof AST_Return) {
  959. if (++n > 1) return true;
  960. }
  961. }
  962. return false;
  963. }
  964. };
  965. function eliminate_dead_code(statements, compressor) {
  966. var has_quit = false;
  967. var orig = statements.length;
  968. var self = compressor.self();
  969. statements = statements.reduce(function(a, stat){
  970. if (has_quit) {
  971. extract_declarations_from_unreachable_code(compressor, stat, a);
  972. } else {
  973. if (stat instanceof AST_LoopControl) {
  974. var lct = compressor.loopcontrol_target(stat);
  975. if ((stat instanceof AST_Break
  976. && !(lct instanceof AST_IterationStatement)
  977. && loop_body(lct) === self) || (stat instanceof AST_Continue
  978. && loop_body(lct) === self)) {
  979. if (stat.label) {
  980. remove(stat.label.thedef.references, stat);
  981. }
  982. } else {
  983. a.push(stat);
  984. }
  985. } else {
  986. a.push(stat);
  987. }
  988. if (aborts(stat)) has_quit = true;
  989. }
  990. return a;
  991. }, []);
  992. CHANGED = statements.length != orig;
  993. return statements;
  994. };
  995. function sequencesize(statements, compressor) {
  996. if (statements.length < 2) return statements;
  997. var seq = [], ret = [];
  998. function push_seq() {
  999. seq = AST_Seq.from_array(seq);
  1000. if (seq) ret.push(make_node(AST_SimpleStatement, seq, {
  1001. body: seq
  1002. }));
  1003. seq = [];
  1004. };
  1005. statements.forEach(function(stat){
  1006. if (stat instanceof AST_SimpleStatement) {
  1007. if (seqLength(seq) >= compressor.sequences_limit) push_seq();
  1008. var body = stat.body;
  1009. if (seq.length > 0) body = body.drop_side_effect_free(compressor);
  1010. if (body) seq.push(body);
  1011. } else {
  1012. push_seq();
  1013. ret.push(stat);
  1014. }
  1015. });
  1016. push_seq();
  1017. ret = sequencesize_2(ret, compressor);
  1018. CHANGED = ret.length != statements.length;
  1019. return ret;
  1020. };
  1021. function seqLength(a) {
  1022. for (var len = 0, i = 0; i < a.length; ++i) {
  1023. var stat = a[i];
  1024. if (stat instanceof AST_Seq) {
  1025. len += stat.len();
  1026. } else {
  1027. len++;
  1028. }
  1029. }
  1030. return len;
  1031. };
  1032. function sequencesize_2(statements, compressor) {
  1033. function cons_seq(right) {
  1034. ret.pop();
  1035. var left = prev.body;
  1036. if (left instanceof AST_Seq) {
  1037. left.add(right);
  1038. } else {
  1039. left = AST_Seq.cons(left, right);
  1040. }
  1041. return left.transform(compressor);
  1042. };
  1043. var ret = [], prev = null;
  1044. statements.forEach(function(stat){
  1045. if (prev) {
  1046. if (stat instanceof AST_For) {
  1047. var opera = {};
  1048. try {
  1049. prev.body.walk(new TreeWalker(function(node){
  1050. if (node instanceof AST_Binary && node.operator == "in")
  1051. throw opera;
  1052. }));
  1053. if (stat.init && !(stat.init instanceof AST_Definitions)) {
  1054. stat.init = cons_seq(stat.init);
  1055. }
  1056. else if (!stat.init) {
  1057. stat.init = prev.body.drop_side_effect_free(compressor);
  1058. ret.pop();
  1059. }
  1060. } catch(ex) {
  1061. if (ex !== opera) throw ex;
  1062. }
  1063. }
  1064. else if (stat instanceof AST_If) {
  1065. stat.condition = cons_seq(stat.condition);
  1066. }
  1067. else if (stat instanceof AST_With) {
  1068. stat.expression = cons_seq(stat.expression);
  1069. }
  1070. else if (stat instanceof AST_Exit && stat.value) {
  1071. stat.value = cons_seq(stat.value);
  1072. }
  1073. else if (stat instanceof AST_Exit) {
  1074. stat.value = cons_seq(make_node(AST_Undefined, stat).transform(compressor));
  1075. }
  1076. else if (stat instanceof AST_Switch) {
  1077. stat.expression = cons_seq(stat.expression);
  1078. }
  1079. }
  1080. ret.push(stat);
  1081. prev = stat instanceof AST_SimpleStatement ? stat : null;
  1082. });
  1083. return ret;
  1084. };
  1085. function join_consecutive_vars(statements, compressor) {
  1086. var prev = null;
  1087. return statements.reduce(function(a, stat){
  1088. if (stat instanceof AST_Definitions && prev && prev.TYPE == stat.TYPE) {
  1089. prev.definitions = prev.definitions.concat(stat.definitions);
  1090. CHANGED = true;
  1091. }
  1092. else if (stat instanceof AST_For
  1093. && prev instanceof AST_Var
  1094. && (!stat.init || stat.init.TYPE == prev.TYPE)) {
  1095. CHANGED = true;
  1096. a.pop();
  1097. if (stat.init) {
  1098. stat.init.definitions = prev.definitions.concat(stat.init.definitions);
  1099. } else {
  1100. stat.init = prev;
  1101. }
  1102. a.push(stat);
  1103. prev = stat;
  1104. }
  1105. else {
  1106. prev = stat;
  1107. a.push(stat);
  1108. }
  1109. return a;
  1110. }, []);
  1111. };
  1112. };
  1113. function extract_functions_from_statement_array(statements) {
  1114. var funs = [];
  1115. for (var i = statements.length - 1; i >= 0; --i) {
  1116. var stat = statements[i];
  1117. if (stat instanceof AST_Defun) {
  1118. statements.splice(i, 1);
  1119. funs.unshift(stat);
  1120. }
  1121. }
  1122. return funs;
  1123. }
  1124. function extract_declarations_from_unreachable_code(compressor, stat, target) {
  1125. if (!(stat instanceof AST_Defun)) {
  1126. compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
  1127. }
  1128. stat.walk(new TreeWalker(function(node){
  1129. if (node instanceof AST_Definitions) {
  1130. compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
  1131. node.remove_initializers();
  1132. target.push(node);
  1133. return true;
  1134. }
  1135. if (node instanceof AST_Defun) {
  1136. target.push(node);
  1137. return true;
  1138. }
  1139. if (node instanceof AST_Scope) {
  1140. return true;
  1141. }
  1142. }));
  1143. };
  1144. function is_undefined(node, compressor) {
  1145. return node.is_undefined
  1146. || node instanceof AST_Undefined
  1147. || node instanceof AST_UnaryPrefix
  1148. && node.operator == "void"
  1149. && !node.expression.has_side_effects(compressor);
  1150. }
  1151. // may_throw_on_access()
  1152. // returns true if this node may be null, undefined or contain `AST_Accessor`
  1153. (function(def) {
  1154. AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
  1155. var pure_getters = compressor.option("pure_getters");
  1156. return !pure_getters || this._throw_on_access(pure_getters);
  1157. });
  1158. function is_strict(pure_getters) {
  1159. return /strict/.test(pure_getters);
  1160. }
  1161. def(AST_Node, is_strict);
  1162. def(AST_Null, return_true);
  1163. def(AST_Undefined, return_true);
  1164. def(AST_Constant, return_false);
  1165. def(AST_Array, return_false);
  1166. def(AST_Object, function(pure_getters) {
  1167. if (!is_strict(pure_getters)) return false;
  1168. for (var i = this.properties.length; --i >=0;)
  1169. if (this.properties[i].value instanceof AST_Accessor) return true;
  1170. return false;
  1171. });
  1172. def(AST_Function, return_false);
  1173. def(AST_UnaryPostfix, return_false);
  1174. def(AST_UnaryPrefix, function() {
  1175. return this.operator == "void";
  1176. });
  1177. def(AST_Binary, function(pure_getters) {
  1178. switch (this.operator) {
  1179. case "&&":
  1180. return this.left._throw_on_access(pure_getters);
  1181. case "||":
  1182. return this.left._throw_on_access(pure_getters)
  1183. && this.right._throw_on_access(pure_getters);
  1184. default:
  1185. return false;
  1186. }
  1187. })
  1188. def(AST_Assign, function(pure_getters) {
  1189. return this.operator == "="
  1190. && this.right._throw_on_access(pure_getters);
  1191. })
  1192. def(AST_Conditional, function(pure_getters) {
  1193. return this.consequent._throw_on_access(pure_getters)
  1194. || this.alternative._throw_on_access(pure_getters);
  1195. })
  1196. def(AST_Seq, function(pure_getters) {
  1197. return this.cdr._throw_on_access(pure_getters);
  1198. });
  1199. def(AST_SymbolRef, function(pure_getters) {
  1200. if (this.is_undefined) return true;
  1201. if (!is_strict(pure_getters)) return false;
  1202. var fixed = this.fixed_value();
  1203. return !fixed || fixed._throw_on_access(pure_getters);
  1204. });
  1205. })(function(node, func) {
  1206. node.DEFMETHOD("_throw_on_access", func);
  1207. });
  1208. /* -----[ boolean/negation helpers ]----- */
  1209. // methods to determine whether an expression has a boolean result type
  1210. (function (def){
  1211. var unary_bool = [ "!", "delete" ];
  1212. var binary_bool = [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ];
  1213. def(AST_Node, return_false);
  1214. def(AST_UnaryPrefix, function(){
  1215. return member(this.operator, unary_bool);
  1216. });
  1217. def(AST_Binary, function(){
  1218. return member(this.operator, binary_bool) ||
  1219. ( (this.operator == "&&" || this.operator == "||") &&
  1220. this.left.is_boolean() && this.right.is_boolean() );
  1221. });
  1222. def(AST_Conditional, function(){
  1223. return this.consequent.is_boolean() && this.alternative.is_boolean();
  1224. });
  1225. def(AST_Assign, function(){
  1226. return this.operator == "=" && this.right.is_boolean();
  1227. });
  1228. def(AST_Seq, function(){
  1229. return this.cdr.is_boolean();
  1230. });
  1231. def(AST_True, return_true);
  1232. def(AST_False, return_true);
  1233. })(function(node, func){
  1234. node.DEFMETHOD("is_boolean", func);
  1235. });
  1236. // methods to determine if an expression has a numeric result type
  1237. (function (def){
  1238. def(AST_Node, return_false);
  1239. def(AST_Number, return_true);
  1240. var unary = makePredicate("+ - ~ ++ --");
  1241. def(AST_Unary, function(){
  1242. return unary(this.operator);
  1243. });
  1244. var binary = makePredicate("- * / % & | ^ << >> >>>");
  1245. def(AST_Binary, function(compressor){
  1246. return binary(this.operator) || this.operator == "+"
  1247. && this.left.is_number(compressor)
  1248. && this.right.is_number(compressor);
  1249. });
  1250. def(AST_Assign, function(compressor){
  1251. return binary(this.operator.slice(0, -1))
  1252. || this.operator == "=" && this.right.is_number(compressor);
  1253. });
  1254. def(AST_Seq, function(compressor){
  1255. return this.cdr.is_number(compressor);
  1256. });
  1257. def(AST_Conditional, function(compressor){
  1258. return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
  1259. });
  1260. })(function(node, func){
  1261. node.DEFMETHOD("is_number", func);
  1262. });
  1263. // methods to determine if an expression has a string result type
  1264. (function (def){
  1265. def(AST_Node, return_false);
  1266. def(AST_String, return_true);
  1267. def(AST_UnaryPrefix, function(){
  1268. return this.operator == "typeof";
  1269. });
  1270. def(AST_Binary, function(compressor){
  1271. return this.operator == "+" &&
  1272. (this.left.is_string(compressor) || this.right.is_string(compressor));
  1273. });
  1274. def(AST_Assign, function(compressor){
  1275. return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
  1276. });
  1277. def(AST_Seq, function(compressor){
  1278. return this.cdr.is_string(compressor);
  1279. });
  1280. def(AST_Conditional, function(compressor){
  1281. return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
  1282. });
  1283. })(function(node, func){
  1284. node.DEFMETHOD("is_string", func);
  1285. });
  1286. var unary_side_effects = makePredicate("delete ++ --");
  1287. function is_lhs(node, parent) {
  1288. if (parent instanceof AST_Unary && unary_side_effects(parent.operator)) return parent.expression;
  1289. if (parent instanceof AST_Assign && parent.left === node) return node;
  1290. }
  1291. (function (def){
  1292. AST_Node.DEFMETHOD("resolve_defines", function(compressor) {
  1293. if (!compressor.option("global_defs")) return;
  1294. var def = this._find_defs(compressor, "");
  1295. if (def) {
  1296. var node, parent = this, level = 0;
  1297. do {
  1298. node = parent;
  1299. parent = compressor.parent(level++);
  1300. } while (parent instanceof AST_PropAccess && parent.expression === node);
  1301. if (is_lhs(node, parent)) {
  1302. compressor.warn('global_defs ' + this.print_to_string() + ' redefined [{file}:{line},{col}]', this.start);
  1303. } else {
  1304. return def;
  1305. }
  1306. }
  1307. });
  1308. function to_node(value, orig) {
  1309. if (value instanceof AST_Node) return make_node(value.CTOR, orig, value);
  1310. if (Array.isArray(value)) return make_node(AST_Array, orig, {
  1311. elements: value.map(function(value) {
  1312. return to_node(value, orig);
  1313. })
  1314. });
  1315. if (value && typeof value == "object") {
  1316. var props = [];
  1317. for (var key in value) {
  1318. props.push(make_node(AST_ObjectKeyVal, orig, {
  1319. key: key,
  1320. value: to_node(value[key], orig)
  1321. }));
  1322. }
  1323. return make_node(AST_Object, orig, {
  1324. properties: props
  1325. });
  1326. }
  1327. return make_node_from_constant(value, orig);
  1328. }
  1329. def(AST_Node, noop);
  1330. def(AST_Dot, function(compressor, suffix){
  1331. return this.expression._find_defs(compressor, "." + this.property + suffix);
  1332. });
  1333. def(AST_SymbolRef, function(compressor, suffix){
  1334. if (!this.global()) return;
  1335. var name;
  1336. var defines = compressor.option("global_defs");
  1337. if (defines && HOP(defines, (name = this.name + suffix))) {
  1338. var node = to_node(defines[name], this);
  1339. var top = compressor.find_parent(AST_Toplevel);
  1340. node.walk(new TreeWalker(function(node) {
  1341. if (node instanceof AST_SymbolRef) {
  1342. node.scope = top;
  1343. node.thedef = top.def_global(node);
  1344. }
  1345. }));
  1346. return node;
  1347. }
  1348. });
  1349. })(function(node, func){
  1350. node.DEFMETHOD("_find_defs", func);
  1351. });
  1352. function best_of_expression(ast1, ast2) {
  1353. return ast1.print_to_string().length >
  1354. ast2.print_to_string().length
  1355. ? ast2 : ast1;
  1356. }
  1357. function best_of_statement(ast1, ast2) {
  1358. return best_of_expression(make_node(AST_SimpleStatement, ast1, {
  1359. body: ast1
  1360. }), make_node(AST_SimpleStatement, ast2, {
  1361. body: ast2
  1362. })).body;
  1363. }
  1364. function best_of(compressor, ast1, ast2) {
  1365. return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2);
  1366. }
  1367. // methods to evaluate a constant expression
  1368. (function (def){
  1369. // If the node has been successfully reduced to a constant,
  1370. // then its value is returned; otherwise the element itself
  1371. // is returned.
  1372. // They can be distinguished as constant value is never a
  1373. // descendant of AST_Node.
  1374. AST_Node.DEFMETHOD("evaluate", function(compressor){
  1375. if (!compressor.option("evaluate")) return this;
  1376. try {
  1377. var val = this._eval(compressor);
  1378. return !val || val instanceof RegExp || typeof val != "object" ? val : this;
  1379. } catch(ex) {
  1380. if (ex !== def) throw ex;
  1381. return this;
  1382. }
  1383. });
  1384. var unaryPrefix = makePredicate("! ~ - + void");
  1385. AST_Node.DEFMETHOD("is_constant", function(){
  1386. // Accomodate when compress option evaluate=false
  1387. // as well as the common constant expressions !0 and -1
  1388. if (this instanceof AST_Constant) {
  1389. return !(this instanceof AST_RegExp);
  1390. } else {
  1391. return this instanceof AST_UnaryPrefix
  1392. && this.expression instanceof AST_Constant
  1393. && unaryPrefix(this.operator);
  1394. }
  1395. });
  1396. // Obtain the constant value of an expression already known to be constant.
  1397. // Result only valid iff this.is_constant() is true.
  1398. AST_Node.DEFMETHOD("constant_value", function(compressor){
  1399. // Accomodate when option evaluate=false.
  1400. if (this instanceof AST_Constant && !(this instanceof AST_RegExp)) {
  1401. return this.value;
  1402. }
  1403. // Accomodate the common constant expressions !0 and -1 when option evaluate=false.
  1404. if (this instanceof AST_UnaryPrefix
  1405. && this.expression instanceof AST_Constant) switch (this.operator) {
  1406. case "!":
  1407. return !this.expression.value;
  1408. case "~":
  1409. return ~this.expression.value;
  1410. case "-":
  1411. return -this.expression.value;
  1412. case "+":
  1413. return +this.expression.value;
  1414. default:
  1415. throw new Error(string_template("Cannot evaluate unary expression {value}", {
  1416. value: this.print_to_string()
  1417. }));
  1418. }
  1419. var result = this.evaluate(compressor);
  1420. if (result !== this) {
  1421. return result;
  1422. }
  1423. throw new Error(string_template("Cannot evaluate constant [{file}:{line},{col}]", this.start));
  1424. });
  1425. def(AST_Statement, function(){
  1426. throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
  1427. });
  1428. def(AST_Lambda, function(){
  1429. throw def;
  1430. });
  1431. function ev(node, compressor) {
  1432. if (!compressor) throw new Error("Compressor must be passed");
  1433. return node._eval(compressor);
  1434. };
  1435. def(AST_Node, function(){
  1436. throw def; // not constant
  1437. });
  1438. def(AST_Constant, function(){
  1439. return this.getValue();
  1440. });
  1441. def(AST_Array, function(compressor){
  1442. if (compressor.option("unsafe")) {
  1443. return this.elements.map(function(element) {
  1444. return ev(element, compressor);
  1445. });
  1446. }
  1447. throw def;
  1448. });
  1449. def(AST_Object, function(compressor){
  1450. if (compressor.option("unsafe")) {
  1451. var val = {};
  1452. for (var i = 0, len = this.properties.length; i < len; i++) {
  1453. var prop = this.properties[i];
  1454. var key = prop.key;
  1455. if (key instanceof AST_Symbol) {
  1456. key = key.name;
  1457. } else if (key instanceof AST_Node) {
  1458. key = ev(key, compressor);
  1459. }
  1460. if (typeof Object.prototype[key] === 'function') {
  1461. throw def;
  1462. }
  1463. val[key] = ev(prop.value, compressor);
  1464. }
  1465. return val;
  1466. }
  1467. throw def;
  1468. });
  1469. def(AST_UnaryPrefix, function(compressor){
  1470. var e = this.expression;
  1471. switch (this.operator) {
  1472. case "!": return !ev(e, compressor);
  1473. case "typeof":
  1474. // Function would be evaluated to an array and so typeof would
  1475. // incorrectly return 'object'. Hence making is a special case.
  1476. if (e instanceof AST_Function) return typeof function(){};
  1477. e = ev(e, compressor);
  1478. // typeof <RegExp> returns "object" or "function" on different platforms
  1479. // so cannot evaluate reliably
  1480. if (e instanceof RegExp) throw def;
  1481. return typeof e;
  1482. case "void": return void ev(e, compressor);
  1483. case "~": return ~ev(e, compressor);
  1484. case "-": return -ev(e, compressor);
  1485. case "+": return +ev(e, compressor);
  1486. }
  1487. throw def;
  1488. });
  1489. def(AST_Binary, function(c){
  1490. var left = this.left, right = this.right, result;
  1491. switch (this.operator) {
  1492. case "&&" : result = ev(left, c) && ev(right, c); break;
  1493. case "||" : result = ev(left, c) || ev(right, c); break;
  1494. case "|" : result = ev(left, c) | ev(right, c); break;
  1495. case "&" : result = ev(left, c) & ev(right, c); break;
  1496. case "^" : result = ev(left, c) ^ ev(right, c); break;
  1497. case "+" : result = ev(left, c) + ev(right, c); break;
  1498. case "*" : result = ev(left, c) * ev(right, c); break;
  1499. case "/" : result = ev(left, c) / ev(right, c); break;
  1500. case "%" : result = ev(left, c) % ev(right, c); break;
  1501. case "-" : result = ev(left, c) - ev(right, c); break;
  1502. case "<<" : result = ev(left, c) << ev(right, c); break;
  1503. case ">>" : result = ev(left, c) >> ev(right, c); break;
  1504. case ">>>" : result = ev(left, c) >>> ev(right, c); break;
  1505. case "==" : result = ev(left, c) == ev(right, c); break;
  1506. case "===" : result = ev(left, c) === ev(right, c); break;
  1507. case "!=" : result = ev(left, c) != ev(right, c); break;
  1508. case "!==" : result = ev(left, c) !== ev(right, c); break;
  1509. case "<" : result = ev(left, c) < ev(right, c); break;
  1510. case "<=" : result = ev(left, c) <= ev(right, c); break;
  1511. case ">" : result = ev(left, c) > ev(right, c); break;
  1512. case ">=" : result = ev(left, c) >= ev(right, c); break;
  1513. default:
  1514. throw def;
  1515. }
  1516. if (isNaN(result) && c.find_parent(AST_With)) {
  1517. // leave original expression as is
  1518. throw def;
  1519. }
  1520. return result;
  1521. });
  1522. def(AST_Conditional, function(compressor){
  1523. return ev(this.condition, compressor)
  1524. ? ev(this.consequent, compressor)
  1525. : ev(this.alternative, compressor);
  1526. });
  1527. def(AST_SymbolRef, function(compressor){
  1528. if (!compressor.option("reduce_vars") || this._evaluating) throw def;
  1529. this._evaluating = true;
  1530. try {
  1531. var fixed = this.fixed_value();
  1532. if (!fixed) throw def;
  1533. var value = ev(fixed, compressor);
  1534. if (!HOP(fixed, "_eval")) fixed._eval = function() {
  1535. return value;
  1536. };
  1537. if (value && typeof value == "object" && this.definition().escaped) throw def;
  1538. return value;
  1539. } finally {
  1540. this._evaluating = false;
  1541. }
  1542. });
  1543. def(AST_PropAccess, function(compressor){
  1544. if (compressor.option("unsafe")) {
  1545. var key = this.property;
  1546. if (key instanceof AST_Node) {
  1547. key = ev(key, compressor);
  1548. }
  1549. var val = ev(this.expression, compressor);
  1550. if (val && HOP(val, key)) {
  1551. return val[key];
  1552. }
  1553. }
  1554. throw def;
  1555. });
  1556. })(function(node, func){
  1557. node.DEFMETHOD("_eval", func);
  1558. });
  1559. // method to negate an expression
  1560. (function(def){
  1561. function basic_negation(exp) {
  1562. return make_node(AST_UnaryPrefix, exp, {
  1563. operator: "!",
  1564. expression: exp
  1565. });
  1566. }
  1567. function best(orig, alt, first_in_statement) {
  1568. var negated = basic_negation(orig);
  1569. if (first_in_statement) {
  1570. var stat = make_node(AST_SimpleStatement, alt, {
  1571. body: alt
  1572. });
  1573. return best_of_expression(negated, stat) === stat ? alt : negated;
  1574. }
  1575. return best_of_expression(negated, alt);
  1576. }
  1577. def(AST_Node, function(){
  1578. return basic_negation(this);
  1579. });
  1580. def(AST_Statement, function(){
  1581. throw new Error("Cannot negate a statement");
  1582. });
  1583. def(AST_Function, function(){
  1584. return basic_negation(this);
  1585. });
  1586. def(AST_UnaryPrefix, function(){
  1587. if (this.operator == "!")
  1588. return this.expression;
  1589. return basic_negation(this);
  1590. });
  1591. def(AST_Seq, function(compressor){
  1592. var self = this.clone();
  1593. self.cdr = self.cdr.negate(compressor);
  1594. return self;
  1595. });
  1596. def(AST_Conditional, function(compressor, first_in_statement){
  1597. var self = this.clone();
  1598. self.consequent = self.consequent.negate(compressor);
  1599. self.alternative = self.alternative.negate(compressor);
  1600. return best(this, self, first_in_statement);
  1601. });
  1602. def(AST_Binary, function(compressor, first_in_statement){
  1603. var self = this.clone(), op = this.operator;
  1604. if (compressor.option("unsafe_comps")) {
  1605. switch (op) {
  1606. case "<=" : self.operator = ">" ; return self;
  1607. case "<" : self.operator = ">=" ; return self;
  1608. case ">=" : self.operator = "<" ; return self;
  1609. case ">" : self.operator = "<=" ; return self;
  1610. }
  1611. }
  1612. switch (op) {
  1613. case "==" : self.operator = "!="; return self;
  1614. case "!=" : self.operator = "=="; return self;
  1615. case "===": self.operator = "!=="; return self;
  1616. case "!==": self.operator = "==="; return self;
  1617. case "&&":
  1618. self.operator = "||";
  1619. self.left = self.left.negate(compressor, first_in_statement);
  1620. self.right = self.right.negate(compressor);
  1621. return best(this, self, first_in_statement);
  1622. case "||":
  1623. self.operator = "&&";
  1624. self.left = self.left.negate(compressor, first_in_statement);
  1625. self.right = self.right.negate(compressor);
  1626. return best(this, self, first_in_statement);
  1627. }
  1628. return basic_negation(this);
  1629. });
  1630. })(function(node, func){
  1631. node.DEFMETHOD("negate", function(compressor, first_in_statement){
  1632. return func.call(this, compressor, first_in_statement);
  1633. });
  1634. });
  1635. AST_Call.DEFMETHOD("has_pure_annotation", function(compressor) {
  1636. if (!compressor.option("side_effects")) return false;
  1637. if (this.pure !== undefined) return this.pure;
  1638. var pure = false;
  1639. var comments, last_comment;
  1640. if (this.start
  1641. && (comments = this.start.comments_before)
  1642. && comments.length
  1643. && /[@#]__PURE__/.test((last_comment = comments[comments.length - 1]).value)) {
  1644. pure = last_comment;
  1645. }
  1646. return this.pure = pure;
  1647. });
  1648. // determine if expression has side effects
  1649. (function(def){
  1650. def(AST_Node, return_true);
  1651. def(AST_EmptyStatement, return_false);
  1652. def(AST_Constant, return_false);
  1653. def(AST_This, return_false);
  1654. def(AST_Call, function(compressor){
  1655. if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return true;
  1656. for (var i = this.args.length; --i >= 0;) {
  1657. if (this.args[i].has_side_effects(compressor))
  1658. return true;
  1659. }
  1660. return false;
  1661. });
  1662. function any(list, compressor) {
  1663. for (var i = list.length; --i >= 0;)
  1664. if (list[i].has_side_effects(compressor))
  1665. return true;
  1666. return false;
  1667. }
  1668. def(AST_Block, function(compressor){
  1669. return any(this.body, compressor);
  1670. });
  1671. def(AST_Switch, function(compressor){
  1672. return this.expression.has_side_effects(compressor)
  1673. || any(this.body, compressor);
  1674. });
  1675. def(AST_Case, function(compressor){
  1676. return this.expression.has_side_effects(compressor)
  1677. || any(this.body, compressor);
  1678. });
  1679. def(AST_Try, function(compressor){
  1680. return any(this.body, compressor)
  1681. || this.bcatch && this.bcatch.has_side_effects(compressor)
  1682. || this.bfinally && this.bfinally.has_side_effects(compressor);
  1683. });
  1684. def(AST_If, function(compressor){
  1685. return this.condition.has_side_effects(compressor)
  1686. || this.body && this.body.has_side_effects(compressor)
  1687. || this.alternative && this.alternative.has_side_effects(compressor);
  1688. });
  1689. def(AST_LabeledStatement, function(compressor){
  1690. return this.body.has_side_effects(compressor);
  1691. });
  1692. def(AST_SimpleStatement, function(compressor){
  1693. return this.body.has_side_effects(compressor);
  1694. });
  1695. def(AST_Defun, return_true);
  1696. def(AST_Function, return_false);
  1697. def(AST_Binary, function(compressor){
  1698. return this.left.has_side_effects(compressor)
  1699. || this.right.has_side_effects(compressor);
  1700. });
  1701. def(AST_Assign, return_true);
  1702. def(AST_Conditional, function(compressor){
  1703. return this.condition.has_side_effects(compressor)
  1704. || this.consequent.has_side_effects(compressor)
  1705. || this.alternative.has_side_effects(compressor);
  1706. });
  1707. def(AST_Unary, function(compressor){
  1708. return unary_side_effects(this.operator)
  1709. || this.expression.has_side_effects(compressor);
  1710. });
  1711. def(AST_SymbolRef, function(compressor){
  1712. return this.undeclared();
  1713. });
  1714. def(AST_Object, function(compressor){
  1715. return any(this.properties, compressor);
  1716. });
  1717. def(AST_ObjectProperty, function(compressor){
  1718. return this.value.has_side_effects(compressor);
  1719. });
  1720. def(AST_Array, function(compressor){
  1721. return any(this.elements, compressor);
  1722. });
  1723. def(AST_Dot, function(compressor){
  1724. return this.expression.may_throw_on_access(compressor)
  1725. || this.expression.has_side_effects(compressor);
  1726. });
  1727. def(AST_Sub, function(compressor){
  1728. return this.expression.may_throw_on_access(compressor)
  1729. || this.expression.has_side_effects(compressor)
  1730. || this.property.has_side_effects(compressor);
  1731. });
  1732. def(AST_Seq, function(compressor){
  1733. return this.car.has_side_effects(compressor)
  1734. || this.cdr.has_side_effects(compressor);
  1735. });
  1736. })(function(node, func){
  1737. node.DEFMETHOD("has_side_effects", func);
  1738. });
  1739. // tell me if a statement aborts
  1740. function aborts(thing) {
  1741. return thing && thing.aborts();
  1742. };
  1743. (function(def){
  1744. def(AST_Statement, return_null);
  1745. def(AST_Jump, return_this);
  1746. function block_aborts(){
  1747. var n = this.body.length;
  1748. return n > 0 && aborts(this.body[n - 1]);
  1749. };
  1750. def(AST_BlockStatement, block_aborts);
  1751. def(AST_SwitchBranch, block_aborts);
  1752. def(AST_If, function(){
  1753. return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
  1754. });
  1755. })(function(node, func){
  1756. node.DEFMETHOD("aborts", func);
  1757. });
  1758. /* -----[ optimizers ]----- */
  1759. OPT(AST_Directive, function(self, compressor){
  1760. if (compressor.has_directive(self.value) !== self) {
  1761. return make_node(AST_EmptyStatement, self);
  1762. }
  1763. return self;
  1764. });
  1765. OPT(AST_Debugger, function(self, compressor){
  1766. if (compressor.option("drop_debugger"))
  1767. return make_node(AST_EmptyStatement, self);
  1768. return self;
  1769. });
  1770. OPT(AST_LabeledStatement, function(self, compressor){
  1771. if (self.body instanceof AST_Break
  1772. && compressor.loopcontrol_target(self.body) === self.body) {
  1773. return make_node(AST_EmptyStatement, self);
  1774. }
  1775. return self.label.references.length == 0 ? self.body : self;
  1776. });
  1777. OPT(AST_Block, function(self, compressor){
  1778. self.body = tighten_body(self.body, compressor);
  1779. return self;
  1780. });
  1781. OPT(AST_BlockStatement, function(self, compressor){
  1782. self.body = tighten_body(self.body, compressor);
  1783. switch (self.body.length) {
  1784. case 1: return self.body[0];
  1785. case 0: return make_node(AST_EmptyStatement, self);
  1786. }
  1787. return self;
  1788. });
  1789. AST_Scope.DEFMETHOD("drop_unused", function(compressor){
  1790. var self = this;
  1791. if (compressor.has_directive("use asm")) return self;
  1792. var toplevel = compressor.option("toplevel");
  1793. if (compressor.option("unused")
  1794. && (!(self instanceof AST_Toplevel) || toplevel)
  1795. && !self.uses_eval
  1796. && !self.uses_with) {
  1797. var assign_as_unused = !/keep_assign/.test(compressor.option("unused"));
  1798. var drop_funcs = /funcs/.test(toplevel);
  1799. var drop_vars = /vars/.test(toplevel);
  1800. if (!(self instanceof AST_Toplevel) || toplevel == true) {
  1801. drop_funcs = drop_vars = true;
  1802. }
  1803. var in_use = [];
  1804. var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
  1805. if (self instanceof AST_Toplevel && compressor.top_retain) {
  1806. self.variables.each(function(def) {
  1807. if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
  1808. in_use_ids[def.id] = true;
  1809. in_use.push(def);
  1810. }
  1811. });
  1812. }
  1813. var initializations = new Dictionary();
  1814. // pass 1: find out which symbols are directly used in
  1815. // this scope (not in nested scopes).
  1816. var scope = this;
  1817. var tw = new TreeWalker(function(node, descend){
  1818. if (node !== self) {
  1819. if (node instanceof AST_Defun) {
  1820. if (!drop_funcs && scope === self) {
  1821. var node_def = node.name.definition();
  1822. if (!(node_def.id in in_use_ids)) {
  1823. in_use_ids[node_def.id] = true;
  1824. in_use.push(node_def);
  1825. }
  1826. }
  1827. initializations.add(node.name.name, node);
  1828. return true; // don't go in nested scopes
  1829. }
  1830. if (node instanceof AST_Definitions && scope === self) {
  1831. node.definitions.forEach(function(def){
  1832. if (!drop_vars) {
  1833. var node_def = def.name.definition();
  1834. if (!(node_def.id in in_use_ids)) {
  1835. in_use_ids[node_def.id] = true;
  1836. in_use.push(node_def);
  1837. }
  1838. }
  1839. if (def.value) {
  1840. initializations.add(def.name.name, def.value);
  1841. if (def.value.has_side_effects(compressor)) {
  1842. def.value.walk(tw);
  1843. }
  1844. }
  1845. });
  1846. return true;
  1847. }
  1848. if (assign_as_unused
  1849. && node instanceof AST_Assign
  1850. && node.operator == "="
  1851. && node.left instanceof AST_SymbolRef
  1852. && !is_reference_const(node.left)
  1853. && scope === self) {
  1854. node.right.walk(tw);
  1855. return true;
  1856. }
  1857. if (node instanceof AST_SymbolRef) {
  1858. var node_def = node.definition();
  1859. if (!(node_def.id in in_use_ids)) {
  1860. in_use_ids[node_def.id] = true;
  1861. in_use.push(node_def);
  1862. }
  1863. return true;
  1864. }
  1865. if (node instanceof AST_Scope) {
  1866. var save_scope = scope;
  1867. scope = node;
  1868. descend();
  1869. scope = save_scope;
  1870. return true;
  1871. }
  1872. }
  1873. });
  1874. self.walk(tw);
  1875. // pass 2: for every used symbol we need to walk its
  1876. // initialization code to figure out if it uses other
  1877. // symbols (that may not be in_use).
  1878. for (var i = 0; i < in_use.length; ++i) {
  1879. in_use[i].orig.forEach(function(decl){
  1880. // undeclared globals will be instanceof AST_SymbolRef
  1881. var init = initializations.get(decl.name);
  1882. if (init) init.forEach(function(init){
  1883. var tw = new TreeWalker(function(node){
  1884. if (node instanceof AST_SymbolRef) {
  1885. var node_def = node.definition();
  1886. if (!(node_def.id in in_use_ids)) {
  1887. in_use_ids[node_def.id] = true;
  1888. in_use.push(node_def);
  1889. }
  1890. }
  1891. });
  1892. init.walk(tw);
  1893. });
  1894. });
  1895. }
  1896. // pass 3: we should drop declarations not in_use
  1897. var tt = new TreeTransformer(
  1898. function before(node, descend, in_list) {
  1899. if (node instanceof AST_Function
  1900. && node.name
  1901. && !compressor.option("keep_fnames")) {
  1902. var def = node.name.definition();
  1903. // any declarations with same name will overshadow
  1904. // name of this anonymous function and can therefore
  1905. // never be used anywhere
  1906. if (!(def.id in in_use_ids) || def.orig.length > 1)
  1907. node.name = null;
  1908. }
  1909. if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
  1910. var trim = !compressor.option("keep_fargs");
  1911. for (var a = node.argnames, i = a.length; --i >= 0;) {
  1912. var sym = a[i];
  1913. if (!(sym.definition().id in in_use_ids)) {
  1914. sym.__unused = true;
  1915. if (trim) {
  1916. a.pop();
  1917. compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", {
  1918. name : sym.name,
  1919. file : sym.start.file,
  1920. line : sym.start.line,
  1921. col : sym.start.col
  1922. });
  1923. }
  1924. }
  1925. else {
  1926. trim = false;
  1927. }
  1928. }
  1929. }
  1930. if (drop_funcs && node instanceof AST_Defun && node !== self) {
  1931. if (!(node.name.definition().id in in_use_ids)) {
  1932. compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", {
  1933. name : node.name.name,
  1934. file : node.name.start.file,
  1935. line : node.name.start.line,
  1936. col : node.name.start.col
  1937. });
  1938. return make_node(AST_EmptyStatement, node);
  1939. }
  1940. return node;
  1941. }
  1942. if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn && tt.parent().init === node)) {
  1943. var def = node.definitions.filter(function(def){
  1944. if (def.value) def.value = def.value.transform(tt);
  1945. var sym = def.name.definition();
  1946. if (sym.id in in_use_ids) return true;
  1947. if (sym.orig[0] instanceof AST_SymbolCatch) {
  1948. def.value = def.value && def.value.drop_side_effect_free(compressor);
  1949. return true;
  1950. }
  1951. var w = {
  1952. name : def.name.name,
  1953. file : def.name.start.file,
  1954. line : def.name.start.line,
  1955. col : def.name.start.col
  1956. };
  1957. if (def.value && (def._unused_side_effects = def.value.drop_side_effect_free(compressor))) {
  1958. compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
  1959. return true;
  1960. }
  1961. compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", w);
  1962. return false;
  1963. });
  1964. // place uninitialized names at the start
  1965. def = mergeSort(def, function(a, b){
  1966. if (!a.value && b.value) return -1;
  1967. if (!b.value && a.value) return 1;
  1968. return 0;
  1969. });
  1970. // for unused names whose initialization has
  1971. // side effects, we can cascade the init. code
  1972. // into the next one, or next statement.
  1973. var side_effects = [];
  1974. for (var i = 0; i < def.length;) {
  1975. var x = def[i];
  1976. if (x._unused_side_effects) {
  1977. side_effects.push(x._unused_side_effects);
  1978. def.splice(i, 1);
  1979. } else {
  1980. if (side_effects.length > 0) {
  1981. side_effects.push(x.value);
  1982. x.value = AST_Seq.from_array(side_effects);
  1983. side_effects = [];
  1984. }
  1985. ++i;
  1986. }
  1987. }
  1988. if (side_effects.length > 0) {
  1989. side_effects = make_node(AST_BlockStatement, node, {
  1990. body: [ make_node(AST_SimpleStatement, node, {
  1991. body: AST_Seq.from_array(side_effects)
  1992. }) ]
  1993. });
  1994. } else {
  1995. side_effects = null;
  1996. }
  1997. if (def.length == 0 && !side_effects) {
  1998. return make_node(AST_EmptyStatement, node);
  1999. }
  2000. if (def.length == 0) {
  2001. return in_list ? MAP.splice(side_effects.body) : side_effects;
  2002. }
  2003. node.definitions = def;
  2004. if (side_effects) {
  2005. side_effects.body.unshift(node);
  2006. return in_list ? MAP.splice(side_effects.body) : side_effects;
  2007. }
  2008. return node;
  2009. }
  2010. if (drop_vars && assign_as_unused
  2011. && node instanceof AST_Assign
  2012. && node.operator == "="
  2013. && node.left instanceof AST_SymbolRef) {
  2014. var def = node.left.definition();
  2015. if (!(def.id in in_use_ids)
  2016. && self.variables.get(def.name) === def) {
  2017. return maintain_this_binding(tt.parent(), node, node.right.transform(tt));
  2018. }
  2019. }
  2020. // certain combination of unused name + side effect leads to:
  2021. // https://github.com/mishoo/UglifyJS2/issues/44
  2022. // https://github.com/mishoo/UglifyJS2/issues/1830
  2023. // that's an invalid AST.
  2024. // We fix it at this stage by moving the `var` outside the `for`.
  2025. if (node instanceof AST_For) {
  2026. descend(node, this);
  2027. if (node.init instanceof AST_BlockStatement) {
  2028. var block = node.init;
  2029. node.init = block.body.pop();
  2030. block.body.push(node);
  2031. return in_list ? MAP.splice(block.body) : block;
  2032. } else if (is_empty(node.init)) {
  2033. node.init = null;
  2034. }
  2035. return node;
  2036. }
  2037. if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
  2038. descend(node, this);
  2039. if (node.body instanceof AST_BlockStatement) {
  2040. var block = node.body;
  2041. node.body = block.body.pop();
  2042. block.body.push(node);
  2043. return in_list ? MAP.splice(block.body) : block;
  2044. }
  2045. return node;
  2046. }
  2047. if (node instanceof AST_Scope && node !== self)
  2048. return node;
  2049. }
  2050. );
  2051. self.transform(tt);
  2052. }
  2053. });
  2054. AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
  2055. var self = this;
  2056. if (compressor.has_directive("use asm")) return self;
  2057. var hoist_funs = compressor.option("hoist_funs");
  2058. var hoist_vars = compressor.option("hoist_vars");
  2059. if (hoist_funs || hoist_vars) {
  2060. var dirs = [];
  2061. var hoisted = [];
  2062. var vars = new Dictionary(), vars_found = 0, var_decl = 0;
  2063. // let's count var_decl first, we seem to waste a lot of
  2064. // space if we hoist `var` when there's only one.
  2065. self.walk(new TreeWalker(function(node){
  2066. if (node instanceof AST_Scope && node !== self)
  2067. return true;
  2068. if (node instanceof AST_Var) {
  2069. ++var_decl;
  2070. return true;
  2071. }
  2072. }));
  2073. hoist_vars = hoist_vars && var_decl > 1;
  2074. var tt = new TreeTransformer(
  2075. function before(node) {
  2076. if (node !== self) {
  2077. if (node instanceof AST_Directive) {
  2078. dirs.push(node);
  2079. return make_node(AST_EmptyStatement, node);
  2080. }
  2081. if (node instanceof AST_Defun && hoist_funs) {
  2082. hoisted.push(node);
  2083. return make_node(AST_EmptyStatement, node);
  2084. }
  2085. if (node instanceof AST_Var && hoist_vars) {
  2086. node.definitions.forEach(function(def){
  2087. vars.set(def.name.name, def);
  2088. ++vars_found;
  2089. });
  2090. var seq = node.to_assignments(compressor);
  2091. var p = tt.parent();
  2092. if (p instanceof AST_ForIn && p.init === node) {
  2093. if (seq == null) {
  2094. var def = node.definitions[0].name;
  2095. return make_node(AST_SymbolRef, def, def);
  2096. }
  2097. return seq;
  2098. }
  2099. if (p instanceof AST_For && p.init === node) {
  2100. return seq;
  2101. }
  2102. if (!seq) return make_node(AST_EmptyStatement, node);
  2103. return make_node(AST_SimpleStatement, node, {
  2104. body: seq
  2105. });
  2106. }
  2107. if (node instanceof AST_Scope)
  2108. return node; // to avoid descending in nested scopes
  2109. }
  2110. }
  2111. );
  2112. self = self.transform(tt);
  2113. if (vars_found > 0) {
  2114. // collect only vars which don't show up in self's arguments list
  2115. var defs = [];
  2116. vars.each(function(def, name){
  2117. if (self instanceof AST_Lambda
  2118. && find_if(function(x){ return x.name == def.name.name },
  2119. self.argnames)) {
  2120. vars.del(name);
  2121. } else {
  2122. def = def.clone();
  2123. def.value = null;
  2124. defs.push(def);
  2125. vars.set(name, def);
  2126. }
  2127. });
  2128. if (defs.length > 0) {
  2129. // try to merge in assignments
  2130. for (var i = 0; i < self.body.length;) {
  2131. if (self.body[i] instanceof AST_SimpleStatement) {
  2132. var expr = self.body[i].body, sym, assign;
  2133. if (expr instanceof AST_Assign
  2134. && expr.operator == "="
  2135. && (sym = expr.left) instanceof AST_Symbol
  2136. && vars.has(sym.name))
  2137. {
  2138. var def = vars.get(sym.name);
  2139. if (def.value) break;
  2140. def.value = expr.right;
  2141. remove(defs, def);
  2142. defs.push(def);
  2143. self.body.splice(i, 1);
  2144. continue;
  2145. }
  2146. if (expr instanceof AST_Seq
  2147. && (assign = expr.car) instanceof AST_Assign
  2148. && assign.operator == "="
  2149. && (sym = assign.left) instanceof AST_Symbol
  2150. && vars.has(sym.name))
  2151. {
  2152. var def = vars.get(sym.name);
  2153. if (def.value) break;
  2154. def.value = assign.right;
  2155. remove(defs, def);
  2156. defs.push(def);
  2157. self.body[i].body = expr.cdr;
  2158. continue;
  2159. }
  2160. }
  2161. if (self.body[i] instanceof AST_EmptyStatement) {
  2162. self.body.splice(i, 1);
  2163. continue;
  2164. }
  2165. if (self.body[i] instanceof AST_BlockStatement) {
  2166. var tmp = [ i, 1 ].concat(self.body[i].body);
  2167. self.body.splice.apply(self.body, tmp);
  2168. continue;
  2169. }
  2170. break;
  2171. }
  2172. defs = make_node(AST_Var, self, {
  2173. definitions: defs
  2174. });
  2175. hoisted.push(defs);
  2176. };
  2177. }
  2178. self.body = dirs.concat(hoisted, self.body);
  2179. }
  2180. return self;
  2181. });
  2182. // drop_side_effect_free()
  2183. // remove side-effect-free parts which only affects return value
  2184. (function(def){
  2185. // Drop side-effect-free elements from an array of expressions.
  2186. // Returns an array of expressions with side-effects or null
  2187. // if all elements were dropped. Note: original array may be
  2188. // returned if nothing changed.
  2189. function trim(nodes, compressor, first_in_statement) {
  2190. var ret = [], changed = false;
  2191. for (var i = 0, len = nodes.length; i < len; i++) {
  2192. var node = nodes[i].drop_side_effect_free(compressor, first_in_statement);
  2193. changed |= node !== nodes[i];
  2194. if (node) {
  2195. ret.push(node);
  2196. first_in_statement = false;
  2197. }
  2198. }
  2199. return changed ? ret.length ? ret : null : nodes;
  2200. }
  2201. def(AST_Node, return_this);
  2202. def(AST_Constant, return_null);
  2203. def(AST_This, return_null);
  2204. def(AST_Call, function(compressor, first_in_statement){
  2205. if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) {
  2206. if (this.expression instanceof AST_Function
  2207. && (!this.expression.name || !this.expression.name.definition().references.length)) {
  2208. var node = this.clone();
  2209. node.expression = node.expression.process_expression(false, compressor);
  2210. return node;
  2211. }
  2212. return this;
  2213. }
  2214. if (this.pure) {
  2215. compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
  2216. this.pure.value = this.pure.value.replace(/[@#]__PURE__/g, ' ');
  2217. }
  2218. var args = trim(this.args, compressor, first_in_statement);
  2219. return args && AST_Seq.from_array(args);
  2220. });
  2221. def(AST_Accessor, return_null);
  2222. def(AST_Function, return_null);
  2223. def(AST_Binary, function(compressor, first_in_statement){
  2224. var right = this.right.drop_side_effect_free(compressor);
  2225. if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement);
  2226. switch (this.operator) {
  2227. case "&&":
  2228. case "||":
  2229. if (right === this.right) return this;
  2230. var node = this.clone();
  2231. node.right = right;
  2232. return node;
  2233. default:
  2234. var left = this.left.drop_side_effect_free(compressor, first_in_statement);
  2235. if (!left) return this.right.drop_side_effect_free(compressor, first_in_statement);
  2236. return make_node(AST_Seq, this, {
  2237. car: left,
  2238. cdr: right
  2239. });
  2240. }
  2241. });
  2242. def(AST_Assign, return_this);
  2243. def(AST_Conditional, function(compressor){
  2244. var consequent = this.consequent.drop_side_effect_free(compressor);
  2245. var alternative = this.alternative.drop_side_effect_free(compressor);
  2246. if (consequent === this.consequent && alternative === this.alternative) return this;
  2247. if (!consequent) return alternative ? make_node(AST_Binary, this, {
  2248. operator: "||",
  2249. left: this.condition,
  2250. right: alternative
  2251. }) : this.condition.drop_side_effect_free(compressor);
  2252. if (!alternative) return make_node(AST_Binary, this, {
  2253. operator: "&&",
  2254. left: this.condition,
  2255. right: consequent
  2256. });
  2257. var node = this.clone();
  2258. node.consequent = consequent;
  2259. node.alternative = alternative;
  2260. return node;
  2261. });
  2262. def(AST_Unary, function(compressor, first_in_statement){
  2263. if (unary_side_effects(this.operator)) return this;
  2264. if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef) return null;
  2265. var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
  2266. if (first_in_statement
  2267. && this instanceof AST_UnaryPrefix
  2268. && is_iife_call(expression)) {
  2269. if (expression === this.expression && this.operator.length === 1) return this;
  2270. return make_node(AST_UnaryPrefix, this, {
  2271. operator: this.operator.length === 1 ? this.operator : "!",
  2272. expression: expression
  2273. });
  2274. }
  2275. return expression;
  2276. });
  2277. def(AST_SymbolRef, function() {
  2278. return this.undeclared() ? this : null;
  2279. });
  2280. def(AST_Object, function(compressor, first_in_statement){
  2281. var values = trim(this.properties, compressor, first_in_statement);
  2282. return values && AST_Seq.from_array(values);
  2283. });
  2284. def(AST_ObjectProperty, function(compressor, first_in_statement){
  2285. return this.value.drop_side_effect_free(compressor, first_in_statement);
  2286. });
  2287. def(AST_Array, function(compressor, first_in_statement){
  2288. var values = trim(this.elements, compressor, first_in_statement);
  2289. return values && AST_Seq.from_array(values);
  2290. });
  2291. def(AST_Dot, function(compressor, first_in_statement){
  2292. if (this.expression.may_throw_on_access(compressor)) return this;
  2293. return this.expression.drop_side_effect_free(compressor, first_in_statement);
  2294. });
  2295. def(AST_Sub, function(compressor, first_in_statement){
  2296. if (this.expression.may_throw_on_access(compressor)) return this;
  2297. var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
  2298. if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
  2299. var property = this.property.drop_side_effect_free(compressor);
  2300. if (!property) return expression;
  2301. return make_node(AST_Seq, this, {
  2302. car: expression,
  2303. cdr: property
  2304. });
  2305. });
  2306. def(AST_Seq, function(compressor){
  2307. var cdr = this.cdr.drop_side_effect_free(compressor);
  2308. if (cdr === this.cdr) return this;
  2309. if (!cdr) return this.car;
  2310. return make_node(AST_Seq, this, {
  2311. car: this.car,
  2312. cdr: cdr
  2313. });
  2314. });
  2315. })(function(node, func){
  2316. node.DEFMETHOD("drop_side_effect_free", func);
  2317. });
  2318. OPT(AST_SimpleStatement, function(self, compressor){
  2319. if (compressor.option("side_effects")) {
  2320. var body = self.body;
  2321. var node = body.drop_side_effect_free(compressor, true);
  2322. if (!node) {
  2323. compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
  2324. return make_node(AST_EmptyStatement, self);
  2325. }
  2326. if (node !== body) {
  2327. return make_node(AST_SimpleStatement, self, { body: node });
  2328. }
  2329. }
  2330. return self;
  2331. });
  2332. OPT(AST_DWLoop, function(self, compressor){
  2333. if (!compressor.option("loops")) return self;
  2334. var cond = self.condition.evaluate(compressor);
  2335. if (cond !== self.condition) {
  2336. if (cond) {
  2337. return make_node(AST_For, self, {
  2338. body: self.body
  2339. });
  2340. }
  2341. if (compressor.option("dead_code") && self instanceof AST_While) {
  2342. var a = [];
  2343. extract_declarations_from_unreachable_code(compressor, self.body, a);
  2344. return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
  2345. }
  2346. if (self instanceof AST_Do) {
  2347. var has_loop_control = false;
  2348. var tw = new TreeWalker(function(node) {
  2349. if (node instanceof AST_Scope || has_loop_control) return true;
  2350. if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === self)
  2351. return has_loop_control = true;
  2352. });
  2353. var parent = compressor.parent();
  2354. (parent instanceof AST_LabeledStatement ? parent : self).walk(tw);
  2355. if (!has_loop_control) return self.body;
  2356. }
  2357. }
  2358. if (self instanceof AST_While) {
  2359. return make_node(AST_For, self, self).optimize(compressor);
  2360. }
  2361. return self;
  2362. });
  2363. function if_break_in_loop(self, compressor) {
  2364. function drop_it(rest) {
  2365. rest = as_statement_array(rest);
  2366. if (self.body instanceof AST_BlockStatement) {
  2367. self.body = self.body.clone();
  2368. self.body.body = rest.concat(self.body.body.slice(1));
  2369. self.body = self.body.transform(compressor);
  2370. } else {
  2371. self.body = make_node(AST_BlockStatement, self.body, {
  2372. body: rest
  2373. }).transform(compressor);
  2374. }
  2375. if_break_in_loop(self, compressor);
  2376. }
  2377. var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
  2378. if (first instanceof AST_If) {
  2379. if (first.body instanceof AST_Break
  2380. && compressor.loopcontrol_target(first.body) === compressor.self()) {
  2381. if (self.condition) {
  2382. self.condition = make_node(AST_Binary, self.condition, {
  2383. left: self.condition,
  2384. operator: "&&",
  2385. right: first.condition.negate(compressor),
  2386. });
  2387. } else {
  2388. self.condition = first.condition.negate(compressor);
  2389. }
  2390. drop_it(first.alternative);
  2391. }
  2392. else if (first.alternative instanceof AST_Break
  2393. && compressor.loopcontrol_target(first.alternative) === compressor.self()) {
  2394. if (self.condition) {
  2395. self.condition = make_node(AST_Binary, self.condition, {
  2396. left: self.condition,
  2397. operator: "&&",
  2398. right: first.condition,
  2399. });
  2400. } else {
  2401. self.condition = first.condition;
  2402. }
  2403. drop_it(first.body);
  2404. }
  2405. }
  2406. };
  2407. OPT(AST_For, function(self, compressor){
  2408. if (!compressor.option("loops")) return self;
  2409. if (self.condition) {
  2410. var cond = self.condition.evaluate(compressor);
  2411. if (compressor.option("dead_code") && !cond) {
  2412. var a = [];
  2413. if (self.init instanceof AST_Statement) {
  2414. a.push(self.init);
  2415. }
  2416. else if (self.init) {
  2417. a.push(make_node(AST_SimpleStatement, self.init, {
  2418. body: self.init
  2419. }));
  2420. }
  2421. extract_declarations_from_unreachable_code(compressor, self.body, a);
  2422. return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
  2423. }
  2424. if (cond !== self.condition) {
  2425. cond = make_node_from_constant(cond, self.condition).transform(compressor);
  2426. self.condition = best_of_expression(cond, self.condition);
  2427. }
  2428. }
  2429. if_break_in_loop(self, compressor);
  2430. return self;
  2431. });
  2432. OPT(AST_If, function(self, compressor){
  2433. if (is_empty(self.alternative)) self.alternative = null;
  2434. if (!compressor.option("conditionals")) return self;
  2435. // if condition can be statically determined, warn and drop
  2436. // one of the blocks. note, statically determined implies
  2437. // “has no side effects”; also it doesn't work for cases like
  2438. // `x && true`, though it probably should.
  2439. var cond = self.condition.evaluate(compressor);
  2440. if (cond !== self.condition) {
  2441. if (cond) {
  2442. compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
  2443. if (compressor.option("dead_code")) {
  2444. var a = [];
  2445. if (self.alternative) {
  2446. extract_declarations_from_unreachable_code(compressor, self.alternative, a);
  2447. }
  2448. a.push(self.body);
  2449. return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
  2450. }
  2451. } else {
  2452. compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
  2453. if (compressor.option("dead_code")) {
  2454. var a = [];
  2455. extract_declarations_from_unreachable_code(compressor, self.body, a);
  2456. if (self.alternative) a.push(self.alternative);
  2457. return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
  2458. }
  2459. }
  2460. cond = make_node_from_constant(cond, self.condition).transform(compressor);
  2461. self.condition = best_of_expression(cond, self.condition);
  2462. }
  2463. var negated = self.condition.negate(compressor);
  2464. var self_condition_length = self.condition.print_to_string().length;
  2465. var negated_length = negated.print_to_string().length;
  2466. var negated_is_best = negated_length < self_condition_length;
  2467. if (self.alternative && negated_is_best) {
  2468. negated_is_best = false; // because we already do the switch here.
  2469. // no need to swap values of self_condition_length and negated_length
  2470. // here because they are only used in an equality comparison later on.
  2471. self.condition = negated;
  2472. var tmp = self.body;
  2473. self.body = self.alternative || make_node(AST_EmptyStatement, self);
  2474. self.alternative = tmp;
  2475. }
  2476. if (is_empty(self.body) && is_empty(self.alternative)) {
  2477. return make_node(AST_SimpleStatement, self.condition, {
  2478. body: self.condition.clone()
  2479. }).optimize(compressor);
  2480. }
  2481. if (self.body instanceof AST_SimpleStatement
  2482. && self.alternative instanceof AST_SimpleStatement) {
  2483. return make_node(AST_SimpleStatement, self, {
  2484. body: make_node(AST_Conditional, self, {
  2485. condition : self.condition,
  2486. consequent : self.body.body,
  2487. alternative : self.alternative.body
  2488. })
  2489. }).optimize(compressor);
  2490. }
  2491. if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
  2492. if (self_condition_length === negated_length && !negated_is_best
  2493. && self.condition instanceof AST_Binary && self.condition.operator == "||") {
  2494. // although the code length of self.condition and negated are the same,
  2495. // negated does not require additional surrounding parentheses.
  2496. // see https://github.com/mishoo/UglifyJS2/issues/979
  2497. negated_is_best = true;
  2498. }
  2499. if (negated_is_best) return make_node(AST_SimpleStatement, self, {
  2500. body: make_node(AST_Binary, self, {
  2501. operator : "||",
  2502. left : negated,
  2503. right : self.body.body
  2504. })
  2505. }).optimize(compressor);
  2506. return make_node(AST_SimpleStatement, self, {
  2507. body: make_node(AST_Binary, self, {
  2508. operator : "&&",
  2509. left : self.condition,
  2510. right : self.body.body
  2511. })
  2512. }).optimize(compressor);
  2513. }
  2514. if (self.body instanceof AST_EmptyStatement
  2515. && self.alternative instanceof AST_SimpleStatement) {
  2516. return make_node(AST_SimpleStatement, self, {
  2517. body: make_node(AST_Binary, self, {
  2518. operator : "||",
  2519. left : self.condition,
  2520. right : self.alternative.body
  2521. })
  2522. }).optimize(compressor);
  2523. }
  2524. if (self.body instanceof AST_Exit
  2525. && self.alternative instanceof AST_Exit
  2526. && self.body.TYPE == self.alternative.TYPE) {
  2527. return make_node(self.body.CTOR, self, {
  2528. value: make_node(AST_Conditional, self, {
  2529. condition : self.condition,
  2530. consequent : self.body.value || make_node(AST_Undefined, self.body),
  2531. alternative : self.alternative.value || make_node(AST_Undefined, self.alternative)
  2532. }).transform(compressor)
  2533. }).optimize(compressor);
  2534. }
  2535. if (self.body instanceof AST_If
  2536. && !self.body.alternative
  2537. && !self.alternative) {
  2538. self = make_node(AST_If, self, {
  2539. condition: make_node(AST_Binary, self.condition, {
  2540. operator: "&&",
  2541. left: self.condition,
  2542. right: self.body.condition
  2543. }),
  2544. body: self.body.body,
  2545. alternative: null
  2546. });
  2547. }
  2548. if (aborts(self.body)) {
  2549. if (self.alternative) {
  2550. var alt = self.alternative;
  2551. self.alternative = null;
  2552. return make_node(AST_BlockStatement, self, {
  2553. body: [ self, alt ]
  2554. }).optimize(compressor);
  2555. }
  2556. }
  2557. if (aborts(self.alternative)) {
  2558. var body = self.body;
  2559. self.body = self.alternative;
  2560. self.condition = negated_is_best ? negated : self.condition.negate(compressor);
  2561. self.alternative = null;
  2562. return make_node(AST_BlockStatement, self, {
  2563. body: [ self, body ]
  2564. }).optimize(compressor);
  2565. }
  2566. return self;
  2567. });
  2568. OPT(AST_Switch, function(self, compressor){
  2569. if (!compressor.option("switches")) return self;
  2570. var branch;
  2571. var value = self.expression.evaluate(compressor);
  2572. if (value !== self.expression) {
  2573. var expression = make_node_from_constant(value, self.expression).transform(compressor);
  2574. self.expression = best_of_expression(expression, self.expression);
  2575. }
  2576. if (!compressor.option("dead_code")) return self;
  2577. var decl = [];
  2578. var body = [];
  2579. var default_branch;
  2580. var exact_match;
  2581. for (var i = 0, len = self.body.length; i < len && !exact_match; i++) {
  2582. branch = self.body[i];
  2583. if (branch instanceof AST_Default) {
  2584. if (!default_branch) {
  2585. default_branch = branch;
  2586. } else {
  2587. eliminate_branch(branch, body[body.length - 1]);
  2588. }
  2589. } else if (value !== self.expression) {
  2590. var exp = branch.expression.evaluate(compressor);
  2591. if (exp === value) {
  2592. exact_match = branch;
  2593. if (default_branch) {
  2594. var default_index = body.indexOf(default_branch);
  2595. body.splice(default_index, 1);
  2596. eliminate_branch(default_branch, body[default_index - 1]);
  2597. default_branch = null;
  2598. }
  2599. } else if (exp !== branch.expression) {
  2600. eliminate_branch(branch, body[body.length - 1]);
  2601. continue;
  2602. }
  2603. }
  2604. if (aborts(branch)) {
  2605. var prev = body[body.length - 1];
  2606. if (aborts(prev) && prev.body.length == branch.body.length
  2607. && make_node(AST_BlockStatement, prev, prev).equivalent_to(make_node(AST_BlockStatement, branch, branch))) {
  2608. prev.body = [];
  2609. }
  2610. }
  2611. body.push(branch);
  2612. }
  2613. while (i < len) eliminate_branch(self.body[i++], body[body.length - 1]);
  2614. if (body.length > 0) {
  2615. body[0].body = decl.concat(body[0].body);
  2616. }
  2617. self.body = body;
  2618. while (branch = body[body.length - 1]) {
  2619. var stat = branch.body[branch.body.length - 1];
  2620. if (stat instanceof AST_Break && compressor.loopcontrol_target(stat) === self)
  2621. branch.body.pop();
  2622. if (branch.body.length || branch instanceof AST_Case
  2623. && (default_branch || branch.expression.has_side_effects(compressor))) break;
  2624. if (body.pop() === default_branch) default_branch = null;
  2625. }
  2626. if (body.length == 0) {
  2627. return make_node(AST_BlockStatement, self, {
  2628. body: decl.concat(make_node(AST_SimpleStatement, self.expression, {
  2629. body: self.expression
  2630. }))
  2631. }).optimize(compressor);
  2632. }
  2633. if (body.length == 1 && (body[0] === exact_match || body[0] === default_branch)) {
  2634. var has_break = false;
  2635. var tw = new TreeWalker(function(node) {
  2636. if (has_break
  2637. || node instanceof AST_Lambda
  2638. || node instanceof AST_SimpleStatement) return true;
  2639. if (node instanceof AST_Break && tw.loopcontrol_target(node) === self)
  2640. has_break = true;
  2641. });
  2642. self.walk(tw);
  2643. if (!has_break) {
  2644. body = body[0].body.slice();
  2645. body.unshift(make_node(AST_SimpleStatement, self.expression, {
  2646. body: self.expression
  2647. }));
  2648. return make_node(AST_BlockStatement, self, {
  2649. body: body
  2650. }).optimize(compressor);
  2651. }
  2652. }
  2653. return self;
  2654. function eliminate_branch(branch, prev) {
  2655. if (prev && !aborts(prev)) {
  2656. prev.body = prev.body.concat(branch.body);
  2657. } else {
  2658. extract_declarations_from_unreachable_code(compressor, branch, decl);
  2659. }
  2660. }
  2661. });
  2662. OPT(AST_Try, function(self, compressor){
  2663. self.body = tighten_body(self.body, compressor);
  2664. if (self.bcatch && self.bfinally && all(self.bfinally.body, is_empty)) self.bfinally = null;
  2665. if (all(self.body, is_empty)) {
  2666. var body = [];
  2667. if (self.bcatch) extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
  2668. if (self.bfinally) body = body.concat(self.bfinally.body);
  2669. return make_node(AST_BlockStatement, self, {
  2670. body: body
  2671. }).optimize(compressor);
  2672. }
  2673. return self;
  2674. });
  2675. AST_Definitions.DEFMETHOD("remove_initializers", function(){
  2676. this.definitions.forEach(function(def){ def.value = null });
  2677. });
  2678. AST_Definitions.DEFMETHOD("to_assignments", function(compressor){
  2679. var reduce_vars = compressor.option("reduce_vars");
  2680. var assignments = this.definitions.reduce(function(a, def){
  2681. if (def.value) {
  2682. var name = make_node(AST_SymbolRef, def.name, def.name);
  2683. a.push(make_node(AST_Assign, def, {
  2684. operator : "=",
  2685. left : name,
  2686. right : def.value
  2687. }));
  2688. if (reduce_vars) name.definition().fixed = false;
  2689. }
  2690. return a;
  2691. }, []);
  2692. if (assignments.length == 0) return null;
  2693. return AST_Seq.from_array(assignments);
  2694. });
  2695. OPT(AST_Definitions, function(self, compressor){
  2696. if (self.definitions.length == 0)
  2697. return make_node(AST_EmptyStatement, self);
  2698. return self;
  2699. });
  2700. OPT(AST_Call, function(self, compressor){
  2701. var exp = self.expression;
  2702. if (compressor.option("reduce_vars")
  2703. && exp instanceof AST_SymbolRef) {
  2704. var def = exp.definition();
  2705. var fixed = exp.fixed_value();
  2706. if (fixed instanceof AST_Defun) {
  2707. def.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true);
  2708. }
  2709. if (fixed instanceof AST_Function) {
  2710. exp = fixed;
  2711. if (compressor.option("unused")
  2712. && def.references.length == 1
  2713. && !(def.scope.uses_arguments
  2714. && def.orig[0] instanceof AST_SymbolFunarg)
  2715. && !def.scope.uses_eval
  2716. && compressor.find_parent(AST_Scope) === def.scope) {
  2717. self.expression = exp;
  2718. }
  2719. }
  2720. }
  2721. if (compressor.option("unused")
  2722. && exp instanceof AST_Function
  2723. && !exp.uses_arguments
  2724. && !exp.uses_eval) {
  2725. var pos = 0, last = 0;
  2726. for (var i = 0, len = self.args.length; i < len; i++) {
  2727. var trim = i >= exp.argnames.length;
  2728. if (trim || exp.argnames[i].__unused) {
  2729. var node = self.args[i].drop_side_effect_free(compressor);
  2730. if (node) {
  2731. self.args[pos++] = node;
  2732. } else if (!trim) {
  2733. self.args[pos++] = make_node(AST_Number, self.args[i], {
  2734. value: 0
  2735. });
  2736. continue;
  2737. }
  2738. } else {
  2739. self.args[pos++] = self.args[i];
  2740. }
  2741. last = pos;
  2742. }
  2743. self.args.length = last;
  2744. }
  2745. if (compressor.option("unsafe")) {
  2746. if (exp instanceof AST_SymbolRef && exp.undeclared()) {
  2747. switch (exp.name) {
  2748. case "Array":
  2749. if (self.args.length != 1) {
  2750. return make_node(AST_Array, self, {
  2751. elements: self.args
  2752. }).optimize(compressor);
  2753. }
  2754. break;
  2755. case "Object":
  2756. if (self.args.length == 0) {
  2757. return make_node(AST_Object, self, {
  2758. properties: []
  2759. });
  2760. }
  2761. break;
  2762. case "String":
  2763. if (self.args.length == 0) return make_node(AST_String, self, {
  2764. value: ""
  2765. });
  2766. if (self.args.length <= 1) return make_node(AST_Binary, self, {
  2767. left: self.args[0],
  2768. operator: "+",
  2769. right: make_node(AST_String, self, { value: "" })
  2770. }).optimize(compressor);
  2771. break;
  2772. case "Number":
  2773. if (self.args.length == 0) return make_node(AST_Number, self, {
  2774. value: 0
  2775. });
  2776. if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
  2777. expression: self.args[0],
  2778. operator: "+"
  2779. }).optimize(compressor);
  2780. case "Boolean":
  2781. if (self.args.length == 0) return make_node(AST_False, self);
  2782. if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
  2783. expression: make_node(AST_UnaryPrefix, self, {
  2784. expression: self.args[0],
  2785. operator: "!"
  2786. }),
  2787. operator: "!"
  2788. }).optimize(compressor);
  2789. break;
  2790. case "Function":
  2791. // new Function() => function(){}
  2792. if (self.args.length == 0) return make_node(AST_Function, self, {
  2793. argnames: [],
  2794. body: []
  2795. });
  2796. if (all(self.args, function(x){ return x instanceof AST_String })) {
  2797. // quite a corner-case, but we can handle it:
  2798. // https://github.com/mishoo/UglifyJS2/issues/203
  2799. // if the code argument is a constant, then we can minify it.
  2800. try {
  2801. var code = "(function(" + self.args.slice(0, -1).map(function(arg){
  2802. return arg.value;
  2803. }).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
  2804. var ast = parse(code);
  2805. ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
  2806. var comp = new Compressor(compressor.options);
  2807. ast = ast.transform(comp);
  2808. ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
  2809. ast.mangle_names();
  2810. var fun;
  2811. try {
  2812. ast.walk(new TreeWalker(function(node){
  2813. if (node instanceof AST_Lambda) {
  2814. fun = node;
  2815. throw ast;
  2816. }
  2817. }));
  2818. } catch(ex) {
  2819. if (ex !== ast) throw ex;
  2820. };
  2821. if (!fun) return self;
  2822. var args = fun.argnames.map(function(arg, i){
  2823. return make_node(AST_String, self.args[i], {
  2824. value: arg.print_to_string()
  2825. });
  2826. });
  2827. var code = OutputStream();
  2828. AST_BlockStatement.prototype._codegen.call(fun, fun, code);
  2829. code = code.toString().replace(/^\{|\}$/g, "");
  2830. args.push(make_node(AST_String, self.args[self.args.length - 1], {
  2831. value: code
  2832. }));
  2833. self.args = args;
  2834. return self;
  2835. } catch(ex) {
  2836. if (ex instanceof JS_Parse_Error) {
  2837. compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
  2838. compressor.warn(ex.toString());
  2839. } else {
  2840. console.log(ex);
  2841. throw ex;
  2842. }
  2843. }
  2844. }
  2845. break;
  2846. }
  2847. }
  2848. else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
  2849. return make_node(AST_Binary, self, {
  2850. left: make_node(AST_String, self, { value: "" }),
  2851. operator: "+",
  2852. right: exp.expression
  2853. }).optimize(compressor);
  2854. }
  2855. else if (exp instanceof AST_Dot && exp.expression instanceof AST_Array && exp.property == "join") EXIT: {
  2856. var separator;
  2857. if (self.args.length > 0) {
  2858. separator = self.args[0].evaluate(compressor);
  2859. if (separator === self.args[0]) break EXIT; // not a constant
  2860. }
  2861. var elements = [];
  2862. var consts = [];
  2863. exp.expression.elements.forEach(function(el) {
  2864. var value = el.evaluate(compressor);
  2865. if (value !== el) {
  2866. consts.push(value);
  2867. } else {
  2868. if (consts.length > 0) {
  2869. elements.push(make_node(AST_String, self, {
  2870. value: consts.join(separator)
  2871. }));
  2872. consts.length = 0;
  2873. }
  2874. elements.push(el);
  2875. }
  2876. });
  2877. if (consts.length > 0) {
  2878. elements.push(make_node(AST_String, self, {
  2879. value: consts.join(separator)
  2880. }));
  2881. }
  2882. if (elements.length == 0) return make_node(AST_String, self, { value: "" });
  2883. if (elements.length == 1) {
  2884. if (elements[0].is_string(compressor)) {
  2885. return elements[0];
  2886. }
  2887. return make_node(AST_Binary, elements[0], {
  2888. operator : "+",
  2889. left : make_node(AST_String, self, { value: "" }),
  2890. right : elements[0]
  2891. });
  2892. }
  2893. if (separator == "") {
  2894. var first;
  2895. if (elements[0].is_string(compressor)
  2896. || elements[1].is_string(compressor)) {
  2897. first = elements.shift();
  2898. } else {
  2899. first = make_node(AST_String, self, { value: "" });
  2900. }
  2901. return elements.reduce(function(prev, el){
  2902. return make_node(AST_Binary, el, {
  2903. operator : "+",
  2904. left : prev,
  2905. right : el
  2906. });
  2907. }, first).optimize(compressor);
  2908. }
  2909. // need this awkward cloning to not affect original element
  2910. // best_of will decide which one to get through.
  2911. var node = self.clone();
  2912. node.expression = node.expression.clone();
  2913. node.expression.expression = node.expression.expression.clone();
  2914. node.expression.expression.elements = elements;
  2915. return best_of(compressor, self, node);
  2916. }
  2917. else if (exp instanceof AST_Dot && exp.expression.is_string(compressor) && exp.property == "charAt") {
  2918. var arg = self.args[0];
  2919. var index = arg ? arg.evaluate(compressor) : 0;
  2920. if (index !== arg) {
  2921. return make_node(AST_Sub, exp, {
  2922. expression: exp.expression,
  2923. property: make_node_from_constant(index | 0, arg || exp)
  2924. }).optimize(compressor);
  2925. }
  2926. }
  2927. }
  2928. if (exp instanceof AST_Function) {
  2929. if (exp.body[0] instanceof AST_Return) {
  2930. var value = exp.body[0].value;
  2931. if (!value || value.is_constant()) {
  2932. var args = self.args.concat(value || make_node(AST_Undefined, self));
  2933. return AST_Seq.from_array(args).transform(compressor);
  2934. }
  2935. }
  2936. if (compressor.option("side_effects") && all(exp.body, is_empty)) {
  2937. var args = self.args.concat(make_node(AST_Undefined, self));
  2938. return AST_Seq.from_array(args).transform(compressor);
  2939. }
  2940. }
  2941. if (compressor.option("drop_console")) {
  2942. if (exp instanceof AST_PropAccess) {
  2943. var name = exp.expression;
  2944. while (name.expression) {
  2945. name = name.expression;
  2946. }
  2947. if (name instanceof AST_SymbolRef
  2948. && name.name == "console"
  2949. && name.undeclared()) {
  2950. return make_node(AST_Undefined, self).optimize(compressor);
  2951. }
  2952. }
  2953. }
  2954. if (compressor.option("negate_iife")
  2955. && compressor.parent() instanceof AST_SimpleStatement
  2956. && is_iife_call(self)) {
  2957. return self.negate(compressor, true);
  2958. }
  2959. return self;
  2960. });
  2961. OPT(AST_New, function(self, compressor){
  2962. if (compressor.option("unsafe")) {
  2963. var exp = self.expression;
  2964. if (exp instanceof AST_SymbolRef && exp.undeclared()) {
  2965. switch (exp.name) {
  2966. case "Object":
  2967. case "RegExp":
  2968. case "Function":
  2969. case "Error":
  2970. case "Array":
  2971. return make_node(AST_Call, self, self).transform(compressor);
  2972. }
  2973. }
  2974. }
  2975. return self;
  2976. });
  2977. OPT(AST_Seq, function(self, compressor){
  2978. if (!compressor.option("side_effects"))
  2979. return self;
  2980. self.car = self.car.drop_side_effect_free(compressor, first_in_statement(compressor));
  2981. if (!self.car) return maintain_this_binding(compressor.parent(), self, self.cdr);
  2982. if (compressor.option("cascade")) {
  2983. var left;
  2984. if (self.car instanceof AST_Assign
  2985. && !self.car.left.has_side_effects(compressor)) {
  2986. left = self.car.left;
  2987. } else if (self.car instanceof AST_Unary
  2988. && (self.car.operator == "++" || self.car.operator == "--")) {
  2989. left = self.car.expression;
  2990. }
  2991. if (left
  2992. && !(left instanceof AST_SymbolRef
  2993. && (left.definition().orig[0] instanceof AST_SymbolLambda
  2994. || is_reference_const(left)))) {
  2995. var parent, field;
  2996. var cdr = self.cdr;
  2997. while (true) {
  2998. if (cdr.equivalent_to(left)) {
  2999. var car = self.car instanceof AST_UnaryPostfix ? make_node(AST_UnaryPrefix, self.car, {
  3000. operator: self.car.operator,
  3001. expression: left
  3002. }) : self.car;
  3003. if (parent) {
  3004. parent[field] = car;
  3005. return self.cdr;
  3006. }
  3007. return car;
  3008. }
  3009. if (cdr instanceof AST_Binary && !(cdr instanceof AST_Assign)) {
  3010. if (cdr.left.is_constant()) {
  3011. if (cdr.operator == "||" || cdr.operator == "&&") break;
  3012. field = "right";
  3013. } else {
  3014. field = "left";
  3015. }
  3016. } else if (cdr instanceof AST_Call
  3017. || cdr instanceof AST_Unary && !unary_side_effects(cdr.operator)) {
  3018. field = "expression";
  3019. } else break;
  3020. parent = cdr;
  3021. cdr = cdr[field];
  3022. }
  3023. }
  3024. }
  3025. if (is_undefined(self.cdr, compressor)) {
  3026. return make_node(AST_UnaryPrefix, self, {
  3027. operator : "void",
  3028. expression : self.car
  3029. });
  3030. }
  3031. return self;
  3032. });
  3033. AST_Unary.DEFMETHOD("lift_sequences", function(compressor){
  3034. if (compressor.option("sequences")) {
  3035. if (this.expression instanceof AST_Seq) {
  3036. var seq = this.expression;
  3037. var x = seq.to_array();
  3038. var e = this.clone();
  3039. e.expression = x.pop();
  3040. x.push(e);
  3041. seq = AST_Seq.from_array(x).transform(compressor);
  3042. return seq;
  3043. }
  3044. }
  3045. return this;
  3046. });
  3047. OPT(AST_UnaryPostfix, function(self, compressor){
  3048. return self.lift_sequences(compressor);
  3049. });
  3050. OPT(AST_UnaryPrefix, function(self, compressor){
  3051. var e = self.expression;
  3052. if (self.operator == "delete"
  3053. && !(e instanceof AST_SymbolRef
  3054. || e instanceof AST_PropAccess
  3055. || e instanceof AST_NaN
  3056. || e instanceof AST_Infinity
  3057. || e instanceof AST_Undefined)) {
  3058. if (e instanceof AST_Seq) {
  3059. e = e.to_array();
  3060. e.push(make_node(AST_True, self));
  3061. return AST_Seq.from_array(e).optimize(compressor);
  3062. }
  3063. return make_node(AST_Seq, self, {
  3064. car: e,
  3065. cdr: make_node(AST_True, self)
  3066. }).optimize(compressor);
  3067. }
  3068. var seq = self.lift_sequences(compressor);
  3069. if (seq !== self) {
  3070. return seq;
  3071. }
  3072. if (compressor.option("side_effects") && self.operator == "void") {
  3073. e = e.drop_side_effect_free(compressor);
  3074. if (e) {
  3075. self.expression = e;
  3076. return self;
  3077. } else {
  3078. return make_node(AST_Undefined, self).optimize(compressor);
  3079. }
  3080. }
  3081. if (compressor.option("booleans") && compressor.in_boolean_context()) {
  3082. switch (self.operator) {
  3083. case "!":
  3084. if (e instanceof AST_UnaryPrefix && e.operator == "!") {
  3085. // !!foo ==> foo, if we're in boolean context
  3086. return e.expression;
  3087. }
  3088. if (e instanceof AST_Binary) {
  3089. self = best_of(compressor, self, e.negate(compressor, first_in_statement(compressor)));
  3090. }
  3091. break;
  3092. case "typeof":
  3093. // typeof always returns a non-empty string, thus it's
  3094. // always true in booleans
  3095. compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
  3096. return (e instanceof AST_SymbolRef ? make_node(AST_True, self) : make_node(AST_Seq, self, {
  3097. car: e,
  3098. cdr: make_node(AST_True, self)
  3099. })).optimize(compressor);
  3100. }
  3101. }
  3102. if (self.operator == "-" && e instanceof AST_Infinity) {
  3103. e = e.transform(compressor);
  3104. }
  3105. if (e instanceof AST_Binary
  3106. && (self.operator == "+" || self.operator == "-")
  3107. && (e.operator == "*" || e.operator == "/" || e.operator == "%")) {
  3108. return make_node(AST_Binary, self, {
  3109. operator: e.operator,
  3110. left: make_node(AST_UnaryPrefix, e.left, {
  3111. operator: self.operator,
  3112. expression: e.left
  3113. }),
  3114. right: e.right
  3115. });
  3116. }
  3117. // avoids infinite recursion of numerals
  3118. if (self.operator != "-"
  3119. || !(e instanceof AST_Number || e instanceof AST_Infinity)) {
  3120. var ev = self.evaluate(compressor);
  3121. if (ev !== self) {
  3122. ev = make_node_from_constant(ev, self).optimize(compressor);
  3123. return best_of(compressor, ev, self);
  3124. }
  3125. }
  3126. return self;
  3127. });
  3128. AST_Binary.DEFMETHOD("lift_sequences", function(compressor){
  3129. if (compressor.option("sequences")) {
  3130. if (this.left instanceof AST_Seq) {
  3131. var seq = this.left;
  3132. var x = seq.to_array();
  3133. var e = this.clone();
  3134. e.left = x.pop();
  3135. x.push(e);
  3136. return AST_Seq.from_array(x).optimize(compressor);
  3137. }
  3138. if (this.right instanceof AST_Seq && !this.left.has_side_effects(compressor)) {
  3139. var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
  3140. var root = this.right.clone();
  3141. var cursor, seq = root;
  3142. while (assign || !seq.car.has_side_effects(compressor)) {
  3143. cursor = seq;
  3144. if (seq.cdr instanceof AST_Seq) {
  3145. seq = seq.cdr = seq.cdr.clone();
  3146. } else break;
  3147. }
  3148. if (cursor) {
  3149. var e = this.clone();
  3150. e.right = cursor.cdr;
  3151. cursor.cdr = e;
  3152. return root.optimize(compressor);
  3153. }
  3154. }
  3155. }
  3156. return this;
  3157. });
  3158. var commutativeOperators = makePredicate("== === != !== * & | ^");
  3159. OPT(AST_Binary, function(self, compressor){
  3160. function reversible() {
  3161. return self.left.is_constant()
  3162. || self.right.is_constant()
  3163. || !self.left.has_side_effects(compressor)
  3164. && !self.right.has_side_effects(compressor);
  3165. }
  3166. function reverse(op) {
  3167. if (reversible()) {
  3168. if (op) self.operator = op;
  3169. var tmp = self.left;
  3170. self.left = self.right;
  3171. self.right = tmp;
  3172. }
  3173. }
  3174. if (commutativeOperators(self.operator)) {
  3175. if (self.right.is_constant()
  3176. && !self.left.is_constant()) {
  3177. // if right is a constant, whatever side effects the
  3178. // left side might have could not influence the
  3179. // result. hence, force switch.
  3180. if (!(self.left instanceof AST_Binary
  3181. && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
  3182. reverse();
  3183. }
  3184. }
  3185. }
  3186. self = self.lift_sequences(compressor);
  3187. if (compressor.option("comparisons")) switch (self.operator) {
  3188. case "===":
  3189. case "!==":
  3190. if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
  3191. (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
  3192. (self.left.is_boolean() && self.right.is_boolean())) {
  3193. self.operator = self.operator.substr(0, 2);
  3194. }
  3195. // XXX: intentionally falling down to the next case
  3196. case "==":
  3197. case "!=":
  3198. // "undefined" == typeof x => undefined === x
  3199. if (self.left instanceof AST_String
  3200. && self.left.value == "undefined"
  3201. && self.right instanceof AST_UnaryPrefix
  3202. && self.right.operator == "typeof") {
  3203. var expr = self.right.expression;
  3204. if (expr instanceof AST_SymbolRef ? !expr.undeclared()
  3205. : !(expr instanceof AST_PropAccess) || compressor.option("screw_ie8")) {
  3206. self.right = expr;
  3207. self.left = make_node(AST_Undefined, self.left).optimize(compressor);
  3208. if (self.operator.length == 2) self.operator += "=";
  3209. }
  3210. }
  3211. break;
  3212. }
  3213. if (compressor.option("booleans") && self.operator == "+" && compressor.in_boolean_context()) {
  3214. var ll = self.left.evaluate(compressor);
  3215. var rr = self.right.evaluate(compressor);
  3216. if (ll && typeof ll == "string") {
  3217. compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
  3218. return make_node(AST_Seq, self, {
  3219. car: self.right,
  3220. cdr: make_node(AST_True, self)
  3221. }).optimize(compressor);
  3222. }
  3223. if (rr && typeof rr == "string") {
  3224. compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
  3225. return make_node(AST_Seq, self, {
  3226. car: self.left,
  3227. cdr: make_node(AST_True, self)
  3228. }).optimize(compressor);
  3229. }
  3230. }
  3231. if (compressor.option("comparisons") && self.is_boolean()) {
  3232. if (!(compressor.parent() instanceof AST_Binary)
  3233. || compressor.parent() instanceof AST_Assign) {
  3234. var negated = make_node(AST_UnaryPrefix, self, {
  3235. operator: "!",
  3236. expression: self.negate(compressor, first_in_statement(compressor))
  3237. });
  3238. self = best_of(compressor, self, negated);
  3239. }
  3240. if (compressor.option("unsafe_comps")) {
  3241. switch (self.operator) {
  3242. case "<": reverse(">"); break;
  3243. case "<=": reverse(">="); break;
  3244. }
  3245. }
  3246. }
  3247. if (self.operator == "+") {
  3248. if (self.right instanceof AST_String
  3249. && self.right.getValue() == ""
  3250. && self.left.is_string(compressor)) {
  3251. return self.left;
  3252. }
  3253. if (self.left instanceof AST_String
  3254. && self.left.getValue() == ""
  3255. && self.right.is_string(compressor)) {
  3256. return self.right;
  3257. }
  3258. if (self.left instanceof AST_Binary
  3259. && self.left.operator == "+"
  3260. && self.left.left instanceof AST_String
  3261. && self.left.left.getValue() == ""
  3262. && self.right.is_string(compressor)) {
  3263. self.left = self.left.right;
  3264. return self.transform(compressor);
  3265. }
  3266. }
  3267. if (compressor.option("evaluate")) {
  3268. switch (self.operator) {
  3269. case "&&":
  3270. var ll = self.left.evaluate(compressor);
  3271. if (!ll) {
  3272. compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
  3273. return maintain_this_binding(compressor.parent(), self, self.left).optimize(compressor);
  3274. } else if (ll !== self.left) {
  3275. compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
  3276. return maintain_this_binding(compressor.parent(), self, self.right).optimize(compressor);
  3277. }
  3278. if (compressor.option("booleans") && compressor.in_boolean_context()) {
  3279. var rr = self.right.evaluate(compressor);
  3280. if (!rr) {
  3281. compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
  3282. return make_node(AST_Seq, self, {
  3283. car: self.left,
  3284. cdr: make_node(AST_False, self)
  3285. }).optimize(compressor);
  3286. } else if (rr !== self.right) {
  3287. compressor.warn("Dropping side-effect-free && in boolean context [{file}:{line},{col}]", self.start);
  3288. return self.left.optimize(compressor);
  3289. }
  3290. }
  3291. break;
  3292. case "||":
  3293. var ll = self.left.evaluate(compressor);
  3294. if (!ll) {
  3295. compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
  3296. return maintain_this_binding(compressor.parent(), self, self.right).optimize(compressor);
  3297. } else if (ll !== self.left) {
  3298. compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
  3299. return maintain_this_binding(compressor.parent(), self, self.left).optimize(compressor);
  3300. }
  3301. if (compressor.option("booleans") && compressor.in_boolean_context()) {
  3302. var rr = self.right.evaluate(compressor);
  3303. if (!rr) {
  3304. compressor.warn("Dropping side-effect-free || in boolean context [{file}:{line},{col}]", self.start);
  3305. return self.left.optimize(compressor);
  3306. } else if (rr !== self.right) {
  3307. compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
  3308. return make_node(AST_Seq, self, {
  3309. car: self.left,
  3310. cdr: make_node(AST_True, self)
  3311. }).optimize(compressor);
  3312. }
  3313. }
  3314. break;
  3315. }
  3316. var associative = true;
  3317. switch (self.operator) {
  3318. case "+":
  3319. // "foo" + ("bar" + x) => "foobar" + x
  3320. if (self.left instanceof AST_Constant
  3321. && self.right instanceof AST_Binary
  3322. && self.right.operator == "+"
  3323. && self.right.left instanceof AST_Constant
  3324. && self.right.is_string(compressor)) {
  3325. self = make_node(AST_Binary, self, {
  3326. operator: "+",
  3327. left: make_node(AST_String, self.left, {
  3328. value: "" + self.left.getValue() + self.right.left.getValue(),
  3329. start: self.left.start,
  3330. end: self.right.left.end
  3331. }),
  3332. right: self.right.right
  3333. });
  3334. }
  3335. // (x + "foo") + "bar" => x + "foobar"
  3336. if (self.right instanceof AST_Constant
  3337. && self.left instanceof AST_Binary
  3338. && self.left.operator == "+"
  3339. && self.left.right instanceof AST_Constant
  3340. && self.left.is_string(compressor)) {
  3341. self = make_node(AST_Binary, self, {
  3342. operator: "+",
  3343. left: self.left.left,
  3344. right: make_node(AST_String, self.right, {
  3345. value: "" + self.left.right.getValue() + self.right.getValue(),
  3346. start: self.left.right.start,
  3347. end: self.right.end
  3348. })
  3349. });
  3350. }
  3351. // (x + "foo") + ("bar" + y) => (x + "foobar") + y
  3352. if (self.left instanceof AST_Binary
  3353. && self.left.operator == "+"
  3354. && self.left.is_string(compressor)
  3355. && self.left.right instanceof AST_Constant
  3356. && self.right instanceof AST_Binary
  3357. && self.right.operator == "+"
  3358. && self.right.left instanceof AST_Constant
  3359. && self.right.is_string(compressor)) {
  3360. self = make_node(AST_Binary, self, {
  3361. operator: "+",
  3362. left: make_node(AST_Binary, self.left, {
  3363. operator: "+",
  3364. left: self.left.left,
  3365. right: make_node(AST_String, self.left.right, {
  3366. value: "" + self.left.right.getValue() + self.right.left.getValue(),
  3367. start: self.left.right.start,
  3368. end: self.right.left.end
  3369. })
  3370. }),
  3371. right: self.right.right
  3372. });
  3373. }
  3374. // a + -b => a - b
  3375. if (self.right instanceof AST_UnaryPrefix
  3376. && self.right.operator == "-"
  3377. && self.left.is_number(compressor)) {
  3378. self = make_node(AST_Binary, self, {
  3379. operator: "-",
  3380. left: self.left,
  3381. right: self.right.expression
  3382. });
  3383. break;
  3384. }
  3385. // -a + b => b - a
  3386. if (self.left instanceof AST_UnaryPrefix
  3387. && self.left.operator == "-"
  3388. && reversible()
  3389. && self.right.is_number(compressor)) {
  3390. self = make_node(AST_Binary, self, {
  3391. operator: "-",
  3392. left: self.right,
  3393. right: self.left.expression
  3394. });
  3395. break;
  3396. }
  3397. case "*":
  3398. associative = compressor.option("unsafe_math");
  3399. case "&":
  3400. case "|":
  3401. case "^":
  3402. // a + +b => +b + a
  3403. if (self.left.is_number(compressor)
  3404. && self.right.is_number(compressor)
  3405. && reversible()
  3406. && !(self.left instanceof AST_Binary
  3407. && self.left.operator != self.operator
  3408. && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
  3409. var reversed = make_node(AST_Binary, self, {
  3410. operator: self.operator,
  3411. left: self.right,
  3412. right: self.left
  3413. });
  3414. if (self.right instanceof AST_Constant
  3415. && !(self.left instanceof AST_Constant)) {
  3416. self = best_of(compressor, reversed, self);
  3417. } else {
  3418. self = best_of(compressor, self, reversed);
  3419. }
  3420. }
  3421. if (associative && self.is_number(compressor)) {
  3422. // a + (b + c) => (a + b) + c
  3423. if (self.right instanceof AST_Binary
  3424. && self.right.operator == self.operator) {
  3425. self = make_node(AST_Binary, self, {
  3426. operator: self.operator,
  3427. left: make_node(AST_Binary, self.left, {
  3428. operator: self.operator,
  3429. left: self.left,
  3430. right: self.right.left,
  3431. start: self.left.start,
  3432. end: self.right.left.end
  3433. }),
  3434. right: self.right.right
  3435. });
  3436. }
  3437. // (n + 2) + 3 => 5 + n
  3438. // (2 * n) * 3 => 6 + n
  3439. if (self.right instanceof AST_Constant
  3440. && self.left instanceof AST_Binary
  3441. && self.left.operator == self.operator) {
  3442. if (self.left.left instanceof AST_Constant) {
  3443. self = make_node(AST_Binary, self, {
  3444. operator: self.operator,
  3445. left: make_node(AST_Binary, self.left, {
  3446. operator: self.operator,
  3447. left: self.left.left,
  3448. right: self.right,
  3449. start: self.left.left.start,
  3450. end: self.right.end
  3451. }),
  3452. right: self.left.right
  3453. });
  3454. } else if (self.left.right instanceof AST_Constant) {
  3455. self = make_node(AST_Binary, self, {
  3456. operator: self.operator,
  3457. left: make_node(AST_Binary, self.left, {
  3458. operator: self.operator,
  3459. left: self.left.right,
  3460. right: self.right,
  3461. start: self.left.right.start,
  3462. end: self.right.end
  3463. }),
  3464. right: self.left.left
  3465. });
  3466. }
  3467. }
  3468. // (a | 1) | (2 | d) => (3 | a) | b
  3469. if (self.left instanceof AST_Binary
  3470. && self.left.operator == self.operator
  3471. && self.left.right instanceof AST_Constant
  3472. && self.right instanceof AST_Binary
  3473. && self.right.operator == self.operator
  3474. && self.right.left instanceof AST_Constant) {
  3475. self = make_node(AST_Binary, self, {
  3476. operator: self.operator,
  3477. left: make_node(AST_Binary, self.left, {
  3478. operator: self.operator,
  3479. left: make_node(AST_Binary, self.left.left, {
  3480. operator: self.operator,
  3481. left: self.left.right,
  3482. right: self.right.left,
  3483. start: self.left.right.start,
  3484. end: self.right.left.end
  3485. }),
  3486. right: self.left.left
  3487. }),
  3488. right: self.right.right
  3489. });
  3490. }
  3491. }
  3492. }
  3493. }
  3494. // x && (y && z) ==> x && y && z
  3495. // x || (y || z) ==> x || y || z
  3496. // x + ("y" + z) ==> x + "y" + z
  3497. // "x" + (y + "z")==> "x" + y + "z"
  3498. if (self.right instanceof AST_Binary
  3499. && self.right.operator == self.operator
  3500. && (self.operator == "&&"
  3501. || self.operator == "||"
  3502. || (self.operator == "+"
  3503. && (self.right.left.is_string(compressor)
  3504. || (self.left.is_string(compressor)
  3505. && self.right.right.is_string(compressor))))))
  3506. {
  3507. self.left = make_node(AST_Binary, self.left, {
  3508. operator : self.operator,
  3509. left : self.left,
  3510. right : self.right.left
  3511. });
  3512. self.right = self.right.right;
  3513. return self.transform(compressor);
  3514. }
  3515. var ev = self.evaluate(compressor);
  3516. if (ev !== self) {
  3517. ev = make_node_from_constant(ev, self).optimize(compressor);
  3518. return best_of(compressor, ev, self);
  3519. }
  3520. return self;
  3521. });
  3522. OPT(AST_SymbolRef, function(self, compressor){
  3523. var def = self.resolve_defines(compressor);
  3524. if (def) {
  3525. return def.optimize(compressor);
  3526. }
  3527. // testing against !self.scope.uses_with first is an optimization
  3528. if (compressor.option("screw_ie8")
  3529. && self.undeclared()
  3530. && (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
  3531. switch (self.name) {
  3532. case "undefined":
  3533. return make_node(AST_Undefined, self).optimize(compressor);
  3534. case "NaN":
  3535. return make_node(AST_NaN, self).optimize(compressor);
  3536. case "Infinity":
  3537. return make_node(AST_Infinity, self).optimize(compressor);
  3538. }
  3539. }
  3540. if (compressor.option("evaluate")
  3541. && compressor.option("reduce_vars")
  3542. && is_lhs(self, compressor.parent()) !== self) {
  3543. var d = self.definition();
  3544. var fixed = self.fixed_value();
  3545. if (fixed) {
  3546. if (d.should_replace === undefined) {
  3547. var init = fixed.evaluate(compressor);
  3548. if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) {
  3549. init = make_node_from_constant(init, fixed);
  3550. var value = init.optimize(compressor).print_to_string().length;
  3551. var fn;
  3552. if (has_symbol_ref(fixed)) {
  3553. fn = function() {
  3554. var result = init.optimize(compressor);
  3555. return result === init ? result.clone(true) : result;
  3556. };
  3557. } else {
  3558. value = Math.min(value, fixed.print_to_string().length);
  3559. fn = function() {
  3560. var result = best_of_expression(init.optimize(compressor), fixed);
  3561. return result === init || result === fixed ? result.clone(true) : result;
  3562. };
  3563. }
  3564. var name = d.name.length;
  3565. var overhead = 0;
  3566. if (compressor.option("unused") && (!d.global || compressor.option("toplevel"))) {
  3567. overhead = (name + 2 + value) / d.references.length;
  3568. }
  3569. d.should_replace = value <= name + overhead ? fn : false;
  3570. } else {
  3571. d.should_replace = false;
  3572. }
  3573. }
  3574. if (d.should_replace) {
  3575. return d.should_replace();
  3576. }
  3577. }
  3578. }
  3579. return self;
  3580. function has_symbol_ref(value) {
  3581. var found;
  3582. value.walk(new TreeWalker(function(node) {
  3583. if (node instanceof AST_SymbolRef) found = true;
  3584. if (found) return true;
  3585. }));
  3586. return found;
  3587. }
  3588. });
  3589. function is_atomic(lhs, self) {
  3590. return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
  3591. }
  3592. OPT(AST_Undefined, function(self, compressor){
  3593. if (compressor.option("unsafe")) {
  3594. var undef = find_variable(compressor, "undefined");
  3595. if (undef) {
  3596. var ref = make_node(AST_SymbolRef, self, {
  3597. name : "undefined",
  3598. scope : undef.scope,
  3599. thedef : undef
  3600. });
  3601. ref.is_undefined = true;
  3602. return ref;
  3603. }
  3604. }
  3605. var lhs = is_lhs(compressor.self(), compressor.parent());
  3606. if (lhs && is_atomic(lhs, self)) return self;
  3607. return make_node(AST_UnaryPrefix, self, {
  3608. operator: "void",
  3609. expression: make_node(AST_Number, self, {
  3610. value: 0
  3611. })
  3612. });
  3613. });
  3614. OPT(AST_Infinity, function(self, compressor){
  3615. var lhs = is_lhs(compressor.self(), compressor.parent());
  3616. if (lhs && is_atomic(lhs, self)) return self;
  3617. if (compressor.option("keep_infinity")
  3618. && !(lhs && !is_atomic(lhs, self))
  3619. && !find_variable(compressor, "Infinity"))
  3620. return self;
  3621. return make_node(AST_Binary, self, {
  3622. operator: "/",
  3623. left: make_node(AST_Number, self, {
  3624. value: 1
  3625. }),
  3626. right: make_node(AST_Number, self, {
  3627. value: 0
  3628. })
  3629. });
  3630. });
  3631. OPT(AST_NaN, function(self, compressor){
  3632. var lhs = is_lhs(compressor.self(), compressor.parent());
  3633. if (lhs && !is_atomic(lhs, self)
  3634. || find_variable(compressor, "NaN")) {
  3635. return make_node(AST_Binary, self, {
  3636. operator: "/",
  3637. left: make_node(AST_Number, self, {
  3638. value: 0
  3639. }),
  3640. right: make_node(AST_Number, self, {
  3641. value: 0
  3642. })
  3643. });
  3644. }
  3645. return self;
  3646. });
  3647. var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
  3648. var ASSIGN_OPS_COMMUTATIVE = [ '*', '|', '^', '&' ];
  3649. OPT(AST_Assign, function(self, compressor){
  3650. self = self.lift_sequences(compressor);
  3651. if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
  3652. // x = expr1 OP expr2
  3653. if (self.right.left instanceof AST_SymbolRef
  3654. && self.right.left.name == self.left.name
  3655. && member(self.right.operator, ASSIGN_OPS)) {
  3656. // x = x - 2 ---> x -= 2
  3657. self.operator = self.right.operator + "=";
  3658. self.right = self.right.right;
  3659. }
  3660. else if (self.right.right instanceof AST_SymbolRef
  3661. && self.right.right.name == self.left.name
  3662. && member(self.right.operator, ASSIGN_OPS_COMMUTATIVE)
  3663. && !self.right.left.has_side_effects(compressor)) {
  3664. // x = 2 & x ---> x &= 2
  3665. self.operator = self.right.operator + "=";
  3666. self.right = self.right.left;
  3667. }
  3668. }
  3669. return self;
  3670. });
  3671. OPT(AST_Conditional, function(self, compressor){
  3672. if (!compressor.option("conditionals")) return self;
  3673. if (self.condition instanceof AST_Seq) {
  3674. var car = self.condition.car;
  3675. self.condition = self.condition.cdr;
  3676. return AST_Seq.cons(car, self);
  3677. }
  3678. var cond = self.condition.evaluate(compressor);
  3679. if (cond !== self.condition) {
  3680. if (cond) {
  3681. compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
  3682. return maintain_this_binding(compressor.parent(), self, self.consequent);
  3683. } else {
  3684. compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
  3685. return maintain_this_binding(compressor.parent(), self, self.alternative);
  3686. }
  3687. }
  3688. var negated = cond.negate(compressor, first_in_statement(compressor));
  3689. if (best_of(compressor, cond, negated) === negated) {
  3690. self = make_node(AST_Conditional, self, {
  3691. condition: negated,
  3692. consequent: self.alternative,
  3693. alternative: self.consequent
  3694. });
  3695. }
  3696. var condition = self.condition;
  3697. var consequent = self.consequent;
  3698. var alternative = self.alternative;
  3699. // x?x:y --> x||y
  3700. if (condition instanceof AST_SymbolRef
  3701. && consequent instanceof AST_SymbolRef
  3702. && condition.definition() === consequent.definition()) {
  3703. return make_node(AST_Binary, self, {
  3704. operator: "||",
  3705. left: condition,
  3706. right: alternative
  3707. });
  3708. }
  3709. // if (foo) exp = something; else exp = something_else;
  3710. // |
  3711. // v
  3712. // exp = foo ? something : something_else;
  3713. if (consequent instanceof AST_Assign
  3714. && alternative instanceof AST_Assign
  3715. && consequent.operator == alternative.operator
  3716. && consequent.left.equivalent_to(alternative.left)
  3717. && (!self.condition.has_side_effects(compressor)
  3718. || consequent.operator == "="
  3719. && !consequent.left.has_side_effects(compressor))) {
  3720. return make_node(AST_Assign, self, {
  3721. operator: consequent.operator,
  3722. left: consequent.left,
  3723. right: make_node(AST_Conditional, self, {
  3724. condition: self.condition,
  3725. consequent: consequent.right,
  3726. alternative: alternative.right
  3727. })
  3728. });
  3729. }
  3730. // x ? y(a) : y(b) --> y(x ? a : b)
  3731. if (consequent instanceof AST_Call
  3732. && alternative.TYPE === consequent.TYPE
  3733. && consequent.args.length == 1
  3734. && alternative.args.length == 1
  3735. && consequent.expression.equivalent_to(alternative.expression)
  3736. && !consequent.expression.has_side_effects(compressor)) {
  3737. consequent.args[0] = make_node(AST_Conditional, self, {
  3738. condition: self.condition,
  3739. consequent: consequent.args[0],
  3740. alternative: alternative.args[0]
  3741. });
  3742. return consequent;
  3743. }
  3744. // x?y?z:a:a --> x&&y?z:a
  3745. if (consequent instanceof AST_Conditional
  3746. && consequent.alternative.equivalent_to(alternative)) {
  3747. return make_node(AST_Conditional, self, {
  3748. condition: make_node(AST_Binary, self, {
  3749. left: self.condition,
  3750. operator: "&&",
  3751. right: consequent.condition
  3752. }),
  3753. consequent: consequent.consequent,
  3754. alternative: alternative
  3755. });
  3756. }
  3757. // x ? y : y --> x, y
  3758. if (consequent.equivalent_to(alternative)) {
  3759. return make_node(AST_Seq, self, {
  3760. car: self.condition,
  3761. cdr: consequent
  3762. }).optimize(compressor);
  3763. }
  3764. if (is_true(self.consequent)) {
  3765. if (is_false(self.alternative)) {
  3766. // c ? true : false ---> !!c
  3767. return booleanize(self.condition);
  3768. }
  3769. // c ? true : x ---> !!c || x
  3770. return make_node(AST_Binary, self, {
  3771. operator: "||",
  3772. left: booleanize(self.condition),
  3773. right: self.alternative
  3774. });
  3775. }
  3776. if (is_false(self.consequent)) {
  3777. if (is_true(self.alternative)) {
  3778. // c ? false : true ---> !c
  3779. return booleanize(self.condition.negate(compressor));
  3780. }
  3781. // c ? false : x ---> !c && x
  3782. return make_node(AST_Binary, self, {
  3783. operator: "&&",
  3784. left: booleanize(self.condition.negate(compressor)),
  3785. right: self.alternative
  3786. });
  3787. }
  3788. if (is_true(self.alternative)) {
  3789. // c ? x : true ---> !c || x
  3790. return make_node(AST_Binary, self, {
  3791. operator: "||",
  3792. left: booleanize(self.condition.negate(compressor)),
  3793. right: self.consequent
  3794. });
  3795. }
  3796. if (is_false(self.alternative)) {
  3797. // c ? x : false ---> !!c && x
  3798. return make_node(AST_Binary, self, {
  3799. operator: "&&",
  3800. left: booleanize(self.condition),
  3801. right: self.consequent
  3802. });
  3803. }
  3804. return self;
  3805. function booleanize(node) {
  3806. if (node.is_boolean()) return node;
  3807. // !!expression
  3808. return make_node(AST_UnaryPrefix, node, {
  3809. operator: "!",
  3810. expression: node.negate(compressor)
  3811. });
  3812. }
  3813. // AST_True or !0
  3814. function is_true(node) {
  3815. return node instanceof AST_True
  3816. || (node instanceof AST_UnaryPrefix
  3817. && node.operator == "!"
  3818. && node.expression instanceof AST_Constant
  3819. && !node.expression.value);
  3820. }
  3821. // AST_False or !1
  3822. function is_false(node) {
  3823. return node instanceof AST_False
  3824. || (node instanceof AST_UnaryPrefix
  3825. && node.operator == "!"
  3826. && node.expression instanceof AST_Constant
  3827. && !!node.expression.value);
  3828. }
  3829. });
  3830. OPT(AST_Boolean, function(self, compressor){
  3831. if (compressor.option("booleans")) {
  3832. var p = compressor.parent();
  3833. if (p instanceof AST_Binary && (p.operator == "=="
  3834. || p.operator == "!=")) {
  3835. compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
  3836. operator : p.operator,
  3837. value : self.value,
  3838. file : p.start.file,
  3839. line : p.start.line,
  3840. col : p.start.col,
  3841. });
  3842. return make_node(AST_Number, self, {
  3843. value: +self.value
  3844. });
  3845. }
  3846. return make_node(AST_UnaryPrefix, self, {
  3847. operator: "!",
  3848. expression: make_node(AST_Number, self, {
  3849. value: 1 - self.value
  3850. })
  3851. });
  3852. }
  3853. return self;
  3854. });
  3855. OPT(AST_Sub, function(self, compressor){
  3856. var prop = self.property;
  3857. if (prop instanceof AST_String && compressor.option("properties")) {
  3858. prop = prop.getValue();
  3859. if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) {
  3860. return make_node(AST_Dot, self, {
  3861. expression : self.expression,
  3862. property : prop
  3863. }).optimize(compressor);
  3864. }
  3865. var v = parseFloat(prop);
  3866. if (!isNaN(v) && v.toString() == prop) {
  3867. self.property = make_node(AST_Number, self.property, {
  3868. value: v
  3869. });
  3870. }
  3871. }
  3872. var ev = self.evaluate(compressor);
  3873. if (ev !== self) {
  3874. ev = make_node_from_constant(ev, self).optimize(compressor);
  3875. return best_of(compressor, ev, self);
  3876. }
  3877. return self;
  3878. });
  3879. OPT(AST_Dot, function(self, compressor){
  3880. var def = self.resolve_defines(compressor);
  3881. if (def) {
  3882. return def.optimize(compressor);
  3883. }
  3884. var prop = self.property;
  3885. if (RESERVED_WORDS(prop) && !compressor.option("screw_ie8")) {
  3886. return make_node(AST_Sub, self, {
  3887. expression : self.expression,
  3888. property : make_node(AST_String, self, {
  3889. value: prop
  3890. })
  3891. }).optimize(compressor);
  3892. }
  3893. if (compressor.option("unsafe_proto")
  3894. && self.expression instanceof AST_Dot
  3895. && self.expression.property == "prototype") {
  3896. var exp = self.expression.expression;
  3897. if (exp instanceof AST_SymbolRef && exp.undeclared()) switch (exp.name) {
  3898. case "Array":
  3899. self.expression = make_node(AST_Array, self.expression, {
  3900. elements: []
  3901. });
  3902. break;
  3903. case "Object":
  3904. self.expression = make_node(AST_Object, self.expression, {
  3905. properties: []
  3906. });
  3907. break;
  3908. case "String":
  3909. self.expression = make_node(AST_String, self.expression, {
  3910. value: ""
  3911. });
  3912. break;
  3913. }
  3914. }
  3915. var ev = self.evaluate(compressor);
  3916. if (ev !== self) {
  3917. ev = make_node_from_constant(ev, self).optimize(compressor);
  3918. return best_of(compressor, ev, self);
  3919. }
  3920. return self;
  3921. });
  3922. function literals_in_boolean_context(self, compressor) {
  3923. if (compressor.option("booleans") && compressor.in_boolean_context()) {
  3924. return best_of(compressor, self, make_node(AST_Seq, self, {
  3925. car: self,
  3926. cdr: make_node(AST_True, self)
  3927. }).optimize(compressor));
  3928. }
  3929. return self;
  3930. };
  3931. OPT(AST_Array, literals_in_boolean_context);
  3932. OPT(AST_Object, literals_in_boolean_context);
  3933. OPT(AST_RegExp, literals_in_boolean_context);
  3934. OPT(AST_Return, function(self, compressor){
  3935. if (self.value && is_undefined(self.value, compressor)) {
  3936. self.value = null;
  3937. }
  3938. return self;
  3939. });
  3940. OPT(AST_VarDef, function(self, compressor){
  3941. var defines = compressor.option("global_defs");
  3942. if (defines && HOP(defines, self.name.name)) {
  3943. compressor.warn('global_defs ' + self.name.name + ' redefined [{file}:{line},{col}]', self.start);
  3944. }
  3945. return self;
  3946. });
  3947. })();