PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB`Mini Shell

HOME


Mini Shell 1.0
DIR:/home/htlwork.com/www/himsaral/node_modules/terser/lib/compress/
Upload File :
Current File : //home/htlwork.com/www/himsaral/node_modules/terser/lib/compress/inference.js
/***********************************************************************

  A JavaScript tokenizer / parser / beautifier / compressor.
  https://github.com/mishoo/UglifyJS2

  -------------------------------- (C) ---------------------------------

                           Author: Mihai Bazon
                         <mihai.bazon@gmail.com>
                       http://mihai.bazon.net/blog

  Distributed under the BSD license:

    Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:

        * Redistributions of source code must retain the above
          copyright notice, this list of conditions and the following
          disclaimer.

        * Redistributions in binary form must reproduce the above
          copyright notice, this list of conditions and the following
          disclaimer in the documentation and/or other materials
          provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    SUCH DAMAGE.

 ***********************************************************************/

import {
  AST_Array,
  AST_Arrow,
  AST_Assign,
  AST_Binary,
  AST_Block,
  AST_BlockStatement,
  AST_Call,
  AST_Case,
  AST_Chain,
  AST_Class,
  AST_DefClass,
  AST_ClassStaticBlock,
  AST_ClassProperty,
  AST_ConciseMethod,
  AST_Conditional,
  AST_Constant,
  AST_Definitions,
  AST_Dot,
  AST_EmptyStatement,
  AST_Expansion,
  AST_False,
  AST_Function,
  AST_If,
  AST_Import,
  AST_Jump,
  AST_LabeledStatement,
  AST_Lambda,
  AST_New,
  AST_Node,
  AST_Null,
  AST_Number,
  AST_Object,
  AST_ObjectGetter,
  AST_ObjectKeyVal,
  AST_ObjectProperty,
  AST_ObjectSetter,
  AST_PropAccess,
  AST_RegExp,
  AST_Return,
  AST_Sequence,
  AST_SimpleStatement,
  AST_Statement,
  AST_String,
  AST_Sub,
  AST_Switch,
  AST_SwitchBranch,
  AST_SymbolClassProperty,
  AST_SymbolDeclaration,
  AST_SymbolRef,
  AST_TemplateSegment,
  AST_TemplateString,
  AST_This,
  AST_Toplevel,
  AST_True,
  AST_Try,
  AST_Unary,
  AST_UnaryPostfix,
  AST_UnaryPrefix,
  AST_Undefined,
  AST_VarDef,

  TreeTransformer,
  walk,
  walk_abort,

  _PURE
} from "../ast.js";
import {
    makePredicate,
    return_true,
    return_false,
    return_null,
    return_this,
    make_node,
    member,
    noop,
    has_annotation,
    HOP
} from "../utils/index.js";
import { make_node_from_constant, make_sequence, best_of_expression, read_property } from "./common.js";

import { INLINED, UNDEFINED, has_flag } from "./compressor-flags.js";
import { pure_prop_access_globals, is_pure_native_fn, is_pure_native_method } from "./native-objects.js";

// Functions and methods to infer certain facts about expressions
// It's not always possible to be 100% sure about something just by static analysis,
// so `true` means yes, and `false` means maybe

export const is_undeclared_ref = (node) =>
    node instanceof AST_SymbolRef && node.definition().undeclared;

export const lazy_op = makePredicate("&& || ??");
export const unary_side_effects = makePredicate("delete ++ --");

// methods to determine whether an expression has a boolean result type
(function(def_is_boolean) {
    const unary_bool = makePredicate("! delete");
    const binary_bool = makePredicate("in instanceof == != === !== < <= >= >");
    def_is_boolean(AST_Node, return_false);
    def_is_boolean(AST_UnaryPrefix, function() {
        return unary_bool.has(this.operator);
    });
    def_is_boolean(AST_Binary, function() {
        return binary_bool.has(this.operator)
            || lazy_op.has(this.operator)
                && this.left.is_boolean()
                && this.right.is_boolean();
    });
    def_is_boolean(AST_Conditional, function() {
        return this.consequent.is_boolean() && this.alternative.is_boolean();
    });
    def_is_boolean(AST_Assign, function() {
        return this.operator == "=" && this.right.is_boolean();
    });
    def_is_boolean(AST_Sequence, function() {
        return this.tail_node().is_boolean();
    });
    def_is_boolean(AST_True, return_true);
    def_is_boolean(AST_False, return_true);
})(function(node, func) {
    node.DEFMETHOD("is_boolean", func);
});

// methods to determine if an expression has a numeric result type
(function(def_is_number) {
    def_is_number(AST_Node, return_false);
    def_is_number(AST_Number, return_true);
    const unary = makePredicate("+ - ~ ++ --");
    def_is_number(AST_Unary, function() {
        return unary.has(this.operator);
    });
    const numeric_ops = makePredicate("- * / % & | ^ << >> >>>");
    def_is_number(AST_Binary, function(compressor) {
        return numeric_ops.has(this.operator) || this.operator == "+"
            && this.left.is_number(compressor)
            && this.right.is_number(compressor);
    });
    def_is_number(AST_Assign, function(compressor) {
        return numeric_ops.has(this.operator.slice(0, -1))
            || this.operator == "=" && this.right.is_number(compressor);
    });
    def_is_number(AST_Sequence, function(compressor) {
        return this.tail_node().is_number(compressor);
    });
    def_is_number(AST_Conditional, function(compressor) {
        return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
    });
})(function(node, func) {
    node.DEFMETHOD("is_number", func);
});

// methods to determine if an expression has a string result type
(function(def_is_string) {
    def_is_string(AST_Node, return_false);
    def_is_string(AST_String, return_true);
    def_is_string(AST_TemplateString, return_true);
    def_is_string(AST_UnaryPrefix, function() {
        return this.operator == "typeof";
    });
    def_is_string(AST_Binary, function(compressor) {
        return this.operator == "+" &&
            (this.left.is_string(compressor) || this.right.is_string(compressor));
    });
    def_is_string(AST_Assign, function(compressor) {
        return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
    });
    def_is_string(AST_Sequence, function(compressor) {
        return this.tail_node().is_string(compressor);
    });
    def_is_string(AST_Conditional, function(compressor) {
        return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
    });
})(function(node, func) {
    node.DEFMETHOD("is_string", func);
});

export function is_undefined(node, compressor) {
    return (
        has_flag(node, UNDEFINED)
        || node instanceof AST_Undefined
        || node instanceof AST_UnaryPrefix
            && node.operator == "void"
            && !node.expression.has_side_effects(compressor)
    );
}

// Is the node explicitly null or undefined.
function is_null_or_undefined(node, compressor) {
    let fixed;
    return (
        node instanceof AST_Null
        || is_undefined(node, compressor)
        || (
            node instanceof AST_SymbolRef
            && (fixed = node.definition().fixed) instanceof AST_Node
            && is_nullish(fixed, compressor)
        )
    );
}

// Find out if this expression is optionally chained from a base-point that we
// can statically analyze as null or undefined.
export function is_nullish_shortcircuited(node, compressor) {
    if (node instanceof AST_PropAccess || node instanceof AST_Call) {
        return (
            (node.optional && is_null_or_undefined(node.expression, compressor))
            || is_nullish_shortcircuited(node.expression, compressor)
        );
    }
    if (node instanceof AST_Chain) return is_nullish_shortcircuited(node.expression, compressor);
    return false;
}

// Find out if something is == null, or can short circuit into nullish.
// Used to optimize ?. and ??
export function is_nullish(node, compressor) {
    if (is_null_or_undefined(node, compressor)) return true;
    return is_nullish_shortcircuited(node, compressor);
}

// Determine if expression might cause side effects
// If there's a possibility that a node may change something when it's executed, this returns true
(function(def_has_side_effects) {
    def_has_side_effects(AST_Node, return_true);

    def_has_side_effects(AST_EmptyStatement, return_false);
    def_has_side_effects(AST_Constant, return_false);
    def_has_side_effects(AST_This, return_false);

    function any(list, compressor) {
        for (var i = list.length; --i >= 0;)
            if (list[i].has_side_effects(compressor))
                return true;
        return false;
    }

    def_has_side_effects(AST_Block, function(compressor) {
        return any(this.body, compressor);
    });
    def_has_side_effects(AST_Call, function(compressor) {
        if (
            !this.is_callee_pure(compressor)
            && (!this.expression.is_call_pure(compressor)
                || this.expression.has_side_effects(compressor))
        ) {
            return true;
        }
        return any(this.args, compressor);
    });
    def_has_side_effects(AST_Switch, function(compressor) {
        return this.expression.has_side_effects(compressor)
            || any(this.body, compressor);
    });
    def_has_side_effects(AST_Case, function(compressor) {
        return this.expression.has_side_effects(compressor)
            || any(this.body, compressor);
    });
    def_has_side_effects(AST_Try, function(compressor) {
        return any(this.body, compressor)
            || this.bcatch && this.bcatch.has_side_effects(compressor)
            || this.bfinally && this.bfinally.has_side_effects(compressor);
    });
    def_has_side_effects(AST_If, function(compressor) {
        return this.condition.has_side_effects(compressor)
            || this.body && this.body.has_side_effects(compressor)
            || this.alternative && this.alternative.has_side_effects(compressor);
    });
    def_has_side_effects(AST_LabeledStatement, function(compressor) {
        return this.body.has_side_effects(compressor);
    });
    def_has_side_effects(AST_SimpleStatement, function(compressor) {
        return this.body.has_side_effects(compressor);
    });
    def_has_side_effects(AST_Lambda, return_false);
    def_has_side_effects(AST_Class, function (compressor) {
        if (this.extends && this.extends.has_side_effects(compressor)) {
            return true;
        }
        return any(this.properties, compressor);
    });
    def_has_side_effects(AST_ClassStaticBlock, function(compressor) {
        return any(this.body, compressor);
    });
    def_has_side_effects(AST_Binary, function(compressor) {
        return this.left.has_side_effects(compressor)
            || this.right.has_side_effects(compressor);
    });
    def_has_side_effects(AST_Assign, return_true);
    def_has_side_effects(AST_Conditional, function(compressor) {
        return this.condition.has_side_effects(compressor)
            || this.consequent.has_side_effects(compressor)
            || this.alternative.has_side_effects(compressor);
    });
    def_has_side_effects(AST_Unary, function(compressor) {
        return unary_side_effects.has(this.operator)
            || this.expression.has_side_effects(compressor);
    });
    def_has_side_effects(AST_SymbolRef, function(compressor) {
        return !this.is_declared(compressor) && !pure_prop_access_globals.has(this.name);
    });
    def_has_side_effects(AST_SymbolClassProperty, return_false);
    def_has_side_effects(AST_SymbolDeclaration, return_false);
    def_has_side_effects(AST_Object, function(compressor) {
        return any(this.properties, compressor);
    });
    def_has_side_effects(AST_ObjectProperty, function(compressor) {
        return (
            this.computed_key() && this.key.has_side_effects(compressor)
            || this.value && this.value.has_side_effects(compressor)
        );
    });
    def_has_side_effects(AST_ClassProperty, function(compressor) {
        return (
            this.computed_key() && this.key.has_side_effects(compressor)
            || this.static && this.value && this.value.has_side_effects(compressor)
        );
    });
    def_has_side_effects(AST_ConciseMethod, function(compressor) {
        return this.computed_key() && this.key.has_side_effects(compressor);
    });
    def_has_side_effects(AST_ObjectGetter, function(compressor) {
        return this.computed_key() && this.key.has_side_effects(compressor);
    });
    def_has_side_effects(AST_ObjectSetter, function(compressor) {
        return this.computed_key() && this.key.has_side_effects(compressor);
    });
    def_has_side_effects(AST_Array, function(compressor) {
        return any(this.elements, compressor);
    });
    def_has_side_effects(AST_Dot, function(compressor) {
        if (is_nullish(this, compressor)) return false;
        return !this.optional && this.expression.may_throw_on_access(compressor)
            || this.expression.has_side_effects(compressor);
    });
    def_has_side_effects(AST_Sub, function(compressor) {
        if (is_nullish(this, compressor)) return false;

        return !this.optional && this.expression.may_throw_on_access(compressor)
            || this.expression.has_side_effects(compressor)
            || this.property.has_side_effects(compressor);
    });
    def_has_side_effects(AST_Chain, function (compressor) {
        return this.expression.has_side_effects(compressor);
    });
    def_has_side_effects(AST_Sequence, function(compressor) {
        return any(this.expressions, compressor);
    });
    def_has_side_effects(AST_Definitions, function(compressor) {
        return any(this.definitions, compressor);
    });
    def_has_side_effects(AST_VarDef, function() {
        return this.value;
    });
    def_has_side_effects(AST_TemplateSegment, return_false);
    def_has_side_effects(AST_TemplateString, function(compressor) {
        return any(this.segments, compressor);
    });
})(function(node, func) {
    node.DEFMETHOD("has_side_effects", func);
});

// determine if expression may throw
(function(def_may_throw) {
    def_may_throw(AST_Node, return_true);

    def_may_throw(AST_Constant, return_false);
    def_may_throw(AST_EmptyStatement, return_false);
    def_may_throw(AST_Lambda, return_false);
    def_may_throw(AST_SymbolDeclaration, return_false);
    def_may_throw(AST_This, return_false);

    function any(list, compressor) {
        for (var i = list.length; --i >= 0;)
            if (list[i].may_throw(compressor))
                return true;
        return false;
    }

    def_may_throw(AST_Class, function(compressor) {
        if (this.extends && this.extends.may_throw(compressor)) return true;
        return any(this.properties, compressor);
    });
    def_may_throw(AST_ClassStaticBlock, function (compressor) {
        return any(this.body, compressor);
    });

    def_may_throw(AST_Array, function(compressor) {
        return any(this.elements, compressor);
    });
    def_may_throw(AST_Assign, function(compressor) {
        if (this.right.may_throw(compressor)) return true;
        if (!compressor.has_directive("use strict")
            && this.operator == "="
            && this.left instanceof AST_SymbolRef) {
            return false;
        }
        return this.left.may_throw(compressor);
    });
    def_may_throw(AST_Binary, function(compressor) {
        return this.left.may_throw(compressor)
            || this.right.may_throw(compressor);
    });
    def_may_throw(AST_Block, function(compressor) {
        return any(this.body, compressor);
    });
    def_may_throw(AST_Call, function(compressor) {
        if (is_nullish(this, compressor)) return false;
        if (any(this.args, compressor)) return true;
        if (this.is_callee_pure(compressor)) return false;
        if (this.expression.may_throw(compressor)) return true;
        return !(this.expression instanceof AST_Lambda)
            || any(this.expression.body, compressor);
    });
    def_may_throw(AST_Case, function(compressor) {
        return this.expression.may_throw(compressor)
            || any(this.body, compressor);
    });
    def_may_throw(AST_Conditional, function(compressor) {
        return this.condition.may_throw(compressor)
            || this.consequent.may_throw(compressor)
            || this.alternative.may_throw(compressor);
    });
    def_may_throw(AST_Definitions, function(compressor) {
        return any(this.definitions, compressor);
    });
    def_may_throw(AST_If, function(compressor) {
        return this.condition.may_throw(compressor)
            || this.body && this.body.may_throw(compressor)
            || this.alternative && this.alternative.may_throw(compressor);
    });
    def_may_throw(AST_LabeledStatement, function(compressor) {
        return this.body.may_throw(compressor);
    });
    def_may_throw(AST_Object, function(compressor) {
        return any(this.properties, compressor);
    });
    def_may_throw(AST_ObjectProperty, function(compressor) {
        // TODO key may throw too
        return this.value ? this.value.may_throw(compressor) : false;
    });
    def_may_throw(AST_ClassProperty, function(compressor) {
        return (
            this.computed_key() && this.key.may_throw(compressor)
            || this.static && this.value && this.value.may_throw(compressor)
        );
    });
    def_may_throw(AST_ConciseMethod, function(compressor) {
        return this.computed_key() && this.key.may_throw(compressor);
    });
    def_may_throw(AST_ObjectGetter, function(compressor) {
        return this.computed_key() && this.key.may_throw(compressor);
    });
    def_may_throw(AST_ObjectSetter, function(compressor) {
        return this.computed_key() && this.key.may_throw(compressor);
    });
    def_may_throw(AST_Return, function(compressor) {
        return this.value && this.value.may_throw(compressor);
    });
    def_may_throw(AST_Sequence, function(compressor) {
        return any(this.expressions, compressor);
    });
    def_may_throw(AST_SimpleStatement, function(compressor) {
        return this.body.may_throw(compressor);
    });
    def_may_throw(AST_Dot, function(compressor) {
        if (is_nullish(this, compressor)) return false;
        return !this.optional && this.expression.may_throw_on_access(compressor)
            || this.expression.may_throw(compressor);
    });
    def_may_throw(AST_Sub, function(compressor) {
        if (is_nullish(this, compressor)) return false;
        return !this.optional && this.expression.may_throw_on_access(compressor)
            || this.expression.may_throw(compressor)
            || this.property.may_throw(compressor);
    });
    def_may_throw(AST_Chain, function(compressor) {
        return this.expression.may_throw(compressor);
    });
    def_may_throw(AST_Switch, function(compressor) {
        return this.expression.may_throw(compressor)
            || any(this.body, compressor);
    });
    def_may_throw(AST_SymbolRef, function(compressor) {
        return !this.is_declared(compressor) && !pure_prop_access_globals.has(this.name);
    });
    def_may_throw(AST_SymbolClassProperty, return_false);
    def_may_throw(AST_Try, function(compressor) {
        return this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor)
            || this.bfinally && this.bfinally.may_throw(compressor);
    });
    def_may_throw(AST_Unary, function(compressor) {
        if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef)
            return false;
        return this.expression.may_throw(compressor);
    });
    def_may_throw(AST_VarDef, function(compressor) {
        if (!this.value) return false;
        return this.value.may_throw(compressor);
    });
})(function(node, func) {
    node.DEFMETHOD("may_throw", func);
});

// determine if expression is constant
(function(def_is_constant_expression) {
    function all_refs_local(scope) {
        let result = true;
        walk(this, node => {
            if (node instanceof AST_SymbolRef) {
                if (has_flag(this, INLINED)) {
                    result = false;
                    return walk_abort;
                }
                var def = node.definition();
                if (
                    member(def, this.enclosed)
                    && !this.variables.has(def.name)
                ) {
                    if (scope) {
                        var scope_def = scope.find_variable(node);
                        if (def.undeclared ? !scope_def : scope_def === def) {
                            result = "f";
                            return true;
                        }
                    }
                    result = false;
                    return walk_abort;
                }
                return true;
            }
            if (node instanceof AST_This && this instanceof AST_Arrow) {
                result = false;
                return walk_abort;
            }
        });
        return result;
    }

    def_is_constant_expression(AST_Node, return_false);
    def_is_constant_expression(AST_Constant, return_true);
    def_is_constant_expression(AST_Class, function(scope) {
        if (this.extends && !this.extends.is_constant_expression(scope)) {
            return false;
        }

        for (const prop of this.properties) {
            if (prop.computed_key() && !prop.key.is_constant_expression(scope)) {
                return false;
            }
            if (prop.static && prop.value && !prop.value.is_constant_expression(scope)) {
                return false;
            }
            if (prop instanceof AST_ClassStaticBlock) {
                return false;
            }
        }

        return all_refs_local.call(this, scope);
    });
    def_is_constant_expression(AST_Lambda, all_refs_local);
    def_is_constant_expression(AST_Unary, function() {
        return this.expression.is_constant_expression();
    });
    def_is_constant_expression(AST_Binary, function() {
        return this.left.is_constant_expression()
            && this.right.is_constant_expression();
    });
    def_is_constant_expression(AST_Array, function() {
        return this.elements.every((l) => l.is_constant_expression());
    });
    def_is_constant_expression(AST_Object, function() {
        return this.properties.every((l) => l.is_constant_expression());
    });
    def_is_constant_expression(AST_ObjectProperty, function() {
        return !!(!(this.key instanceof AST_Node) && this.value && this.value.is_constant_expression());
    });
})(function(node, func) {
    node.DEFMETHOD("is_constant_expression", func);
});


// may_throw_on_access()
// returns true if this node may be null, undefined or contain `AST_Accessor`
(function(def_may_throw_on_access) {
    AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
        return !compressor.option("pure_getters")
            || this._dot_throw(compressor);
    });

    function is_strict(compressor) {
        return /strict/.test(compressor.option("pure_getters"));
    }

    def_may_throw_on_access(AST_Node, is_strict);
    def_may_throw_on_access(AST_Null, return_true);
    def_may_throw_on_access(AST_Undefined, return_true);
    def_may_throw_on_access(AST_Constant, return_false);
    def_may_throw_on_access(AST_Array, return_false);
    def_may_throw_on_access(AST_Object, function(compressor) {
        if (!is_strict(compressor)) return false;
        for (var i = this.properties.length; --i >=0;)
            if (this.properties[i]._dot_throw(compressor)) return true;
        return false;
    });
    // Do not be as strict with classes as we are with objects.
    // Hopefully the community is not going to abuse static getters and setters.
    // https://github.com/terser/terser/issues/724#issuecomment-643655656
    def_may_throw_on_access(AST_Class, return_false);
    def_may_throw_on_access(AST_ObjectProperty, return_false);
    def_may_throw_on_access(AST_ObjectGetter, return_true);
    def_may_throw_on_access(AST_Expansion, function(compressor) {
        return this.expression._dot_throw(compressor);
    });
    def_may_throw_on_access(AST_Function, return_false);
    def_may_throw_on_access(AST_Arrow, return_false);
    def_may_throw_on_access(AST_UnaryPostfix, return_false);
    def_may_throw_on_access(AST_UnaryPrefix, function() {
        return this.operator == "void";
    });
    def_may_throw_on_access(AST_Binary, function(compressor) {
        return (this.operator == "&&" || this.operator == "||" || this.operator == "??")
            && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
    });
    def_may_throw_on_access(AST_Assign, function(compressor) {
        if (this.logical) return true;

        return this.operator == "="
            && this.right._dot_throw(compressor);
    });
    def_may_throw_on_access(AST_Conditional, function(compressor) {
        return this.consequent._dot_throw(compressor)
            || this.alternative._dot_throw(compressor);
    });
    def_may_throw_on_access(AST_Dot, function(compressor) {
        if (!is_strict(compressor)) return false;

        if (this.property == "prototype") {
            return !(
                this.expression instanceof AST_Function
                || this.expression instanceof AST_Class
            );
        }
        return true;
    });
    def_may_throw_on_access(AST_Chain, function(compressor) {
        return this.expression._dot_throw(compressor);
    });
    def_may_throw_on_access(AST_Sequence, function(compressor) {
        return this.tail_node()._dot_throw(compressor);
    });
    def_may_throw_on_access(AST_SymbolRef, function(compressor) {
        if (this.name === "arguments" && this.scope instanceof AST_Lambda) return false;
        if (has_flag(this, UNDEFINED)) return true;
        if (!is_strict(compressor)) return false;
        if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
        if (this.is_immutable()) return false;
        var fixed = this.fixed_value();
        return !fixed || fixed._dot_throw(compressor);
    });
})(function(node, func) {
    node.DEFMETHOD("_dot_throw", func);
});

export function is_lhs(node, parent) {
    if (parent instanceof AST_Unary && unary_side_effects.has(parent.operator)) return parent.expression;
    if (parent instanceof AST_Assign && parent.left === node) return node;
}

(function(def_find_defs) {
    function to_node(value, orig) {
        if (value instanceof AST_Node) {
            if (!(value instanceof AST_Constant)) {
                // Value may be a function, an array including functions and even a complex assign / block expression,
                // so it should never be shared in different places.
                // Otherwise wrong information may be used in the compression phase
                value = value.clone(true);
            }
            return make_node(value.CTOR, orig, value);
        }
        if (Array.isArray(value)) return make_node(AST_Array, orig, {
            elements: value.map(function(value) {
                return to_node(value, orig);
            })
        });
        if (value && typeof value == "object") {
            var props = [];
            for (var key in value) if (HOP(value, key)) {
                props.push(make_node(AST_ObjectKeyVal, orig, {
                    key: key,
                    value: to_node(value[key], orig)
                }));
            }
            return make_node(AST_Object, orig, {
                properties: props
            });
        }
        return make_node_from_constant(value, orig);
    }

    AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
        if (!compressor.option("global_defs")) return this;
        this.figure_out_scope({ ie8: compressor.option("ie8") });
        return this.transform(new TreeTransformer(function(node) {
            var def = node._find_defs(compressor, "");
            if (!def) return;
            var level = 0, child = node, parent;
            while (parent = this.parent(level++)) {
                if (!(parent instanceof AST_PropAccess)) break;
                if (parent.expression !== child) break;
                child = parent;
            }
            if (is_lhs(child, parent)) {
                return;
            }
            return def;
        }));
    });
    def_find_defs(AST_Node, noop);
    def_find_defs(AST_Chain, function(compressor, suffix) {
        return this.expression._find_defs(compressor, suffix);
    });
    def_find_defs(AST_Dot, function(compressor, suffix) {
        return this.expression._find_defs(compressor, "." + this.property + suffix);
    });
    def_find_defs(AST_SymbolDeclaration, function() {
        if (!this.global()) return;
    });
    def_find_defs(AST_SymbolRef, function(compressor, suffix) {
        if (!this.global()) return;
        var defines = compressor.option("global_defs");
        var name = this.name + suffix;
        if (HOP(defines, name)) return to_node(defines[name], this);
    });
})(function(node, func) {
    node.DEFMETHOD("_find_defs", func);
});

// method to negate an expression
(function(def_negate) {
    function basic_negation(exp) {
        return make_node(AST_UnaryPrefix, exp, {
            operator: "!",
            expression: exp
        });
    }
    function best(orig, alt, first_in_statement) {
        var negated = basic_negation(orig);
        if (first_in_statement) {
            var stat = make_node(AST_SimpleStatement, alt, {
                body: alt
            });
            return best_of_expression(negated, stat) === stat ? alt : negated;
        }
        return best_of_expression(negated, alt);
    }
    def_negate(AST_Node, function() {
        return basic_negation(this);
    });
    def_negate(AST_Statement, function() {
        throw new Error("Cannot negate a statement");
    });
    def_negate(AST_Function, function() {
        return basic_negation(this);
    });
    def_negate(AST_Arrow, function() {
        return basic_negation(this);
    });
    def_negate(AST_UnaryPrefix, function() {
        if (this.operator == "!")
            return this.expression;
        return basic_negation(this);
    });
    def_negate(AST_Sequence, function(compressor) {
        var expressions = this.expressions.slice();
        expressions.push(expressions.pop().negate(compressor));
        return make_sequence(this, expressions);
    });
    def_negate(AST_Conditional, function(compressor, first_in_statement) {
        var self = this.clone();
        self.consequent = self.consequent.negate(compressor);
        self.alternative = self.alternative.negate(compressor);
        return best(this, self, first_in_statement);
    });
    def_negate(AST_Binary, function(compressor, first_in_statement) {
        var self = this.clone(), op = this.operator;
        if (compressor.option("unsafe_comps")) {
            switch (op) {
              case "<=" : self.operator = ">"  ; return self;
              case "<"  : self.operator = ">=" ; return self;
              case ">=" : self.operator = "<"  ; return self;
              case ">"  : self.operator = "<=" ; return self;
            }
        }
        switch (op) {
          case "==" : self.operator = "!="; return self;
          case "!=" : self.operator = "=="; return self;
          case "===": self.operator = "!=="; return self;
          case "!==": self.operator = "==="; return self;
          case "&&":
            self.operator = "||";
            self.left = self.left.negate(compressor, first_in_statement);
            self.right = self.right.negate(compressor);
            return best(this, self, first_in_statement);
          case "||":
            self.operator = "&&";
            self.left = self.left.negate(compressor, first_in_statement);
            self.right = self.right.negate(compressor);
            return best(this, self, first_in_statement);
        }
        return basic_negation(this);
    });
})(function(node, func) {
    node.DEFMETHOD("negate", function(compressor, first_in_statement) {
        return func.call(this, compressor, first_in_statement);
    });
});

// Is the callee of this function pure?
var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError");
AST_Call.DEFMETHOD("is_callee_pure", function(compressor) {
    if (compressor.option("unsafe")) {
        var expr = this.expression;
        var first_arg = (this.args && this.args[0] && this.args[0].evaluate(compressor));
        if (
            expr.expression && expr.expression.name === "hasOwnProperty" &&
            (first_arg == null || first_arg.thedef && first_arg.thedef.undeclared)
        ) {
            return false;
        }
        if (is_undeclared_ref(expr) && global_pure_fns.has(expr.name)) return true;
        if (
            expr instanceof AST_Dot
            && is_undeclared_ref(expr.expression)
            && is_pure_native_fn(expr.expression.name, expr.property)
        ) {
            return true;
        }
    }
    return !!has_annotation(this, _PURE) || !compressor.pure_funcs(this);
});

// If I call this, is it a pure function?
AST_Node.DEFMETHOD("is_call_pure", return_false);
AST_Dot.DEFMETHOD("is_call_pure", function(compressor) {
    if (!compressor.option("unsafe")) return;
    const expr = this.expression;

    let native_obj;
    if (expr instanceof AST_Array) {
        native_obj = "Array";
    } else if (expr.is_boolean()) {
        native_obj = "Boolean";
    } else if (expr.is_number(compressor)) {
        native_obj = "Number";
    } else if (expr instanceof AST_RegExp) {
        native_obj = "RegExp";
    } else if (expr.is_string(compressor)) {
        native_obj = "String";
    } else if (!this.may_throw_on_access(compressor)) {
        native_obj = "Object";
    }
    return native_obj != null && is_pure_native_method(native_obj, this.property);
});

// tell me if a statement aborts
export const aborts = (thing) => thing && thing.aborts();

(function(def_aborts) {
    def_aborts(AST_Statement, return_null);
    def_aborts(AST_Jump, return_this);
    function block_aborts() {
        for (var i = 0; i < this.body.length; i++) {
            if (aborts(this.body[i])) {
                return this.body[i];
            }
        }
        return null;
    }
    def_aborts(AST_Import, return_null);
    def_aborts(AST_BlockStatement, block_aborts);
    def_aborts(AST_SwitchBranch, block_aborts);
    def_aborts(AST_DefClass, function () {
        for (const prop of this.properties) {
            if (prop instanceof AST_ClassStaticBlock) {
                if (prop.aborts()) return prop;
            }
        }
        return null;
    });
    def_aborts(AST_ClassStaticBlock, block_aborts);
    def_aborts(AST_If, function() {
        return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
    });
})(function(node, func) {
    node.DEFMETHOD("aborts", func);
});

export function is_modified(compressor, tw, node, value, level, immutable) {
    var parent = tw.parent(level);
    var lhs = is_lhs(node, parent);
    if (lhs) return lhs;
    if (!immutable
        && parent instanceof AST_Call
        && parent.expression === node
        && !(value instanceof AST_Arrow)
        && !(value instanceof AST_Class)
        && !parent.is_callee_pure(compressor)
        && (!(value instanceof AST_Function)
            || !(parent instanceof AST_New) && value.contains_this())) {
        return true;
    }
    if (parent instanceof AST_Array) {
        return is_modified(compressor, tw, parent, parent, level + 1);
    }
    if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
        var obj = tw.parent(level + 1);
        return is_modified(compressor, tw, obj, obj, level + 2);
    }
    if (parent instanceof AST_PropAccess && parent.expression === node) {
        var prop = read_property(value, parent.property);
        return !immutable && is_modified(compressor, tw, parent, prop, level + 1);
    }
}