123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769 |
- /***********************************************************************
- A JavaScript tokenizer / parser / beautifier / compressor.
- https://github.com/mishoo/UglifyJS
- -------------------------------- (C) ---------------------------------
- Author: Mihai Bazon
- <mihai.bazon@gmail.com>
- http://mihai.bazon.net/blog
- Distributed under the BSD license:
- Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- * Redistributions of source code must retain the above
- copyright notice, this list of conditions and the following
- disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials
- provided with the distribution.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
- ***********************************************************************/
- "use strict";
- function Compressor(options, false_by_default) {
- if (!(this instanceof Compressor))
- return new Compressor(options, false_by_default);
- TreeTransformer.call(this, this.before, this.after);
- this.options = defaults(options, {
- annotations : !false_by_default,
- arguments : !false_by_default,
- arrows : !false_by_default,
- assignments : !false_by_default,
- awaits : !false_by_default,
- booleans : !false_by_default,
- collapse_vars : !false_by_default,
- comparisons : !false_by_default,
- conditionals : !false_by_default,
- dead_code : !false_by_default,
- default_values : !false_by_default,
- directives : !false_by_default,
- drop_console : false,
- drop_debugger : !false_by_default,
- evaluate : !false_by_default,
- expression : false,
- functions : !false_by_default,
- global_defs : false,
- hoist_exports : !false_by_default,
- hoist_funs : false,
- hoist_props : !false_by_default,
- hoist_vars : false,
- ie : false,
- if_return : !false_by_default,
- imports : !false_by_default,
- inline : !false_by_default,
- join_vars : !false_by_default,
- keep_fargs : false_by_default,
- keep_fnames : false,
- keep_infinity : false,
- loops : !false_by_default,
- merge_vars : !false_by_default,
- module : false,
- negate_iife : !false_by_default,
- objects : !false_by_default,
- optional_chains : !false_by_default,
- passes : 1,
- properties : !false_by_default,
- pure_funcs : null,
- pure_getters : !false_by_default && "strict",
- reduce_funcs : !false_by_default,
- reduce_vars : !false_by_default,
- rests : !false_by_default,
- sequences : !false_by_default,
- side_effects : !false_by_default,
- spreads : !false_by_default,
- strings : !false_by_default,
- switches : !false_by_default,
- templates : !false_by_default,
- top_retain : null,
- toplevel : !!(options && !options["expression"] && options["top_retain"]),
- typeofs : !false_by_default,
- unsafe : false,
- unsafe_comps : false,
- unsafe_Function : false,
- unsafe_math : false,
- unsafe_proto : false,
- unsafe_regexp : false,
- unsafe_undefined: false,
- unused : !false_by_default,
- varify : !false_by_default,
- webkit : false,
- yields : !false_by_default,
- }, true);
- var evaluate = this.options["evaluate"];
- this.eval_threshold = /eager/.test(evaluate) ? 1 / 0 : +evaluate;
- var global_defs = this.options["global_defs"];
- if (typeof global_defs == "object") for (var key in global_defs) {
- if (/^@/.test(key) && HOP(global_defs, key)) {
- global_defs[key.slice(1)] = parse(global_defs[key], { expression: true });
- }
- }
- if (this.options["inline"] === true) this.options["inline"] = 4;
- this.drop_fargs = this.options["keep_fargs"] ? return_false : function(lambda, parent) {
- if (lambda.length_read) return false;
- var name = lambda.name;
- if (!name) return parent && parent.TYPE == "Call" && parent.expression === lambda;
- if (name.fixed_value() !== lambda) return false;
- var def = name.definition();
- if (def.direct_access) return false;
- var escaped = def.escaped;
- return escaped && escaped.depth != 1;
- };
- if (this.options["module"]) this.directives["use strict"] = true;
- var pure_funcs = this.options["pure_funcs"];
- if (typeof pure_funcs == "function") {
- this.pure_funcs = pure_funcs;
- } else if (typeof pure_funcs == "string") {
- this.pure_funcs = function(node) {
- var expr;
- if (node instanceof AST_Call) {
- expr = node.expression;
- } else if (node instanceof AST_Template) {
- expr = node.tag;
- }
- return !(expr && pure_funcs === expr.print_to_string());
- };
- } else if (Array.isArray(pure_funcs)) {
- this.pure_funcs = function(node) {
- var expr;
- if (node instanceof AST_Call) {
- expr = node.expression;
- } else if (node instanceof AST_Template) {
- expr = node.tag;
- }
- return !(expr && member(expr.print_to_string(), pure_funcs));
- };
- } else {
- this.pure_funcs = return_true;
- }
- var sequences = this.options["sequences"];
- this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
- var top_retain = this.options["top_retain"];
- if (top_retain instanceof RegExp) {
- this.top_retain = function(def) {
- return top_retain.test(def.name);
- };
- } else if (typeof top_retain == "function") {
- this.top_retain = top_retain;
- } else if (top_retain) {
- if (typeof top_retain == "string") {
- top_retain = top_retain.split(/,/);
- }
- this.top_retain = function(def) {
- return member(def.name, top_retain);
- };
- }
- var toplevel = this.options["toplevel"];
- this.toplevel = typeof toplevel == "string" ? {
- funcs: /funcs/.test(toplevel),
- vars: /vars/.test(toplevel)
- } : {
- funcs: toplevel,
- vars: toplevel
- };
- }
- Compressor.prototype = new TreeTransformer(function(node, descend) {
- if (node._squeezed) return node;
- var is_scope = node instanceof AST_Scope;
- if (is_scope) {
- if (this.option("arrows") && is_arrow(node) && node.value) {
- node.body = [ node.first_statement() ];
- node.value = null;
- }
- node.hoist_properties(this);
- node.hoist_declarations(this);
- node.process_returns(this);
- }
- // Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize()
- // would call AST_Node.transform() if a different instance of AST_Node is
- // produced after OPT().
- // This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
- // Migrate and defer all children's AST_Node.transform() to below, which
- // will now happen after this parent AST_Node has been properly substituted
- // thus gives a consistent AST snapshot.
- descend(node, this);
- // Existing code relies on how AST_Node.optimize() worked, and omitting the
- // following replacement call would result in degraded efficiency of both
- // output and performance.
- descend(node, this);
- var opt = node.optimize(this);
- if (is_scope) {
- if (opt === node && !this.has_directive("use asm") && !opt.pinned()) {
- opt.drop_unused(this);
- if (opt.merge_variables(this)) opt.drop_unused(this);
- descend(opt, this);
- }
- if (this.option("arrows") && is_arrow(opt) && opt.body.length == 1) {
- var stat = opt.body[0];
- if (stat instanceof AST_Return) {
- opt.body.length = 0;
- opt.value = stat.value;
- }
- }
- }
- if (opt === node) opt._squeezed = true;
- return opt;
- });
- Compressor.prototype.option = function(key) {
- return this.options[key];
- };
- Compressor.prototype.exposed = function(def) {
- if (def.exported) return true;
- if (def.undeclared) return true;
- if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false;
- var toplevel = this.toplevel;
- return !all(def.orig, function(sym) {
- return toplevel[sym instanceof AST_SymbolDefun ? "funcs" : "vars"];
- });
- };
- Compressor.prototype.compress = function(node) {
- node = node.resolve_defines(this);
- node.hoist_exports(this);
- if (this.option("expression")) node.process_expression(true);
- var merge_vars = this.options.merge_vars;
- var passes = +this.options.passes || 1;
- var min_count = 1 / 0;
- var stopping = false;
- var mangle = { ie: this.option("ie") };
- for (var pass = 0; pass < passes; pass++) {
- node.figure_out_scope(mangle);
- if (pass > 0 || this.option("reduce_vars"))
- node.reset_opt_flags(this);
- this.options.merge_vars = merge_vars && (stopping || pass == passes - 1);
- node = node.transform(this);
- if (passes > 1) {
- var count = 0;
- node.walk(new TreeWalker(function() {
- count++;
- }));
- AST_Node.info("pass {pass}: last_count: {min_count}, count: {count}", {
- pass: pass,
- min_count: min_count,
- count: count,
- });
- if (count < min_count) {
- min_count = count;
- stopping = false;
- } else if (stopping) {
- break;
- } else {
- stopping = true;
- }
- }
- }
- if (this.option("expression")) node.process_expression(false);
- return node;
- };
- (function(OPT) {
- OPT(AST_Node, function(self) {
- return self;
- });
- AST_Toplevel.DEFMETHOD("hoist_exports", function(compressor) {
- if (!compressor.option("hoist_exports")) return;
- var body = this.body, props = [];
- for (var i = 0; i < body.length; i++) {
- var stat = body[i];
- if (stat instanceof AST_ExportDeclaration) {
- body[i] = stat = stat.body;
- if (stat instanceof AST_Definitions) {
- stat.definitions.forEach(function(defn) {
- defn.name.match_symbol(export_symbol, true);
- });
- } else {
- export_symbol(stat.name);
- }
- } else if (stat instanceof AST_ExportReferences) {
- body.splice(i--, 1);
- [].push.apply(props, stat.properties);
- }
- }
- if (props.length) body.push(make_node(AST_ExportReferences, this, { properties: props }));
- function export_symbol(sym) {
- if (!(sym instanceof AST_SymbolDeclaration)) return;
- var node = make_node(AST_SymbolExport, sym);
- node.alias = make_node(AST_String, node, { value: node.name });
- props.push(node);
- }
- });
- AST_Scope.DEFMETHOD("process_expression", function(insert, transform) {
- var self = this;
- var tt = new TreeTransformer(function(node) {
- if (insert) {
- if (node instanceof AST_Directive) node = make_node(AST_SimpleStatement, node, {
- body: make_node(AST_String, node),
- });
- if (node instanceof AST_SimpleStatement) {
- return transform ? transform(node) : make_node(AST_Return, node, { value: node.body });
- }
- } else if (node instanceof AST_Return) {
- if (transform) return transform(node);
- var value = node.value;
- if (value instanceof AST_String) return make_node(AST_Directive, value);
- return make_node(AST_SimpleStatement, node, {
- body: value || make_node(AST_UnaryPrefix, node, {
- operator: "void",
- expression: make_node(AST_Number, node, { value: 0 }),
- }),
- });
- }
- if (node instanceof AST_Block) {
- if (node instanceof AST_Lambda) {
- if (node !== self) return node;
- } else if (insert === "awaits" && node instanceof AST_Try) {
- if (node.bfinally) return node;
- }
- for (var index = node.body.length; --index >= 0;) {
- var stat = node.body[index];
- if (!is_declaration(stat, true)) {
- node.body[index] = stat.transform(tt);
- break;
- }
- }
- } else if (node instanceof AST_If) {
- node.body = node.body.transform(tt);
- if (node.alternative) node.alternative = node.alternative.transform(tt);
- } else if (node instanceof AST_With) {
- node.body = node.body.transform(tt);
- }
- return node;
- });
- self.transform(tt);
- });
- AST_Toplevel.DEFMETHOD("unwrap_expression", function() {
- var self = this;
- switch (self.body.length) {
- case 0:
- return make_node(AST_UnaryPrefix, self, {
- operator: "void",
- expression: make_node(AST_Number, self, { value: 0 }),
- });
- case 1:
- var stat = self.body[0];
- if (stat instanceof AST_Directive) return make_node(AST_String, stat);
- if (stat instanceof AST_SimpleStatement) return stat.body;
- default:
- return make_node(AST_Call, self, {
- expression: make_node(AST_Function, self, {
- argnames: [],
- body: self.body,
- }).init_vars(self, self),
- args: [],
- });
- }
- });
- AST_Node.DEFMETHOD("wrap_expression", function() {
- var self = this;
- if (!is_statement(self)) self = make_node(AST_SimpleStatement, self, { body: self });
- if (!(self instanceof AST_Toplevel)) self = make_node(AST_Toplevel, self, { body: [ self ] });
- return self;
- });
- function read_property(obj, node) {
- var key = node.get_property();
- if (key instanceof AST_Node) return;
- var value;
- if (obj instanceof AST_Array) {
- var elements = obj.elements;
- if (key == "length") return make_node_from_constant(elements.length, obj);
- if (typeof key == "number" && key in elements) value = elements[key];
- } else if (obj instanceof AST_Lambda) {
- if (key == "length") {
- obj.length_read = true;
- return make_node_from_constant(obj.argnames.length, obj);
- }
- } else if (obj instanceof AST_Object) {
- key = "" + key;
- var props = obj.properties;
- for (var i = props.length; --i >= 0;) {
- var prop = props[i];
- if (!can_hoist_property(prop)) return;
- if (!value && props[i].key === key) value = props[i].value;
- }
- }
- return value instanceof AST_SymbolRef && value.fixed_value() || value;
- }
- function is_read_only_fn(value, name) {
- if (value instanceof AST_Boolean) return native_fns.Boolean[name];
- if (value instanceof AST_Number) return native_fns.Number[name];
- if (value instanceof AST_String) return native_fns.String[name];
- if (name == "valueOf") return false;
- if (value instanceof AST_Array) return native_fns.Array[name];
- if (value instanceof AST_Lambda) return native_fns.Function[name];
- if (value instanceof AST_Object) return native_fns.Object[name];
- if (value instanceof AST_RegExp) return native_fns.RegExp[name] && !value.value.global;
- }
- function is_modified(compressor, tw, node, value, level, immutable, recursive) {
- var parent = tw.parent(level);
- if (compressor.option("unsafe") && parent instanceof AST_Dot && is_read_only_fn(value, parent.property)) {
- return;
- }
- var lhs = is_lhs(node, parent);
- if (lhs) return lhs;
- if (level == 0 && value && value.is_constant()) return;
- if (parent instanceof AST_Array) return is_modified(compressor, tw, parent, parent, level + 1);
- if (parent instanceof AST_Assign) switch (parent.operator) {
- case "=":
- return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive);
- case "&&=":
- case "||=":
- case "??=":
- return is_modified(compressor, tw, parent, parent, level + 1);
- default:
- return;
- }
- if (parent instanceof AST_Binary) {
- if (!lazy_op[parent.operator]) return;
- return is_modified(compressor, tw, parent, parent, level + 1);
- }
- if (parent instanceof AST_Call) {
- return !immutable
- && parent.expression === node
- && !parent.is_expr_pure(compressor)
- && (!(value instanceof AST_LambdaExpression) || !(parent instanceof AST_New) && value.contains_this());
- }
- if (parent instanceof AST_Conditional) {
- if (parent.condition === node) return;
- return is_modified(compressor, tw, parent, parent, level + 1);
- }
- if (parent instanceof AST_ForEnumeration) return parent.init === node;
- if (parent instanceof AST_ObjectKeyVal) {
- if (parent.value !== node) return;
- var obj = tw.parent(level + 1);
- return is_modified(compressor, tw, obj, obj, level + 2);
- }
- if (parent instanceof AST_PropAccess) {
- if (parent.expression !== node) return;
- var prop = read_property(value, parent);
- return (!immutable || recursive) && is_modified(compressor, tw, parent, prop, level + 1);
- }
- if (parent instanceof AST_Sequence) {
- if (parent.tail_node() !== node) return;
- return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive);
- }
- }
- function is_lambda(node) {
- return node instanceof AST_Class || node instanceof AST_Lambda;
- }
- function safe_for_extends(node) {
- return node instanceof AST_Class || node instanceof AST_Defun || node instanceof AST_Function;
- }
- function is_arguments(def) {
- return def.name == "arguments" && def.scope.uses_arguments;
- }
- function cross_scope(def, sym) {
- do {
- if (def === sym) return false;
- if (sym instanceof AST_Scope) return true;
- } while (sym = sym.parent_scope);
- }
- function can_drop_symbol(ref, compressor, keep_lambda) {
- var def = ref.redef || ref.definition();
- if (ref.in_arg && is_funarg(def)) return false;
- return all(def.orig, function(sym) {
- if (sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet) {
- if (sym instanceof AST_SymbolImport) return true;
- return compressor && safe_from_tdz(compressor, sym);
- }
- return !(keep_lambda && sym instanceof AST_SymbolLambda);
- });
- }
- function has_escaped(d, scope, node, parent) {
- if (parent instanceof AST_Assign) return parent.operator == "=" && parent.right === node;
- if (parent instanceof AST_Call) return parent.expression !== node || parent instanceof AST_New;
- if (parent instanceof AST_ClassField) return parent.value === node && !parent.static;
- if (parent instanceof AST_Exit) return parent.value === node && scope.resolve() !== d.scope.resolve();
- if (parent instanceof AST_VarDef) return parent.value === node;
- }
- function make_ref(ref, fixed) {
- var node = make_node(AST_SymbolRef, ref);
- node.fixed = fixed || make_node(AST_Undefined, ref);
- return node;
- }
- function replace_ref(resolve, fixed) {
- return function(node) {
- var ref = resolve(node);
- var node = make_ref(ref, fixed);
- var def = ref.definition();
- def.references.push(node);
- def.replaced++;
- return node;
- };
- }
- var RE_POSITIVE_INTEGER = /^(0|[1-9][0-9]*)$/;
- (function(def) {
- def(AST_Node, noop);
- function reset_def(tw, compressor, def) {
- def.assignments = 0;
- def.bool_return = 0;
- def.cross_loop = false;
- def.direct_access = false;
- def.drop_return = 0;
- def.escaped = [];
- def.first_decl = null;
- def.fixed = !def.const_redefs
- && !def.scope.pinned()
- && !compressor.exposed(def)
- && !(def.init instanceof AST_LambdaExpression && def.init !== def.scope)
- && def.init;
- def.reassigned = 0;
- def.recursive_refs = 0;
- def.references = [];
- def.single_use = undefined;
- }
- function reset_block_variables(tw, compressor, scope) {
- scope.variables.each(function(def) {
- reset_def(tw, compressor, def);
- });
- }
- function reset_variables(tw, compressor, scope) {
- scope.fn_defs = [];
- scope.variables.each(function(def) {
- reset_def(tw, compressor, def);
- var init = def.init;
- if (init instanceof AST_LambdaDefinition) {
- scope.fn_defs.push(init);
- init.safe_ids = null;
- }
- if (def.fixed === null) {
- def.safe_ids = tw.safe_ids;
- mark(tw, def);
- } else if (def.fixed) {
- tw.loop_ids[def.id] = tw.in_loop;
- mark(tw, def);
- }
- });
- scope.may_call_this = function() {
- scope.may_call_this = scope.contains_this() ? return_true : return_false;
- };
- if (scope.uses_arguments) scope.each_argname(function(node) {
- node.definition().last_ref = false;
- });
- if (compressor.option("ie")) scope.variables.each(function(def) {
- var d = def.orig[0].definition();
- if (d !== def) d.fixed = false;
- });
- }
- function safe_to_visit(tw, fn) {
- var marker = fn.safe_ids;
- return marker === undefined || marker === tw.safe_ids;
- }
- function walk_fn_def(tw, fn) {
- var was_scanning = tw.fn_scanning;
- tw.fn_scanning = fn;
- fn.walk(tw);
- tw.fn_scanning = was_scanning;
- }
- function revisit_fn_def(tw, fn) {
- fn.enclosed.forEach(function(d) {
- if (fn.variables.get(d.name) === d) return;
- if (safe_to_read(tw, d)) return;
- d.single_use = false;
- var fixed = d.fixed;
- if (typeof fixed == "function") fixed = fixed();
- if (fixed instanceof AST_Lambda) {
- var safe_ids = fixed.safe_ids;
- switch (safe_ids) {
- case null:
- case false:
- return;
- default:
- if (safe_ids && safe_ids.seq !== tw.safe_ids.seq) return;
- }
- }
- d.fixed = false;
- });
- }
- function mark_fn_def(tw, def, fn) {
- var marker = fn.safe_ids;
- if (marker === undefined) return;
- if (marker === false) return;
- if (fn.parent_scope.resolve().may_call_this === return_true) {
- if (member(fn, tw.fn_visited)) revisit_fn_def(tw, fn);
- } else if (marker) {
- var visited = member(fn, tw.fn_visited);
- if (marker === tw.safe_ids) {
- if (!visited) walk_fn_def(tw, fn);
- } else if (visited) {
- revisit_fn_def(tw, fn);
- } else {
- fn.safe_ids = false;
- }
- } else if (tw.fn_scanning && tw.fn_scanning !== def.scope.resolve()) {
- fn.safe_ids = false;
- } else {
- fn.safe_ids = tw.safe_ids;
- walk_fn_def(tw, fn);
- }
- }
- function pop_scope(tw, scope) {
- pop(tw);
- var fn_defs = scope.fn_defs;
- fn_defs.forEach(function(fn) {
- fn.safe_ids = tw.safe_ids;
- walk_fn_def(tw, fn);
- });
- fn_defs.forEach(function(fn) {
- fn.safe_ids = undefined;
- });
- scope.fn_defs = undefined;
- scope.may_call_this = undefined;
- }
- function push(tw, sequential, conditional) {
- var defined_ids = Object.create(tw.defined_ids);
- if (!sequential || conditional) defined_ids.seq = Object.create(null);
- tw.defined_ids = defined_ids;
- var safe_ids = Object.create(tw.safe_ids);
- if (!sequential) safe_ids.seq = {};
- if (conditional) safe_ids.cond = true;
- tw.safe_ids = safe_ids;
- }
- function pop(tw) {
- tw.defined_ids = Object.getPrototypeOf(tw.defined_ids);
- tw.safe_ids = Object.getPrototypeOf(tw.safe_ids);
- }
- function access(tw, def) {
- var seq = tw.defined_ids.seq;
- tw.defined_ids[def.id] = seq;
- seq[def.id] = true;
- }
- function assign(tw, def) {
- var seq = tw.defined_ids.seq;
- tw.assigned_ids[def.id] = seq;
- seq[def.id] = false;
- }
- function safe_to_access(tw, def) {
- var seq = tw.defined_ids.seq;
- var defined = tw.defined_ids[def.id];
- if (defined !== seq) return false;
- if (!defined[def.id]) return false;
- var assigned = tw.assigned_ids[def.id];
- return !assigned || assigned === seq;
- }
- function mark(tw, def) {
- tw.safe_ids[def.id] = {};
- }
- function push_ref(def, ref) {
- def.references.push(ref);
- if (def.last_ref !== false) def.last_ref = ref;
- }
- function safe_to_read(tw, def) {
- if (def.single_use == "m") return false;
- var safe = tw.safe_ids[def.id];
- if (safe) {
- var in_order = HOP(tw.safe_ids, def.id);
- if (!in_order) {
- var seq = tw.safe_ids.seq;
- if (!safe.read) {
- safe.read = seq;
- } else if (safe.read !== seq) {
- safe.read = true;
- }
- }
- if (def.fixed == null) {
- if (is_arguments(def)) return false;
- if (def.global && def.name == "arguments") return false;
- tw.loop_ids[def.id] = null;
- def.fixed = make_node(AST_Undefined, def.orig[0]);
- if (in_order) def.safe_ids = undefined;
- return true;
- }
- return !safe.assign || safe.assign === tw.safe_ids;
- }
- return def.fixed instanceof AST_LambdaDefinition;
- }
- function safe_to_assign(tw, def, declare) {
- if (!declare) {
- if (is_funarg(def) && def.scope.uses_arguments && !tw.has_directive("use strict")) return false;
- if (!all(def.orig, function(sym) {
- return !(sym instanceof AST_SymbolConst);
- })) return false;
- }
- if (def.fixed === undefined) return declare || all(def.orig, function(sym) {
- return !(sym instanceof AST_SymbolLet);
- });
- if (def.fixed === false || def.fixed === 0) return false;
- var safe = tw.safe_ids[def.id];
- if (def.safe_ids) {
- def.safe_ids[def.id] = false;
- def.safe_ids = undefined;
- return def.fixed === null || HOP(tw.safe_ids, def.id) && !safe.read;
- }
- if (!HOP(tw.safe_ids, def.id)) {
- if (!safe) return false;
- if (safe.read || tw.in_loop) {
- var scope = tw.find_parent(AST_BlockScope);
- if (scope instanceof AST_Class) return false;
- if (def.scope.resolve() !== scope.resolve()) return false;
- }
- safe.assign = safe.assign && safe.assign !== tw.safe_ids ? true : tw.safe_ids;
- }
- if (def.fixed != null && safe.read) {
- if (safe.read !== tw.safe_ids.seq) return false;
- if (tw.loop_ids[def.id] !== tw.in_loop) return false;
- }
- return safe_to_read(tw, def) && all(def.orig, function(sym) {
- return !(sym instanceof AST_SymbolLambda);
- });
- }
- function ref_once(compressor, def) {
- return compressor.option("unused")
- && !def.scope.pinned()
- && def.single_use !== false
- && def.references.length - def.recursive_refs == 1
- && !(is_funarg(def) && def.scope.uses_arguments);
- }
- function is_immutable(value) {
- if (!value) return false;
- if (value instanceof AST_Assign) {
- var op = value.operator;
- return op == "=" ? is_immutable(value.right) : !lazy_op[op.slice(0, -1)];
- }
- if (value instanceof AST_Sequence) return is_immutable(value.tail_node());
- return value.is_constant() || is_lambda(value) || value instanceof AST_ObjectIdentity;
- }
- function value_in_use(node, parent) {
- if (parent instanceof AST_Array) return true;
- if (parent instanceof AST_Binary) return lazy_op[parent.operator];
- if (parent instanceof AST_Conditional) return parent.condition !== node;
- if (parent instanceof AST_Sequence) return parent.tail_node() === node;
- if (parent instanceof AST_Spread) return true;
- }
- function mark_escaped(tw, d, scope, node, value, level, depth) {
- var parent = tw.parent(level);
- if (value && value.is_constant()) return;
- if (has_escaped(d, scope, node, parent)) {
- d.escaped.push(parent);
- if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
- if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth;
- if (d.scope.resolve() !== scope.resolve()) d.escaped.cross_scope = true;
- if (d.fixed) d.fixed.escaped = d.escaped;
- return;
- } else if (value_in_use(node, parent)) {
- mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
- } else if (parent instanceof AST_ObjectKeyVal && parent.value === node) {
- var obj = tw.parent(level + 1);
- mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
- } else if (parent instanceof AST_PropAccess && parent.expression === node) {
- value = read_property(value, parent);
- mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
- if (value) return;
- }
- if (level > 0) return;
- if (parent instanceof AST_Call && parent.expression === node) return;
- if (parent instanceof AST_Sequence && parent.tail_node() !== node) return;
- if (parent instanceof AST_SimpleStatement) return;
- if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return;
- d.direct_access = true;
- if (d.fixed) d.fixed.direct_access = true;
- }
- function mark_assignment_to_arguments(node) {
- if (!(node instanceof AST_Sub)) return;
- var expr = node.expression;
- if (!(expr instanceof AST_SymbolRef)) return;
- var def = expr.definition();
- if (!is_arguments(def)) return;
- var key = node.property;
- if (key.is_constant()) key = key.value;
- if (!(key instanceof AST_Node) && !RE_POSITIVE_INTEGER.test(key)) return;
- def.reassigned++;
- (key instanceof AST_Node ? def.scope.argnames : [ def.scope.argnames[key] ]).forEach(function(argname) {
- if (argname instanceof AST_SymbolFunarg) argname.definition().fixed = false;
- });
- }
- function make_fixed(save, fn) {
- var prev_save, prev_value;
- return function() {
- var current = save();
- if (prev_save !== current) {
- prev_save = current;
- prev_value = fn(current);
- }
- return prev_value;
- };
- }
- function make_fixed_default(compressor, node, save) {
- var prev_save, prev_seq;
- return function() {
- if (prev_seq === node) return node;
- var current = save();
- var ev = fuzzy_eval(compressor, current, true);
- if (ev instanceof AST_Node) {
- prev_seq = node;
- } else if (prev_save !== current) {
- prev_save = current;
- prev_seq = ev === undefined ? make_sequence(node, [ current, node.value ]) : current;
- }
- return prev_seq;
- };
- }
- function scan_declaration(tw, compressor, lhs, fixed, visit) {
- var scanner = new TreeWalker(function(node) {
- if (node instanceof AST_DefaultValue) {
- reset_flags(node);
- push(tw, true, true);
- node.value.walk(tw);
- pop(tw);
- var save = fixed;
- if (save) fixed = make_fixed_default(compressor, node, save);
- node.name.walk(scanner);
- fixed = save;
- return true;
- }
- if (node instanceof AST_DestructuredArray) {
- reset_flags(node);
- var save = fixed;
- node.elements.forEach(function(node, index) {
- if (node instanceof AST_Hole) return reset_flags(node);
- if (save) fixed = make_fixed(save, function(value) {
- return make_node(AST_Sub, node, {
- expression: value,
- property: make_node(AST_Number, node, { value: index }),
- });
- });
- node.walk(scanner);
- });
- if (node.rest) {
- var fixed_node;
- if (save) fixed = compressor.option("rests") && make_fixed(save, function(value) {
- if (!(value instanceof AST_Array)) return node;
- for (var i = 0, len = node.elements.length; i < len; i++) {
- if (value.elements[i] instanceof AST_Spread) return node;
- }
- if (!fixed_node) fixed_node = make_node(AST_Array, node, {});
- fixed_node.elements = value.elements.slice(len);
- return fixed_node;
- });
- node.rest.walk(scanner);
- }
- fixed = save;
- return true;
- }
- if (node instanceof AST_DestructuredObject) {
- reset_flags(node);
- var save = fixed;
- node.properties.forEach(function(node) {
- reset_flags(node);
- if (node.key instanceof AST_Node) {
- push(tw);
- node.key.walk(tw);
- pop(tw);
- }
- if (save) fixed = make_fixed(save, function(value) {
- var key = node.key;
- var type = AST_Sub;
- if (typeof key == "string") {
- if (is_identifier_string(key)) {
- type = AST_Dot;
- } else {
- key = make_node_from_constant(key, node);
- }
- }
- return make_node(type, node, {
- expression: value,
- property: key,
- });
- });
- node.value.walk(scanner);
- });
- if (node.rest) {
- fixed = false;
- node.rest.walk(scanner);
- }
- fixed = save;
- return true;
- }
- visit(node, fixed, function() {
- var save_len = tw.stack.length;
- for (var i = 0, len = scanner.stack.length - 1; i < len; i++) {
- tw.stack.push(scanner.stack[i]);
- }
- node.walk(tw);
- tw.stack.length = save_len;
- });
- return true;
- });
- lhs.walk(scanner);
- }
- function reduce_iife(tw, descend, compressor) {
- var fn = this;
- fn.inlined = false;
- var iife = tw.parent();
- var sequential = !is_async(fn) && !is_generator(fn);
- var hit = !sequential;
- var aborts = false;
- fn.walk(new TreeWalker(function(node) {
- if (hit) return aborts = true;
- if (node instanceof AST_Return) return hit = true;
- if (node instanceof AST_Scope && node !== fn) return true;
- }));
- if (aborts) push(tw, sequential);
- reset_variables(tw, compressor, fn);
- // Virtually turn IIFE parameters into variable definitions:
- // (function(a,b) {...})(c,d) ---> (function() {var a=c,b=d; ...})()
- // So existing transformation rules can work on them.
- var safe = !fn.uses_arguments || tw.has_directive("use strict");
- fn.argnames.forEach(function(argname, i) {
- var value = iife.args[i];
- scan_declaration(tw, compressor, argname, function() {
- var j = fn.argnames.indexOf(argname);
- var arg = j < 0 ? value : iife.args[j];
- if (arg instanceof AST_Sequence && arg.expressions.length < 2) arg = arg.expressions[0];
- return arg || make_node(AST_Undefined, iife);
- }, visit);
- });
- var rest = fn.rest, fixed_node;
- if (rest) scan_declaration(tw, compressor, rest, compressor.option("rests") && function() {
- if (fn.rest !== rest) return rest;
- if (!fixed_node) fixed_node = make_node(AST_Array, fn, {});
- fixed_node.elements = iife.args.slice(fn.argnames.length);
- return fixed_node;
- }, visit);
- walk_lambda(fn, tw);
- var defined_ids = tw.defined_ids;
- var safe_ids = tw.safe_ids;
- pop_scope(tw, fn);
- if (!aborts) {
- tw.defined_ids = defined_ids;
- tw.safe_ids = safe_ids;
- }
- return true;
- function visit(node, fixed) {
- var d = node.definition();
- if (!d.first_decl && d.references.length == 0) d.first_decl = node;
- if (fixed && safe && d.fixed === undefined) {
- mark(tw, d);
- tw.loop_ids[d.id] = tw.in_loop;
- d.fixed = fixed;
- d.fixed.assigns = [ node ];
- } else {
- d.fixed = false;
- }
- }
- }
- def(AST_Assign, function(tw, descend, compressor) {
- var node = this;
- var left = node.left;
- var right = node.right;
- var ld = left instanceof AST_SymbolRef && left.definition();
- var scan = ld || left instanceof AST_Destructured;
- switch (node.operator) {
- case "=":
- if (left.equals(right) && !left.has_side_effects(compressor)) {
- right.walk(tw);
- walk_prop(left);
- return true;
- }
- if (ld && right instanceof AST_LambdaExpression) {
- walk_assign();
- right.parent_scope.resolve().fn_defs.push(right);
- right.safe_ids = null;
- if (!ld.fixed || !node.write_only || tw.safe_ids.cond) mark_fn_def(tw, ld, right);
- return true;
- }
- if (scan) {
- right.walk(tw);
- walk_assign();
- return true;
- }
- mark_assignment_to_arguments(left);
- return;
- case "&&=":
- case "||=":
- case "??=":
- var lazy = true;
- default:
- if (!scan) {
- mark_assignment_to_arguments(left);
- return walk_lazy();
- }
- assign(tw, ld);
- ld.assignments++;
- var fixed = ld.fixed;
- if (is_modified(compressor, tw, node, node, 0)) {
- ld.fixed = false;
- return walk_lazy();
- }
- var safe = safe_to_read(tw, ld);
- if (lazy) push(tw, true, true);
- right.walk(tw);
- if (lazy) pop(tw);
- if (safe && !left.in_arg && safe_to_assign(tw, ld)) {
- push_ref(ld, left);
- mark(tw, ld);
- if (ld.single_use) ld.single_use = false;
- left.fixed = ld.fixed = function() {
- return make_node(AST_Binary, node, {
- operator: node.operator.slice(0, -1),
- left: make_ref(left, fixed),
- right: node.right,
- });
- };
- left.fixed.assigns = !fixed || !fixed.assigns ? [ ld.orig[0] ] : fixed.assigns.slice();
- left.fixed.assigns.push(node);
- left.fixed.to_binary = replace_ref(function(node) {
- return node.left;
- }, fixed);
- } else {
- left.walk(tw);
- ld.fixed = false;
- }
- return true;
- }
- function walk_prop(lhs) {
- reset_flags(lhs);
- if (lhs instanceof AST_Dot) {
- walk_prop(lhs.expression);
- } else if (lhs instanceof AST_Sub) {
- walk_prop(lhs.expression);
- lhs.property.walk(tw);
- } else if (lhs instanceof AST_SymbolRef) {
- var d = lhs.definition();
- push_ref(d, lhs);
- if (d.fixed) {
- lhs.fixed = d.fixed;
- if (lhs.fixed.assigns) {
- lhs.fixed.assigns.push(node);
- } else {
- lhs.fixed.assigns = [ node ];
- }
- }
- } else {
- lhs.walk(tw);
- }
- }
- function walk_assign() {
- var recursive = ld && recursive_ref(tw, ld);
- var modified = is_modified(compressor, tw, node, right, 0, is_immutable(right), recursive);
- scan_declaration(tw, compressor, left, function() {
- return node.right;
- }, function(sym, fixed, walk) {
- if (!(sym instanceof AST_SymbolRef)) {
- mark_assignment_to_arguments(sym);
- walk();
- return;
- }
- var d = sym.definition();
- assign(tw, d);
- d.assignments++;
- if (!fixed || sym.in_arg || !safe_to_assign(tw, d)) {
- walk();
- d.fixed = false;
- } else {
- push_ref(d, sym);
- mark(tw, d);
- if (left instanceof AST_Destructured
- || d.orig.length == 1 && d.orig[0] instanceof AST_SymbolDefun) {
- d.single_use = false;
- }
- tw.loop_ids[d.id] = tw.in_loop;
- d.fixed = modified ? 0 : fixed;
- sym.fixed = fixed;
- sym.fixed.assigns = [ node ];
- mark_escaped(tw, d, sym.scope, node, right, 0, 1);
- }
- });
- }
- function walk_lazy() {
- if (!lazy) return;
- left.walk(tw);
- push(tw, true, true);
- right.walk(tw);
- pop(tw);
- return true;
- }
- });
- def(AST_Binary, function(tw) {
- if (!lazy_op[this.operator]) return;
- this.left.walk(tw);
- push(tw, true, true);
- this.right.walk(tw);
- pop(tw);
- return true;
- });
- def(AST_BlockScope, function(tw, descend, compressor) {
- reset_block_variables(tw, compressor, this);
- });
- def(AST_Call, function(tw) {
- var node = this;
- var exp = node.expression;
- if (exp instanceof AST_LambdaExpression) {
- var iife = is_iife_single(node);
- node.args.forEach(function(arg) {
- arg.walk(tw);
- if (arg instanceof AST_Spread) iife = false;
- });
- if (iife) exp.reduce_vars = reduce_iife;
- exp.walk(tw);
- if (iife) delete exp.reduce_vars;
- return true;
- }
- if (node.TYPE == "Call") switch (tw.in_boolean_context()) {
- case "d":
- var drop = true;
- case true:
- mark_refs(exp, drop);
- }
- exp.walk(tw);
- var optional = node.optional;
- if (optional) push(tw, true, true);
- node.args.forEach(function(arg) {
- arg.walk(tw);
- });
- if (optional) pop(tw);
- var fixed = exp instanceof AST_SymbolRef && exp.fixed_value();
- if (fixed instanceof AST_Lambda) {
- mark_fn_def(tw, exp.definition(), fixed);
- } else {
- tw.defined_ids.seq = {};
- tw.find_parent(AST_Scope).may_call_this();
- }
- return true;
- function mark_refs(node, drop) {
- if (node instanceof AST_Assign) {
- if (node.operator != "=") return;
- mark_refs(node.left, drop);
- mark_refs(node.right, drop);
- } else if (node instanceof AST_Binary) {
- if (!lazy_op[node.operator]) return;
- mark_refs(node.left, drop);
- mark_refs(node.right, drop);
- } else if (node instanceof AST_Conditional) {
- mark_refs(node.consequent, drop);
- mark_refs(node.alternative, drop);
- } else if (node instanceof AST_SymbolRef) {
- var def = node.definition();
- def.bool_return++;
- if (drop) def.drop_return++;
- }
- }
- });
- def(AST_Class, function(tw, descend, compressor) {
- var node = this;
- reset_block_variables(tw, compressor, node);
- if (node.extends) node.extends.walk(tw);
- var props = node.properties.filter(function(prop) {
- reset_flags(prop);
- if (prop.key instanceof AST_Node) {
- tw.push(prop);
- prop.key.walk(tw);
- tw.pop();
- }
- return prop.value;
- });
- if (node.name) {
- var d = node.name.definition();
- var parent = tw.parent();
- if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) d.single_use = false;
- if (safe_to_assign(tw, d, true)) {
- mark(tw, d);
- tw.loop_ids[d.id] = tw.in_loop;
- d.fixed = function() {
- return node;
- };
- d.fixed.assigns = [ node ];
- if (!is_safe_lexical(d)) d.single_use = false;
- } else {
- d.fixed = false;
- }
- }
- props.forEach(function(prop) {
- tw.push(prop);
- if (!prop.static || is_static_field_or_init(prop) && prop.value.contains_this()) {
- push(tw);
- prop.value.walk(tw);
- pop(tw);
- } else {
- prop.value.walk(tw);
- }
- tw.pop();
- });
- return true;
- });
- def(AST_ClassInitBlock, function(tw, descend, compressor) {
- var node = this;
- push(tw, true);
- reset_variables(tw, compressor, node);
- descend();
- pop_scope(tw, node);
- return true;
- });
- def(AST_Conditional, function(tw) {
- this.condition.walk(tw);
- push(tw, true, true);
- this.consequent.walk(tw);
- pop(tw);
- push(tw, true, true);
- this.alternative.walk(tw);
- pop(tw);
- return true;
- });
- def(AST_DefaultValue, function(tw) {
- push(tw, true, true);
- this.value.walk(tw);
- pop(tw);
- this.name.walk(tw);
- return true;
- });
- def(AST_Do, function(tw) {
- var save_loop = tw.in_loop;
- tw.in_loop = this;
- push(tw);
- this.body.walk(tw);
- if (has_loop_control(this, tw.parent())) {
- pop(tw);
- push(tw);
- }
- this.condition.walk(tw);
- pop(tw);
- tw.in_loop = save_loop;
- return true;
- });
- def(AST_Dot, function(tw, descend) {
- descend();
- var node = this;
- var expr = node.expression;
- if (!node.optional) {
- while (expr instanceof AST_Assign && expr.operator == "=") {
- var lhs = expr.left;
- if (lhs instanceof AST_SymbolRef) access(tw, lhs.definition());
- expr = expr.right;
- }
- if (expr instanceof AST_SymbolRef) access(tw, expr.definition());
- }
- return true;
- });
- def(AST_For, function(tw, descend, compressor) {
- var node = this;
- reset_block_variables(tw, compressor, node);
- if (node.init) node.init.walk(tw);
- var save_loop = tw.in_loop;
- tw.in_loop = node;
- push(tw);
- if (node.condition) node.condition.walk(tw);
- node.body.walk(tw);
- if (node.step) {
- if (has_loop_control(node, tw.parent())) {
- pop(tw);
- push(tw);
- }
- node.step.walk(tw);
- }
- pop(tw);
- tw.in_loop = save_loop;
- return true;
- });
- def(AST_ForEnumeration, function(tw, descend, compressor) {
- var node = this;
- reset_block_variables(tw, compressor, node);
- node.object.walk(tw);
- var save_loop = tw.in_loop;
- tw.in_loop = node;
- push(tw);
- var init = node.init;
- if (init instanceof AST_Definitions) {
- init.definitions[0].name.mark_symbol(function(node) {
- if (node instanceof AST_SymbolDeclaration) {
- var def = node.definition();
- def.assignments++;
- def.fixed = false;
- }
- }, tw);
- } else if (init instanceof AST_Destructured || init instanceof AST_SymbolRef) {
- init.mark_symbol(function(node) {
- if (node instanceof AST_SymbolRef) {
- var def = node.definition();
- push_ref(def, node);
- def.assignments++;
- if (!node.is_immutable()) def.fixed = false;
- }
- }, tw);
- } else {
- init.walk(tw);
- }
- node.body.walk(tw);
- pop(tw);
- tw.in_loop = save_loop;
- return true;
- });
- def(AST_If, function(tw) {
- this.condition.walk(tw);
- push(tw, true, true);
- this.body.walk(tw);
- pop(tw);
- if (this.alternative) {
- push(tw, true, true);
- this.alternative.walk(tw);
- pop(tw);
- }
- return true;
- });
- def(AST_LabeledStatement, function(tw) {
- push(tw, true);
- this.body.walk(tw);
- pop(tw);
- return true;
- });
- def(AST_Lambda, function(tw, descend, compressor) {
- var fn = this;
- if (!safe_to_visit(tw, fn)) return true;
- if (!push_uniq(tw.fn_visited, fn)) return true;
- fn.inlined = false;
- push(tw);
- reset_variables(tw, compressor, fn);
- descend();
- pop_scope(tw, fn);
- if (fn.name) mark_escaped(tw, fn.name.definition(), fn, fn.name, fn, 0, 1);
- return true;
- });
- def(AST_LambdaDefinition, function(tw, descend, compressor) {
- var fn = this;
- var def = fn.name.definition();
- if (!safe_to_trim(fn)) def.fixed = false;
- var parent = tw.parent();
- if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) def.single_use = false;
- if (!safe_to_visit(tw, fn)) return true;
- if (!push_uniq(tw.fn_visited, fn)) return true;
- fn.inlined = false;
- push(tw);
- reset_variables(tw, compressor, fn);
- descend();
- pop_scope(tw, fn);
- return true;
- });
- def(AST_Sub, function(tw, descend) {
- var node = this;
- var expr = node.expression;
- if (node.optional) {
- expr.walk(tw);
- push(tw, true, true);
- node.property.walk(tw);
- pop(tw);
- } else {
- descend();
- while (expr instanceof AST_Assign && expr.operator == "=") {
- var lhs = expr.left;
- if (lhs instanceof AST_SymbolRef) access(tw, lhs.definition());
- expr = expr.right;
- }
- if (expr instanceof AST_SymbolRef) access(tw, expr.definition());
- }
- return true;
- });
- def(AST_Switch, function(tw, descend, compressor) {
- var node = this;
- reset_block_variables(tw, compressor, node);
- node.expression.walk(tw);
- var first = true;
- node.body.forEach(function(branch) {
- if (branch instanceof AST_Default) return;
- branch.expression.walk(tw);
- if (first) {
- first = false;
- push(tw, true, true);
- }
- });
- if (!first) pop(tw);
- walk_body(node, tw);
- return true;
- });
- def(AST_SwitchBranch, function(tw) {
- push(tw, true, true);
- walk_body(this, tw);
- pop(tw);
- return true;
- });
- def(AST_SymbolCatch, function() {
- var d = this.definition();
- if (!d.first_decl && d.references.length == 0) d.first_decl = this;
- d.fixed = false;
- });
- def(AST_SymbolDeclaration, function() {
- var d = this.definition();
- if (!d.first_decl && d.references.length == 0) d.first_decl = this;
- });
- def(AST_SymbolDefun, function() {
- var d = this.definition();
- if (!d.first_decl && d.references.length == 0) d.first_decl = this;
- if (d.orig.length > 1 && d.scope.resolve() !== this.scope) d.fixed = false;
- });
- def(AST_SymbolImport, function() {
- var d = this.definition();
- d.first_decl = this;
- d.fixed = false;
- });
- def(AST_SymbolRef, function(tw, descend, compressor) {
- var ref = this;
- var d = ref.definition();
- var fixed = d.fixed || d.last_ref && d.last_ref.fixed;
- push_ref(d, ref);
- if (safe_to_access(tw, d)) ref.defined = true;
- if (d.references.length == 1 && !d.fixed && d.orig[0] instanceof AST_SymbolDefun) {
- tw.loop_ids[d.id] = tw.in_loop;
- }
- var recursive = recursive_ref(tw, d);
- if (recursive) recursive.enclosed.forEach(function(def) {
- if (d === def) return;
- if (def.scope.resolve() === recursive) return;
- var assigns = def.fixed && def.fixed.assigns;
- if (!assigns) return;
- if (assigns[assigns.length - 1] instanceof AST_VarDef) return;
- var safe = tw.safe_ids[def.id];
- if (!safe) return;
- safe.assign = true;
- });
- if (d.single_use == "m" && d.fixed) {
- d.fixed = 0;
- d.single_use = false;
- }
- switch (d.fixed) {
- case 0:
- if (!safe_to_read(tw, d)) d.fixed = false;
- case false:
- var redef = d.redefined();
- if (redef && cross_scope(d.scope, ref.scope)) redef.single_use = false;
- break;
- case undefined:
- d.fixed = false;
- break;
- default:
- if (!safe_to_read(tw, d)) {
- d.fixed = false;
- break;
- }
- if (ref.in_arg && d.orig[0] instanceof AST_SymbolLambda) ref.fixed = d.scope;
- var value = ref.fixed_value();
- if (recursive) {
- d.recursive_refs++;
- } else if (value && ref_once(compressor, d)) {
- d.in_loop = tw.loop_ids[d.id] !== tw.in_loop;
- d.single_use = is_lambda(value)
- && !value.pinned()
- && (!d.in_loop || tw.parent() instanceof AST_Call)
- || !d.in_loop
- && d.scope === ref.scope.resolve()
- && value.is_constant_expression();
- } else {
- d.single_use = false;
- }
- if (is_modified(compressor, tw, ref, value, 0, is_immutable(value), recursive)) {
- if (d.single_use) {
- d.single_use = "m";
- } else {
- d.fixed = 0;
- }
- }
- if (d.fixed && tw.loop_ids[d.id] !== tw.in_loop) d.cross_loop = true;
- mark_escaped(tw, d, ref.scope, ref, value, 0, 1);
- break;
- }
- if (!ref.fixed) ref.fixed = d.fixed === 0 ? fixed : d.fixed;
- if (!value && fixed) value = fixed instanceof AST_Node ? fixed : fixed();
- if (!(value instanceof AST_Lambda)) return;
- if (d.fixed) {
- var parent = tw.parent();
- if (parent instanceof AST_Call && parent.expression === ref) return;
- }
- mark_fn_def(tw, d, value);
- });
- def(AST_Template, function(tw) {
- var node = this;
- var tag = node.tag;
- if (!tag) return;
- if (tag instanceof AST_LambdaExpression) {
- node.expressions.forEach(function(exp) {
- exp.walk(tw);
- });
- tag.walk(tw);
- return true;
- }
- tag.walk(tw);
- node.expressions.forEach(function(exp) {
- exp.walk(tw);
- });
- var fixed = tag instanceof AST_SymbolRef && tag.fixed_value();
- if (fixed instanceof AST_Lambda) {
- mark_fn_def(tw, tag.definition(), fixed);
- } else {
- tw.find_parent(AST_Scope).may_call_this();
- }
- return true;
- });
- def(AST_Toplevel, function(tw, descend, compressor) {
- var node = this;
- node.globals.each(function(def) {
- reset_def(tw, compressor, def);
- });
- push(tw, true);
- reset_variables(tw, compressor, node);
- descend();
- pop_scope(tw, node);
- return true;
- });
- def(AST_Try, function(tw, descend, compressor) {
- var node = this;
- reset_block_variables(tw, compressor, node);
- push(tw, true);
- walk_body(node, tw);
- pop(tw);
- if (node.bcatch) {
- push(tw, true, true);
- node.bcatch.walk(tw);
- pop(tw);
- }
- if (node.bfinally) node.bfinally.walk(tw);
- return true;
- });
- def(AST_Unary, function(tw) {
- var node = this;
- if (!UNARY_POSTFIX[node.operator]) return;
- var exp = node.expression;
- if (!(exp instanceof AST_SymbolRef)) {
- mark_assignment_to_arguments(exp);
- return;
- }
- var d = exp.definition();
- d.assignments++;
- var fixed = d.fixed;
- if (safe_to_read(tw, d) && !exp.in_arg && safe_to_assign(tw, d)) {
- push_ref(d, exp);
- mark(tw, d);
- if (d.single_use) d.single_use = false;
- d.fixed = function() {
- return make_node(AST_Binary, node, {
- operator: node.operator.slice(0, -1),
- left: make_node(AST_UnaryPrefix, node, {
- operator: "+",
- expression: make_ref(exp, fixed),
- }),
- right: make_node(AST_Number, node, { value: 1 }),
- });
- };
- d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : [];
- d.fixed.assigns.push(node);
- if (node instanceof AST_UnaryPrefix) {
- exp.fixed = d.fixed;
- } else {
- exp.fixed = function() {
- return make_node(AST_UnaryPrefix, node, {
- operator: "+",
- expression: make_ref(exp, fixed),
- });
- };
- exp.fixed.assigns = fixed && fixed.assigns;
- exp.fixed.to_prefix = replace_ref(function(node) {
- return node.expression;
- }, d.fixed);
- }
- } else {
- exp.walk(tw);
- d.fixed = false;
- }
- return true;
- });
- def(AST_VarDef, function(tw, descend, compressor) {
- var node = this;
- var value = node.value;
- if (value instanceof AST_LambdaExpression && node.name instanceof AST_SymbolDeclaration) {
- walk_defn();
- value.parent_scope.resolve().fn_defs.push(value);
- value.safe_ids = null;
- var ld = node.name.definition();
- if (!ld.fixed) mark_fn_def(tw, ld, value);
- } else if (value) {
- value.walk(tw);
- walk_defn();
- } else if (tw.parent() instanceof AST_Let) {
- walk_defn();
- } else {
- node.name.walk(tw);
- }
- return true;
- function walk_defn() {
- scan_declaration(tw, compressor, node.name, function() {
- return node.value || make_node(AST_Undefined, node);
- }, function(name, fixed) {
- var d = name.definition();
- assign(tw, d);
- if (!d.first_decl && d.references.length == 0) d.first_decl = name;
- if (fixed && safe_to_assign(tw, d, true)) {
- mark(tw, d);
- tw.loop_ids[d.id] = tw.in_loop;
- d.fixed = fixed;
- d.fixed.assigns = [ node ];
- if (name instanceof AST_SymbolConst && d.redefined()
- || !(can_drop_symbol(name) || is_safe_lexical(d))) {
- d.single_use = false;
- }
- } else {
- d.fixed = false;
- }
- });
- }
- });
- def(AST_While, function(tw, descend) {
- var save_loop = tw.in_loop;
- tw.in_loop = this;
- push(tw);
- descend();
- pop(tw);
- tw.in_loop = save_loop;
- return true;
- });
- })(function(node, func) {
- node.DEFMETHOD("reduce_vars", func);
- });
- function reset_flags(node) {
- node._squeezed = false;
- node._optimized = false;
- node.single_use = false;
- if (node instanceof AST_BlockScope) node._var_names = undefined;
- if (node instanceof AST_SymbolRef) node.fixed = undefined;
- }
- AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
- var tw = new TreeWalker(compressor.option("reduce_vars") ? function(node, descend) {
- reset_flags(node);
- return node.reduce_vars(tw, descend, compressor);
- } : reset_flags);
- // Side-effect tracking on sequential property access
- tw.assigned_ids = Object.create(null);
- tw.defined_ids = Object.create(null);
- tw.defined_ids.seq = {};
- // Flow control for visiting lambda definitions
- tw.fn_scanning = null;
- tw.fn_visited = [];
- // Record the loop body in which `AST_SymbolDeclaration` is first encountered
- tw.in_loop = null;
- tw.loop_ids = Object.create(null);
- // Stack of look-up tables to keep track of whether a `SymbolDef` has been
- // properly assigned before use:
- // - `push()` & `pop()` when visiting conditional branches
- // - backup & restore via `save_ids` when visiting out-of-order sections
- tw.safe_ids = Object.create(null);
- tw.safe_ids.seq = {};
- this.walk(tw);
- });
- AST_Symbol.DEFMETHOD("fixed_value", function(ref_only) {
- var def = this.definition();
- var fixed = def.fixed;
- if (fixed) {
- if (this.fixed) fixed = this.fixed;
- return (fixed instanceof AST_Node ? fixed : fixed()).tail_node();
- }
- fixed = fixed === 0 && this.fixed;
- if (!fixed) return fixed;
- var value = (fixed instanceof AST_Node ? fixed : fixed()).tail_node();
- if (ref_only && def.escaped.depth != 1 && is_object(value, true)) return value;
- if (value.is_constant()) return value;
- });
- AST_SymbolRef.DEFMETHOD("is_immutable", function() {
- var def = this.redef || this.definition();
- if (!(def.orig[0] instanceof AST_SymbolLambda)) return false;
- if (def.orig.length == 1) return true;
- if (!this.in_arg) return false;
- return !(def.orig[1] instanceof AST_SymbolFunarg);
- });
- AST_Node.DEFMETHOD("convert_symbol", noop);
- function convert_destructured(type, process) {
- return this.transform(new TreeTransformer(function(node, descend) {
- if (node instanceof AST_DefaultValue) {
- node = node.clone();
- node.name = node.name.transform(this);
- return node;
- }
- if (node instanceof AST_Destructured) {
- node = node.clone();
- descend(node, this);
- return node;
- }
- if (node instanceof AST_DestructuredKeyVal) {
- node = node.clone();
- node.value = node.value.transform(this);
- return node;
- }
- return node.convert_symbol(type, process);
- }));
- }
- AST_DefaultValue.DEFMETHOD("convert_symbol", convert_destructured);
- AST_Destructured.DEFMETHOD("convert_symbol", convert_destructured);
- function convert_symbol(type, process) {
- var node = make_node(type, this);
- return process(node, this) || node;
- }
- AST_SymbolDeclaration.DEFMETHOD("convert_symbol", convert_symbol);
- AST_SymbolRef.DEFMETHOD("convert_symbol", convert_symbol);
- function process_to_assign(ref) {
- var def = ref.definition();
- def.assignments++;
- def.references.push(ref);
- }
- function mark_destructured(process, tw) {
- var marker = new TreeWalker(function(node) {
- if (node instanceof AST_DefaultValue) {
- node.value.walk(tw);
- node.name.walk(marker);
- return true;
- }
- if (node instanceof AST_DestructuredKeyVal) {
- if (node.key instanceof AST_Node) node.key.walk(tw);
- node.value.walk(marker);
- return true;
- }
- return process(node);
- });
- this.walk(marker);
- }
- AST_DefaultValue.DEFMETHOD("mark_symbol", mark_destructured);
- AST_Destructured.DEFMETHOD("mark_symbol", mark_destructured);
- function mark_symbol(process) {
- return process(this);
- }
- AST_SymbolDeclaration.DEFMETHOD("mark_symbol", mark_symbol);
- AST_SymbolRef.DEFMETHOD("mark_symbol", mark_symbol);
- AST_Node.DEFMETHOD("match_symbol", function(predicate) {
- return predicate(this);
- });
- function match_destructured(predicate, ignore_side_effects) {
- var found = false;
- var tw = new TreeWalker(function(node) {
- if (found) return true;
- if (node instanceof AST_DefaultValue) {
- if (!ignore_side_effects) return found = true;
- node.name.walk(tw);
- return true;
- }
- if (node instanceof AST_DestructuredKeyVal) {
- if (!ignore_side_effects && node.key instanceof AST_Node) return found = true;
- node.value.walk(tw);
- return true;
- }
- if (predicate(node)) return found = true;
- });
- this.walk(tw);
- return found;
- }
- AST_DefaultValue.DEFMETHOD("match_symbol", match_destructured);
- AST_Destructured.DEFMETHOD("match_symbol", match_destructured);
- function in_async_generator(scope) {
- return scope instanceof AST_AsyncGeneratorDefun || scope instanceof AST_AsyncGeneratorFunction;
- }
- function find_scope(compressor) {
- var level = 0, node = compressor.self();
- do {
- if (node.variables) return node;
- } while (node = compressor.parent(level++));
- }
- function find_try(compressor, level, node, scope, may_throw, sync) {
- for (var parent; parent = compressor.parent(level++); node = parent) {
- if (parent === scope) return false;
- if (sync && parent instanceof AST_Lambda) {
- if (parent.name || is_async(parent) || is_generator(parent)) return true;
- } else if (parent instanceof AST_Try) {
- if (parent.bfinally && parent.bfinally !== node) return true;
- if (may_throw && parent.bcatch && parent.bcatch !== node) return true;
- }
- }
- return false;
- }
- var identifier_atom = makePredicate("Infinity NaN undefined");
- function is_lhs_read_only(lhs, compressor) {
- if (lhs instanceof AST_Assign) {
- if (lhs.operator != "=") return true;
- if (lhs.right.tail_node().is_constant()) return true;
- return is_lhs_read_only(lhs.left, compressor);
- }
- if (lhs instanceof AST_Atom) return true;
- if (lhs instanceof AST_ObjectIdentity) return true;
- if (lhs instanceof AST_PropAccess) {
- if (lhs.property === "__proto__") return true;
- lhs = lhs.expression;
- if (lhs instanceof AST_SymbolRef) {
- if (lhs.is_immutable()) return false;
- lhs = lhs.fixed_value();
- }
- if (!lhs) return true;
- if (lhs.tail_node().is_constant()) return true;
- return is_lhs_read_only(lhs, compressor);
- }
- if (lhs instanceof AST_SymbolRef) {
- if (lhs.is_immutable()) return true;
- var def = lhs.definition();
- return compressor.exposed(def) && identifier_atom[def.name];
- }
- return false;
- }
- function make_node(ctor, orig, props) {
- if (props) {
- props.start = orig.start;
- props.end = orig.end;
- } else {
- props = orig;
- }
- return new ctor(props);
- }
- function make_sequence(orig, expressions) {
- if (expressions.length == 1) return expressions[0];
- return make_node(AST_Sequence, orig, { expressions: expressions.reduce(merge_sequence, []) });
- }
- function make_node_from_constant(val, orig) {
- switch (typeof val) {
- case "string":
- return make_node(AST_String, orig, { value: val });
- case "number":
- if (isNaN(val)) return make_node(AST_NaN, orig);
- if (isFinite(val)) {
- return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
- operator: "-",
- expression: make_node(AST_Number, orig, { value: -val }),
- }) : make_node(AST_Number, orig, { value: val });
- }
- return val < 0 ? make_node(AST_UnaryPrefix, orig, {
- operator: "-",
- expression: make_node(AST_Infinity, orig),
- }) : make_node(AST_Infinity, orig);
- case "boolean":
- return make_node(val ? AST_True : AST_False, orig);
- case "undefined":
- return make_node(AST_Undefined, orig);
- default:
- if (val === null) {
- return make_node(AST_Null, orig);
- }
- if (val instanceof RegExp) {
- return make_node(AST_RegExp, orig, { value: val });
- }
- throw new Error(string_template("Can't handle constant of type: {type}", { type: typeof val }));
- }
- }
- function needs_unbinding(val) {
- return val instanceof AST_PropAccess
- || is_undeclared_ref(val) && val.name == "eval";
- }
- // we shouldn't compress (1,func)(something) to
- // func(something) because that changes the meaning of
- // the func (becomes lexical instead of global).
- function maintain_this_binding(parent, orig, val) {
- var wrap = false;
- if (parent.TYPE == "Call") {
- wrap = parent.expression === orig && needs_unbinding(val);
- } else if (parent instanceof AST_Template) {
- wrap = parent.tag === orig && needs_unbinding(val);
- } else if (parent instanceof AST_UnaryPrefix) {
- wrap = parent.operator == "delete"
- || parent.operator == "typeof" && is_undeclared_ref(val);
- }
- return wrap ? make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]) : val;
- }
- function merge_expression(base, target) {
- var fixed_by_id = new Dictionary();
- base.walk(new TreeWalker(function(node) {
- if (!(node instanceof AST_SymbolRef)) return;
- var def = node.definition();
- var fixed = node.fixed;
- if (!fixed || !fixed_by_id.has(def.id)) {
- fixed_by_id.set(def.id, fixed);
- } else if (fixed_by_id.get(def.id) !== fixed) {
- fixed_by_id.set(def.id, false);
- }
- }));
- if (fixed_by_id.size() > 0) target.walk(new TreeWalker(function(node) {
- if (!(node instanceof AST_SymbolRef)) return;
- var def = node.definition();
- var fixed = node.fixed;
- if (!fixed || !fixed_by_id.has(def.id)) return;
- if (fixed_by_id.get(def.id) !== fixed) node.fixed = false;
- }));
- return target;
- }
- function merge_sequence(array, node) {
- if (node instanceof AST_Sequence) {
- [].push.apply(array, node.expressions);
- } else {
- array.push(node);
- }
- return array;
- }
- function is_lexical_definition(stat) {
- return stat instanceof AST_Const || stat instanceof AST_DefClass || stat instanceof AST_Let;
- }
- function safe_to_trim(stat) {
- if (stat instanceof AST_LambdaDefinition) {
- var def = stat.name.definition();
- var scope = stat.name.scope;
- if (def.orig.length > 1 && def.scope.resolve() !== scope) return false;
- return def.scope === scope || all(def.references, function(ref) {
- var s = ref.scope;
- do {
- if (s === scope) return true;
- } while (s = s.parent_scope);
- });
- }
- return !is_lexical_definition(stat);
- }
- function as_statement_array(thing) {
- if (thing === null) return [];
- if (thing instanceof AST_BlockStatement) return all(thing.body, safe_to_trim) ? thing.body : [ thing ];
- if (thing instanceof AST_EmptyStatement) return [];
- if (is_statement(thing)) return [ thing ];
- throw new Error("Can't convert thing to statement array");
- }
- function is_empty(thing) {
- if (thing === null) return true;
- if (thing instanceof AST_EmptyStatement) return true;
- if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
- return false;
- }
- function has_declarations_only(block) {
- return all(block.body, function(stat) {
- return is_empty(stat)
- || stat instanceof AST_Defun
- || stat instanceof AST_Var && declarations_only(stat);
- });
- }
- function loop_body(x) {
- if (x instanceof AST_IterationStatement) {
- return x.body instanceof AST_BlockStatement ? x.body : x;
- }
- return x;
- }
- function is_iife_call(node) {
- if (node.TYPE != "Call") return false;
- do {
- node = node.expression;
- } while (node instanceof AST_PropAccess);
- return node instanceof AST_LambdaExpression ? !is_arrow(node) : is_iife_call(node);
- }
- function is_iife_single(call) {
- var exp = call.expression;
- if (exp.name) return false;
- if (!(call instanceof AST_New)) return true;
- var found = false;
- exp.walk(new TreeWalker(function(node) {
- if (found) return true;
- if (node instanceof AST_NewTarget) return found = true;
- if (node instanceof AST_Scope && node !== exp) return true;
- }));
- return !found;
- }
- function is_undeclared_ref(node) {
- return node instanceof AST_SymbolRef && node.definition().undeclared;
- }
- var global_names = makePredicate("Array Boolean clearInterval clearTimeout console Date decodeURI decodeURIComponent encodeURI encodeURIComponent Error escape eval EvalError Function isFinite isNaN JSON Map Math Number parseFloat parseInt RangeError ReferenceError RegExp Object Set setInterval setTimeout String SyntaxError TypeError unescape URIError WeakMap WeakSet");
- AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) {
- return this.defined
- || !this.definition().undeclared
- || compressor.option("unsafe") && global_names[this.name];
- });
- function is_static_field_or_init(prop) {
- return prop.static && prop.value && (prop instanceof AST_ClassField || prop instanceof AST_ClassInit);
- }
- function declarations_only(node) {
- return all(node.definitions, function(var_def) {
- return !var_def.value;
- });
- }
- function is_declaration(stat, lexical) {
- if (stat instanceof AST_DefClass) return lexical && !stat.extends && all(stat.properties, function(prop) {
- if (prop.key instanceof AST_Node) return false;
- return !is_static_field_or_init(prop);
- });
- if (stat instanceof AST_Definitions) return (lexical || stat instanceof AST_Var) && declarations_only(stat);
- if (stat instanceof AST_ExportDeclaration) return is_declaration(stat.body, lexical);
- if (stat instanceof AST_ExportDefault) return is_declaration(stat.body, lexical);
- return stat instanceof AST_LambdaDefinition;
- }
- function is_last_statement(body, stat) {
- var index = body.lastIndexOf(stat);
- if (index < 0) return false;
- while (++index < body.length) {
- if (!is_declaration(body[index], true)) return false;
- }
- return true;
- }
- // Certain combination of unused name + side effect leads to invalid AST:
- // https://github.com/mishoo/UglifyJS/issues/44
- // https://github.com/mishoo/UglifyJS/issues/1838
- // https://github.com/mishoo/UglifyJS/issues/3371
- // We fix it at this stage by moving the `var` outside the `for`.
- function patch_for_init(node, in_list) {
- var block;
- if (node.init instanceof AST_BlockStatement) {
- block = node.init;
- node.init = block.body.pop();
- block.body.push(node);
- }
- if (node.init instanceof AST_Defun) {
- if (!block) block = make_node(AST_BlockStatement, node, { body: [ node ] });
- block.body.splice(-1, 0, node.init);
- node.init = null;
- } else if (node.init instanceof AST_SimpleStatement) {
- node.init = node.init.body;
- } else if (is_empty(node.init)) {
- node.init = null;
- }
- if (!block) return;
- return in_list ? List.splice(block.body) : block;
- }
- function tighten_body(statements, compressor) {
- var in_lambda = last_of(compressor, function(node) {
- return node instanceof AST_Lambda;
- });
- var block_scope, iife_in_try, in_iife_single, in_loop, in_try, scope;
- find_loop_scope_try();
- var changed, last_changed, max_iter = 10;
- do {
- last_changed = changed;
- changed = 0;
- if (eliminate_spurious_blocks(statements)) changed = 1;
- if (!changed && last_changed == 1) break;
- if (compressor.option("dead_code")) {
- if (eliminate_dead_code(statements, compressor)) changed = 2;
- if (!changed && last_changed == 2) break;
- }
- if (compressor.option("if_return")) {
- if (handle_if_return(statements, compressor)) changed = 3;
- if (!changed && last_changed == 3) break;
- }
- if (compressor.option("awaits") && compressor.option("side_effects")) {
- if (trim_awaits(statements, compressor)) changed = 4;
- if (!changed && last_changed == 4) break;
- }
- if (compressor.option("inline") >= 4) {
- if (inline_iife(statements, compressor)) changed = 5;
- if (!changed && last_changed == 5) break;
- }
- if (compressor.sequences_limit > 0) {
- if (sequencesize(statements, compressor)) changed = 6;
- if (!changed && last_changed == 6) break;
- if (sequencesize_2(statements, compressor)) changed = 7;
- if (!changed && last_changed == 7) break;
- }
- if (compressor.option("join_vars")) {
- if (join_consecutive_vars(statements)) changed = 8;
- if (!changed && last_changed == 8) break;
- }
- if (compressor.option("collapse_vars")) {
- if (collapse(statements, compressor)) changed = 9;
- }
- } while (changed && max_iter-- > 0);
- return statements;
- function last_of(compressor, predicate) {
- var block = compressor.self(), level = 0, stat;
- do {
- if (block instanceof AST_Catch) {
- block = compressor.parent(level++);
- } else if (block instanceof AST_LabeledStatement) {
- block = block.body;
- } else if (block instanceof AST_SwitchBranch) {
- var branches = compressor.parent(level);
- if (branches.body[branches.body.length - 1] === block || has_break(block.body)) {
- level++;
- block = branches;
- }
- }
- do {
- stat = block;
- if (predicate(stat)) return stat;
- block = compressor.parent(level++);
- } while (block instanceof AST_If);
- } while (stat
- && (block instanceof AST_BlockStatement
- || block instanceof AST_Catch
- || block instanceof AST_Scope
- || block instanceof AST_SwitchBranch
- || block instanceof AST_Try)
- && is_last_statement(block.body, stat));
- function has_break(stats) {
- for (var i = stats.length; --i >= 0;) {
- if (stats[i] instanceof AST_Break) return true;
- }
- return false;
- }
- }
- function find_loop_scope_try() {
- var node = compressor.self(), level = 0;
- do {
- if (!block_scope && node.variables) block_scope = node;
- if (node instanceof AST_Catch) {
- if (compressor.parent(level).bfinally) {
- if (!in_try) in_try = {};
- in_try.bfinally = true;
- }
- level++;
- } else if (node instanceof AST_Finally) {
- level++;
- } else if (node instanceof AST_IterationStatement) {
- in_loop = true;
- } else if (node instanceof AST_Scope) {
- scope = node;
- break;
- } else if (node instanceof AST_Try) {
- if (!in_try) in_try = {};
- if (node.bcatch) in_try.bcatch = true;
- if (node.bfinally) in_try.bfinally = true;
- }
- } while (node = compressor.parent(level++));
- }
- // Search from right to left for assignment-like expressions:
- // - `var a = x;`
- // - `a = x;`
- // - `++a`
- // For each candidate, scan from left to right for first usage, then try
- // to fold assignment into the site for compression.
- // Will not attempt to collapse assignments into or past code blocks
- // which are not sequentially executed, e.g. loops and conditionals.
- function collapse(statements, compressor) {
- if (scope.pinned()) return;
- var args;
- var assignments = new Dictionary();
- var candidates = [];
- var changed = false;
- var declare_only = new Dictionary();
- var force_single;
- var stat_index = statements.length;
- var scanner = new TreeTransformer(function(node, descend) {
- if (abort) return node;
- // Skip nodes before `candidate` as quickly as possible
- if (!hit) {
- if (node !== hit_stack[hit_index]) return node;
- hit_index++;
- if (hit_index < hit_stack.length) return handle_custom_scan_order(node, scanner);
- hit = true;
- stop_after = (value_def ? find_stop_value : find_stop)(node, 0);
- if (stop_after === node) abort = true;
- return node;
- }
- var parent = scanner.parent();
- // Stop only if candidate is found within conditional branches
- if (!stop_if_hit && in_conditional(node, parent)) {
- stop_if_hit = parent;
- }
- // Cascade compound assignments
- if (compound && scan_lhs && can_replace && !stop_if_hit
- && node instanceof AST_Assign && node.operator != "=" && node.left.equals(lhs)) {
- replaced++;
- changed = true;
- AST_Node.info("Cascading {this} [{start}]", node);
- can_replace = false;
- lvalues = get_lvalues(lhs);
- node.right.transform(scanner);
- clear_write_only(candidate);
- var folded;
- if (abort) {
- folded = candidate;
- } else {
- abort = true;
- folded = make_node(AST_Binary, candidate, {
- operator: compound,
- left: lhs.fixed && lhs.definition().fixed ? lhs.fixed.to_binary(candidate) : lhs,
- right: rvalue,
- });
- }
- return make_node(AST_Assign, node, {
- operator: "=",
- left: node.left,
- right: make_node(AST_Binary, node, {
- operator: node.operator.slice(0, -1),
- left: folded,
- right: node.right,
- }),
- });
- }
- // Stop immediately if these node types are encountered
- if (should_stop(node, parent)) {
- abort = true;
- return node;
- }
- // Skip transient nodes caused by single-use variable replacement
- if (node.single_use) return node;
- // Replace variable with assignment when found
- var hit_rhs;
- if (!(node instanceof AST_SymbolDeclaration)
- && (scan_lhs && lhs.equals(node)
- || scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
- if (!can_replace || stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
- if (!hit_rhs && !value_def) abort = true;
- return node;
- }
- if (is_lhs(node, parent)) {
- if (value_def && !hit_rhs) assign_used = true;
- return node;
- }
- if (!hit_rhs && verify_ref && node.fixed !== lhs.fixed) {
- abort = true;
- return node;
- }
- if (value_def) {
- if (stop_if_hit && assign_pos == 0) assign_pos = remaining - replaced;
- if (!hit_rhs) replaced++;
- return node;
- }
- replaced++;
- changed = abort = true;
- AST_Node.info("Collapsing {this} [{start}]", node);
- if (candidate.TYPE == "Binary") {
- update_symbols(candidate, node);
- return make_node(AST_Assign, candidate, {
- operator: "=",
- left: candidate.right.left,
- right: candidate.operator == "&&" ? make_node(AST_Conditional, candidate, {
- condition: candidate.left,
- consequent: candidate.right.right,
- alternative: node,
- }) : make_node(AST_Conditional, candidate, {
- condition: candidate.left,
- consequent: node,
- alternative: candidate.right.right,
- }),
- });
- }
- if (candidate instanceof AST_UnaryPostfix) return make_node(AST_UnaryPrefix, candidate, {
- operator: candidate.operator,
- expression: lhs.fixed && lhs.definition().fixed ? lhs.fixed.to_prefix(candidate) : lhs,
- });
- if (candidate instanceof AST_UnaryPrefix) {
- clear_write_only(candidate);
- return candidate;
- }
- update_symbols(rvalue, node);
- if (candidate instanceof AST_VarDef) {
- var def = candidate.name.definition();
- if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
- def.replaced++;
- return maintain_this_binding(parent, node, rvalue);
- }
- return make_node(AST_Assign, candidate, {
- operator: "=",
- left: node,
- right: rvalue,
- });
- }
- clear_write_only(rvalue);
- var assign = candidate.clone();
- assign.right = rvalue;
- return assign;
- }
- // Stop signals related to AST_SymbolRef
- if (should_stop_ref(node, parent)) {
- abort = true;
- return node;
- }
- // These node types have child nodes that execute sequentially,
- // but are otherwise not safe to scan into or beyond them.
- if (is_last_node(node, parent) || may_throw(node)) {
- stop_after = node;
- if (node instanceof AST_Scope) abort = true;
- }
- // Scan but don't replace inside getter/setter
- if (node instanceof AST_Accessor) {
- var replace = can_replace;
- can_replace = false;
- descend(node, scanner);
- can_replace = replace;
- return signal_abort(node);
- }
- // Scan but don't replace inside destructuring expression
- if (node instanceof AST_Destructured) {
- var replace = can_replace;
- can_replace = false;
- descend(node, scanner);
- can_replace = replace;
- return signal_abort(node);
- }
- // Scan but don't replace inside default value
- if (node instanceof AST_DefaultValue) {
- node.name = node.name.transform(scanner);
- var replace = can_replace;
- can_replace = false;
- node.value = node.value.transform(scanner);
- can_replace = replace;
- return signal_abort(node);
- }
- // Scan but don't replace inside block scope with colliding variable
- if (node instanceof AST_BlockScope
- && !(node instanceof AST_Scope)
- && !(node.variables && node.variables.all(function(def) {
- return !enclosed.has(def.name) && !lvalues.has(def.name);
- }))) {
- var replace = can_replace;
- can_replace = false;
- if (!handle_custom_scan_order(node, scanner)) descend(node, scanner);
- can_replace = replace;
- return signal_abort(node);
- }
- if (handle_custom_scan_order(node, scanner)) return signal_abort(node);
- }, signal_abort);
- var multi_replacer = new TreeTransformer(function(node) {
- if (abort) return node;
- // Skip nodes before `candidate` as quickly as possible
- if (!hit) {
- if (node !== hit_stack[hit_index]) return node;
- hit_index++;
- switch (hit_stack.length - hit_index) {
- case 0:
- hit = true;
- if (assign_used) return node;
- if (node !== candidate) return node;
- if (node instanceof AST_VarDef) return node;
- def.replaced++;
- var parent = multi_replacer.parent();
- if (parent instanceof AST_Sequence && parent.tail_node() !== node) {
- value_def.replaced++;
- if (rvalue === rhs_value) return List.skip;
- return make_sequence(rhs_value, rhs_value.expressions.slice(0, -1));
- }
- return rvalue;
- case 1:
- if (!assign_used && node.body === candidate) {
- hit = true;
- def.replaced++;
- value_def.replaced++;
- return null;
- }
- default:
- return handle_custom_scan_order(node, multi_replacer);
- }
- }
- // Replace variable when found
- if (node instanceof AST_SymbolRef && node.definition() === def) {
- if (is_lhs(node, multi_replacer.parent())) return node;
- if (!--replaced) abort = true;
- AST_Node.info("Replacing {this} [{start}]", node);
- var ref = rvalue.clone();
- ref.scope = node.scope;
- ref.reference();
- if (replaced == assign_pos) {
- abort = true;
- return make_node(AST_Assign, candidate, {
- operator: "=",
- left: node,
- right: ref,
- });
- }
- def.replaced++;
- return ref;
- }
- // Skip (non-executed) functions and (leading) default case in switch statements
- if (node instanceof AST_Default || node instanceof AST_Scope) return node;
- }, function(node) {
- return patch_sequence(node, multi_replacer);
- });
- while (--stat_index >= 0) {
- // Treat parameters as collapsible in IIFE, i.e.
- // function(a, b){ ... }(x());
- // would be translated into equivalent assignments:
- // var a = x(), b = undefined;
- if (stat_index == 0 && compressor.option("unused")) extract_args();
- // Find collapsible assignments
- var hit_stack = [];
- extract_candidates(statements[stat_index]);
- while (candidates.length > 0) {
- hit_stack = candidates.pop();
- var hit_index = 0;
- var candidate = hit_stack[hit_stack.length - 1];
- var assign_pos = -1;
- var assign_used = false;
- var verify_ref = false;
- var remaining;
- var value_def = null;
- var stop_after = null;
- var stop_if_hit = null;
- var lhs = get_lhs(candidate);
- var side_effects = lhs && lhs.has_side_effects(compressor);
- var scan_lhs = lhs && (!side_effects || lhs instanceof AST_SymbolRef)
- && !is_lhs_read_only(lhs, compressor);
- var scan_rhs = foldable(candidate);
- if (!scan_lhs && !scan_rhs) continue;
- var compound = candidate instanceof AST_Assign && candidate.operator.slice(0, -1);
- var funarg = candidate.name instanceof AST_SymbolFunarg;
- var may_throw = return_false;
- if (candidate.may_throw(compressor)) {
- if (funarg && is_async(scope)) continue;
- may_throw = in_try ? function(node) {
- return node.has_side_effects(compressor);
- } : side_effects_external;
- }
- var read_toplevel = false;
- var modify_toplevel = false;
- var defun_scopes = get_defun_scopes(lhs);
- // Locate symbols which may execute code outside of scanning range
- var enclosed = new Dictionary();
- var well_defined = true;
- var lvalues = get_lvalues(candidate);
- var lhs_local = is_lhs_local(lhs);
- var rhs_value = get_rvalue(candidate);
- var rvalue = rhs_value;
- if (!side_effects) {
- if (!compound && rvalue instanceof AST_Sequence) rvalue = rvalue.tail_node();
- side_effects = value_has_side_effects();
- }
- var check_destructured = in_try || !lhs_local ? function(node) {
- return node instanceof AST_Destructured;
- } : return_false;
- var replace_all = replace_all_symbols(candidate);
- var hit = funarg;
- var abort = false;
- var replaced = 0;
- var can_replace = !args || !hit;
- if (!can_replace) {
- for (var j = candidate.arg_index + 1; !abort && j < args.length; j++) {
- if (args[j]) args[j].transform(scanner);
- }
- can_replace = true;
- }
- for (var i = stat_index; !abort && i < statements.length; i++) {
- statements[i].transform(scanner);
- }
- if (value_def) {
- if (!replaced || remaining > replaced + assign_used) {
- candidates.push(hit_stack);
- force_single = true;
- continue;
- }
- if (replaced == assign_pos) assign_used = true;
- var def = lhs.definition();
- abort = false;
- hit_index = 0;
- hit = funarg;
- for (var i = stat_index; !abort && i < statements.length; i++) {
- if (!statements[i].transform(multi_replacer)) statements.splice(i--, 1);
- }
- replaced = candidate instanceof AST_VarDef
- && candidate === hit_stack[hit_stack.length - 1]
- && def.references.length == def.replaced
- && !compressor.exposed(def);
- value_def.last_ref = false;
- value_def.single_use = false;
- changed = true;
- }
- if (replaced) remove_candidate(candidate);
- }
- }
- return changed;
- function signal_abort(node) {
- if (abort) return node;
- if (stop_after === node) abort = true;
- if (stop_if_hit === node) stop_if_hit = null;
- return node;
- }
- function handle_custom_scan_order(node, tt) {
- if (!(node instanceof AST_BlockScope)) return;
- // Skip (non-executed) functions
- if (node instanceof AST_Scope) return node;
- // Scan computed keys, static fields & initializers in class
- if (node instanceof AST_Class) {
- var replace = can_replace;
- can_replace = false;
- if (node.name) node.name.transform(tt);
- if (!abort && node.extends) node.extends.transform(tt);
- var fields = [], stats = [];
- for (var i = 0; !abort && i < node.properties.length; i++) {
- var prop = node.properties[i];
- if (prop.key instanceof AST_Node) prop.key = prop.key.transform(tt);
- if (!prop.static) continue;
- if (prop instanceof AST_ClassField) {
- if (prop.value) fields.push(prop);
- } else if (prop instanceof AST_ClassInit) {
- [].push.apply(stats, prop.value.body);
- }
- }
- for (var i = 0; !abort && i < stats.length; i++) {
- stats[i].transform(tt);
- }
- for (var i = 0; !abort && i < fields.length; i++) {
- fields[i].value.transform(tt);
- }
- can_replace = replace;
- return node;
- }
- // Scan object only in a for-in/of statement
- if (node instanceof AST_ForEnumeration) {
- node.object = node.object.transform(tt);
- abort = true;
- return node;
- }
- // Scan first case expression only in a switch statement
- if (node instanceof AST_Switch) {
- node.expression = node.expression.transform(tt);
- for (var i = 0; !abort && i < node.body.length; i++) {
- var branch = node.body[i];
- if (branch instanceof AST_Case) {
- if (!hit) {
- if (branch !== hit_stack[hit_index]) continue;
- hit_index++;
- }
- branch.expression = branch.expression.transform(tt);
- if (!replace_all || verify_ref) break;
- scan_rhs = false;
- }
- }
- abort = true;
- return node;
- }
- }
- function is_direct_assignment(node, parent) {
- if (parent instanceof AST_Assign) return parent.operator == "=" && parent.left === node;
- if (parent instanceof AST_DefaultValue) return parent.name === node;
- if (parent instanceof AST_DestructuredArray) return true;
- if (parent instanceof AST_DestructuredKeyVal) return parent.value === node;
- }
- function should_stop(node, parent) {
- if (node === rvalue) return true;
- if (parent instanceof AST_For) {
- if (node !== parent.init) return true;
- }
- if (node instanceof AST_Assign) return node.operator != "=" && lhs.equals(node.left);
- if (node instanceof AST_BlockStatement) {
- return defun_scopes && !all(defun_scopes, function(scope) {
- return node !== scope;
- });
- }
- if (node instanceof AST_Call) {
- if (!(lhs instanceof AST_PropAccess)) return false;
- if (!lhs.equals(node.expression)) return false;
- return !(rvalue instanceof AST_LambdaExpression && !rvalue.contains_this());
- }
- if (node instanceof AST_Class) return !compressor.has_directive("use strict");
- if (node instanceof AST_Debugger) return true;
- if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
- if (node instanceof AST_DestructuredKeyVal) return node.key instanceof AST_Node;
- if (node instanceof AST_DWLoop) return true;
- if (node instanceof AST_LoopControl) return true;
- if (node instanceof AST_Try) return true;
- if (node instanceof AST_With) return true;
- return false;
- }
- function should_stop_ref(node, parent) {
- if (!(node instanceof AST_SymbolRef)) return false;
- if (node.is_declared(compressor)) {
- if (node.fixed_value()) return false;
- if (can_drop_symbol(node)) {
- return !(parent instanceof AST_PropAccess && parent.expression === node)
- && is_arguments(node.definition());
- }
- } else if (is_direct_assignment(node, parent)) {
- return false;
- }
- if (!replace_all) return true;
- scan_rhs = false;
- return false;
- }
- function in_conditional(node, parent) {
- if (parent instanceof AST_Assign) return parent.left !== node && lazy_op[parent.operator.slice(0, -1)];
- if (parent instanceof AST_Binary) return parent.left !== node && lazy_op[parent.operator];
- if (parent instanceof AST_Call) return parent.optional && parent.expression !== node;
- if (parent instanceof AST_Case) return parent.expression !== node;
- if (parent instanceof AST_Conditional) return parent.condition !== node;
- if (parent instanceof AST_If) return parent.condition !== node;
- if (parent instanceof AST_Sub) return parent.optional && parent.expression !== node;
- }
- function is_last_node(node, parent) {
- if (node instanceof AST_Await) return true;
- if (node.TYPE == "Binary") return !can_drop_op(node, compressor);
- if (node instanceof AST_Call) {
- var def, fn = node.expression;
- if (fn instanceof AST_SymbolRef) {
- def = fn.definition();
- fn = fn.fixed_value();
- }
- if (!(fn instanceof AST_Lambda)) return !node.is_expr_pure(compressor);
- if (def && recursive_ref(compressor, def, fn)) return true;
- if (fn.collapse_scanning) return false;
- fn.collapse_scanning = true;
- var replace = can_replace;
- can_replace = false;
- var after = stop_after;
- var if_hit = stop_if_hit;
- for (var i = 0; !abort && i < fn.argnames.length; i++) {
- if (arg_may_throw(reject, fn.argnames[i], node.args[i])) abort = true;
- }
- if (!abort) {
- if (fn.rest && arg_may_throw(reject, fn.rest, make_node(AST_Array, node, {
- elements: node.args.slice(i),
- }))) {
- abort = true;
- } else if (is_arrow(fn) && fn.value) {
- fn.value.transform(scanner);
- } else for (var i = 0; !abort && i < fn.body.length; i++) {
- var stat = fn.body[i];
- if (stat instanceof AST_Return) {
- if (stat.value) stat.value.transform(scanner);
- break;
- }
- stat.transform(scanner);
- }
- }
- stop_if_hit = if_hit;
- stop_after = after;
- can_replace = replace;
- fn.collapse_scanning = false;
- if (!abort) return false;
- abort = false;
- return true;
- }
- if (node instanceof AST_Class) {
- if (!in_try) return false;
- var base = node.extends;
- if (!base) return false;
- if (base instanceof AST_SymbolRef) base = base.fixed_value();
- return !safe_for_extends(base);
- }
- if (node instanceof AST_Exit) {
- if (in_try) {
- if (in_try.bfinally) return true;
- if (in_try.bcatch && node instanceof AST_Throw) return true;
- }
- return side_effects || lhs instanceof AST_PropAccess || may_modify(lhs);
- }
- if (node instanceof AST_Function) {
- return compressor.option("ie") && node.name && lvalues.has(node.name.name);
- }
- if (node instanceof AST_ObjectIdentity) return symbol_in_lvalues(node);
- if (node instanceof AST_PropAccess) {
- var exp = node.expression;
- if (compressor.option("unsafe")) {
- if (is_undeclared_ref(exp) && global_names[exp.name]) return false;
- if (is_static_fn(exp)) return false;
- }
- if (exp instanceof AST_SymbolRef && is_arguments(exp.definition())) return true;
- if (side_effects) return true;
- if (!well_defined) return true;
- if (value_def) return false;
- if (!in_try && lhs_local) return false;
- if (node.optional) return false;
- return exp.may_throw_on_access(compressor);
- }
- if (node instanceof AST_Spread) return true;
- if (node instanceof AST_SymbolRef) {
- var assign_direct = symbol_in_lvalues(node);
- if (is_undeclared_ref(node) && node.is_declared(compressor)) return false;
- if (assign_direct) return !is_direct_assignment(node, parent);
- if (side_effects && may_modify(node)) return true;
- var def = node.definition();
- return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node);
- }
- if (node instanceof AST_Template) return !node.is_expr_pure(compressor);
- if (node instanceof AST_VarDef) {
- if (check_destructured(node.name)) return true;
- return (node.value || parent instanceof AST_Let) && node.name.match_symbol(function(node) {
- return node instanceof AST_SymbolDeclaration
- && (lvalues.has(node.name) || side_effects && may_modify(node));
- }, true);
- }
- if (node instanceof AST_Y
|