From 54dda6d139f5b2cf6b36c72c61acde4f793f1846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 5 Jun 2024 12:11:33 +0200 Subject: [PATCH 001/167] Add IDE module --- OPAL/ProjectDependencies.mmd | 6 +++- OPAL/ProjectDependencies.pdf | Bin 26955 -> 27786 bytes OPAL/ProjectDependencies.svg | 2 +- OPAL/ide/Readme.md | 2 ++ OPAL/ide/build.sbt | 1 + OPAL/ide/src/main/resources/reference.conf | 6 ++++ .../main/scala/org/opalj/ide/package.scala | 34 ++++++++++++++++++ build.sbt | 13 +++++++ project/Dependencies.scala | 1 + 9 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 OPAL/ide/Readme.md create mode 100644 OPAL/ide/build.sbt create mode 100644 OPAL/ide/src/main/resources/reference.conf create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/package.scala diff --git a/OPAL/ProjectDependencies.mmd b/OPAL/ProjectDependencies.mmd index b2055fe410..d43c52eb46 100644 --- a/OPAL/ProjectDependencies.mmd +++ b/OPAL/ProjectDependencies.mmd @@ -9,6 +9,7 @@ flowchart BT br[Bytecode Representation\n br] da[Bytecode Disassembler\n da] + ide[IDE\n ide] ifds[IFDS\n ifds] ai[Abstract Interpretation Framework\n ai] bc[Bytecode Creator\n bc] @@ -40,6 +41,9 @@ flowchart BT br --> bi da --> bi + ide --> si + ide --> br + ifds --> si ifds --> br @@ -68,4 +72,4 @@ flowchart BT demos --> framework bp --> framework - hermes --> framework \ No newline at end of file + hermes --> framework diff --git a/OPAL/ProjectDependencies.pdf b/OPAL/ProjectDependencies.pdf index 1cd89db7f58d46fde942cfdf0ef88921bea2690d..9fa874d11c1509b471ab752f3b53c37f4909009a 100644 GIT binary patch delta 10962 zcmZ`LLu|oE8IQIlo?V|%1oJMk0R;aKqyM@rqMt?EmXENR7fHrWUsPi zXZL%(?^Rvb_vg_c&hvd<=bYC$=XuVnN0nDIMSWwstgA;*;85M}>*V0?=H;pD;O~Tz z*Hy!!v3N8QO~9bhBpgKvjg|v{fS#$B~N@!~W+YvQw{%oQD?c%i9*6ro|H` zWTV(2u5A0n=bmubm* za^unG(`tD_Pa+jQdhlPT2u@f^60Z9w@w-b1mvDDMAIh9wILW#?CkS+zKjja~Iu>Xx zw)NquptV;_o^!qEye#qZ0C~1vp{7UJDM8sSAQ!q^6OFyrUj4{q`ktTGtqtowG8yZe zN0lA*v@wzG)LJdVU1EJzRVt+CN~_qwLc6qJ0(X8^m!V|7yy5n!wZ&l@nnso9UKb`< z_5Tj!Ih?rez6CzwH2!F1-tHS|GZQ)YTINnZ^xs&@6(@Y*Xp?r1Q%8?Lv$o}v)y+Q7IY^Fa1C!{3wOuA0>Exi?%7Wvkp1 zPl%*~nQr5=i!&_Qah>X++qm+7YZby?3ggm~Kw#D6LD$DKH(0#!>GP+sAWx9UrziqrzxbaeL zNnKsV+hfPf;-T<1JH^67D)|LU!o`kW9W35Y3l#4i8-JSEQj=;Mu(9NAsHuZn@}r}f zU&}Wbe3q&%e}^0k|5~wgSOWjs!t?=g;|0w<38>tmyPTbSuIyE#$UThMQ}n*q&!|G6 zp{#PoQRCNwr$UbL$;OWt>NM3}MR$gWemJCb;^YgS{^a|R-M1m9;N;N%OkXsn<7Q7A zaGRyh>{Cd)u32zgFK-Kxm`FMLS5@*?*UAWM*_#Me(s1v-s;@i?&x=+z^dP8uM_^=LlAL5;rqnh&JJVX0 zV#&emfyq_8I=$t4h3AHbQ@%H9zll^ZsVft`o;dG$V>oMv-zSUwommr^S(T%10Sm6G z_QD@R#s_N69zz2L!F`Qe`{V8ZJ7s07-FTs%^-$Zq+GI`t>=(N{;)dBD^$+jttG_hW z-FSMUB4dwsTDthq%u$wCUZ)agfvMVv&vV%9>aVZt4@{8w1uQShTJlfv=9Lw>IXPra zs|93rh~0JHUeVkn8^klLv4}dzY(n!VX*yi*Dv6WnYy*FD zYzJSJ`u^2xL}7DcNB7Wm&7E*K^Xh_(9Wu zBJ^vq_V`(i%>2%rIiG&(ANeEUZIzbi@?3b#HpE^$C+DH_E1Ac}|B7%p>UC0h(T2Sj z-dJ{7zUqL`l&~LpDf?Yr>?OQybv8*tL$2mMTsB-f4lfsc-}N&W?iHAMpvW8=-B=PL z|E@5b{Msp^*0f(X04^|UN%1r%J&Z?0h$au)_J`-5jeoCv^RBSYR|{|F#GC4B@*}AS z65c?1?Rf&ztDi$UC-6N2lc8HJR&v?#IY)Gj@6VDeEaao^flQbj=Q8V6iaC0 zndYO)B}Kb#bEF`mdE=;9^*Y+bhmY zX3VPPKf2`4Jzn>H@H$U58FEX&?-Y`YhD(gPQhch(f5qc&8eL-%{T6Jy0{UjY9<4Nx zJkE2){+vgx`{uC5O9q~L{T8A-JL+!s?)eg)Vwm=pO>|38ME6ue4liLs?~I6I)LRcN zy90uR&?9C1vz#0mqAgDgSUm}Np4>32xZEE#*3J>lx}K5{DK`FtX}7E|Uw$Ma)5KOi z&$T1-8}9V1_5NacGk1X}S72`O1eCDq{6{>IdCkUx0Oxs}w}zfH^LBaZD+Zfk;`8o~ zjXO^Ja`2jRoT?pZIA-3+d8Wh?Tb-=1m4i91iLad!KIJ>E{GxtcHPaq;30GN#uIs61 zb9|t0RtarbU1_;1d}Wc{y1tT>*aX9!|HK)sD=rgTZZzH~y!R9i4+(w39__lY{s+HG z?SSPgCiB#iX@#~q73L+h1> zusOXf`5KGq!q+HIdA-E3bY-=Z8Xn1BQt8{aNMmvy%YOdu=mC-(EMce6hrg3;c)CH-hGBF^!Bzv}%8*f~c+c@9YG^Cd@qdGC4%kBJjBy(kMrXi1)J&kERYYHj?k zw^$2pWqA=Q+2#|fhM9G9pO(uQ?|!89iq%+^FJ=wX_OE1zIJD_aSrfk0wKbt`Z}D{E z+-JMe@qsG+o7V$RwR~f*xv*ds+SeatBtNKZtfT{p8LL*y?BKf}S*`1qgx?(}Z+aDy z{U^@~?qK`Ri&a(ND%Z?Bb0*Jy2j0t4tK8V0e325@l2e`~40iHJJUTz-tJ5e~oU*M% z-=*V$=-8^CDa!A%f4cSv&F^B-IJv&UiK5W%(bG8ZyAGEoOY|5p)rLMjHdK7{*iO_Y z#o*#558{oA8MgTAhR7I%mgXScNCFe7_0F4|yq|AU$UE)Lf*leH>&hH6?o`*^h%}Cz z+GBT!|E@Ss&G_j#?q?QR=w1823A0ri5VkD}8*a`xUTAUN@V?|{9plXj8AC(PYsc!` zx_nxh%CY!JvDW7=Q#_l|wRZdZS>Fqg5`HI|K^~2$|F`PFH*z)Eo3?dIJt1}KA7NGD zAxRK_*dZh7shaq{9;X1ieLV~P^DZx?Iiu=pH!@)cZlr_^ADW=pzLJijY`8I@Pt$A4zN$j9rCk#N8E!xM?hJ&R=n4r*+HN?#)#UQW zqu1-|?}y+)(RUu?lrelevGWu=W4V{ApC&%ctQZ`aGMYcU<@DMIywIt(JguG~+u-yU zS#dKXJq3QD$8!QsNLu^yX{dBD58S=l*wv(v_4L9)b{zDmsWG#al4W>?ym$BNw%d99 zwzjQYt#OI<*I)PZHOX_^yw<9=eQw6nc=B2Pbn$hCPNGkIo@ukJCtq%4(|Zp(@t2Q! zX`gz7!(^iSfe^VlT$_;=lsP+as_kg3N}JKnt8|@)bRJEFbo&fmUlX-#`^fd!%jtZ^ zc$&KfvqeRyI;)K=MqqwnoE8nj|cSfyz*~oRLHzg*izGYo~6`$7ME_PP7 zAF?T4*n0D*`1H0cB7x17zX$t?jkEiN#yz(N*Gat}n;tSbTdixzg0fFFU$C?Ezehe~ zxSMahu5&&|!CXxt@#@YUa`)kCqrMb>I+1(l2|4R@*_T~MszDk;auQD1y`CQ&Fg-Co zW6|}umhB7L`e32160N+-X4VI!ul4yA2S3j}EW+~=iL5Q$uG{k@?mR!Mu{wn5 zR_Al$wPQN&EM{y;(D^;l>}$ESNraw;Zqc`4Ovl$OgnSVQtN(GNqjJ5Cvbo{x=rCHW z`MG9>n87aJotxe%H5!MP*Q&;-5>#T)vM+P3x=8HdO4W&@DD?jbRkHEx z*eR}mY1iJpK_WX83z|3JW|-aBG&w~Kq(u$Z58^JI@kcA_TKnYih`b=Z)+>ABRn4ug(I z5;FG`s;6h%J@j4}ThJU$1vWWYAHc zQ{@{X@ zL7QK#@^1>r@C>}xXHmr+1qB7q@J}Yb5`Hxg9p_7~;x|$nDTlboJR$1$e4p#P2*?cv z>eb%JceyskR_Py|f2^}q`}fFL*NwfA&9S^|T+z)3ed>!j4nK>kjw_9{&)yI9;q4x>RM2>MZZI!WruZq{r$}-a*)CJ%fr5}*Q#=K zYZO4qc~bB2_1VMRwFNZ=>_%(_bwdYIr!J;%T*cfwenYKn=wg(s#?XB-up(tJwE=ou#SaI!V`n#Z$-LnKk7{ zX`2X~9#6ieH&gj-Q-rMJqUTJ3z(o1+oU68P+92P%fdSYxFBK1#&P0*FvU)ScEN-2g z^leJ3x8r>_fo%%FwMd^xRdEa6U}=-OdsfAq%b(xBz42^Sqe1KAMfKKl8B3mwBc~dJ z4`jGL-h3ooUwq29@xQi?ms6&#Yu|u3>y%x`)-Vf4MZL7x_=6&W+h&5#3OcGA6x865FcaXvHH}P>&|P^%YI9v zpDQZg-)6zTYEDF_wBgmdysHwj%|R}AMppGjoO-q@G1VG6F1A%=%~j?J|KrMfeH;bq zw%v6Wr1nXb-k|##cV+HuOFema^qOC&#_+0C^$@6(Dez8WnO16#=4ssF@RD|A*_W1F zgM!V!R{J^JKECGrO~I#4(&O>R*POzxDRYiJ5yS>*@JxtLt!dePI`PdniRYd|&)TvF zVoMy=?!B~k>$qI|*)>$hMq~%?o+zy~qq{y&RW@MS-3->-z2+E6-<@N7?zrQkjbQJB zp#T2yFSntgn2R|SOq1ekxwCVkZ+}K;9d}GHX{&H`@=JL_y6%&a+~$VcTV<}R^z$2$ zuc=fdRWYVA^mxptVWDO(+=aIqZO&ZL?wh)Z=X7!_YRj(WJxZ4rbv3Ek@a@=PHEdEl zGqXvvka;gLRjhc@`hdxKjXfsC7SU(Sa;(9F<1Y5>Eqzs{-s+}O zHLg7LK|U1rJ$8>2ou)3IFU6yMF@3Hpp1Wp}EoQsMmb8L%Y~mkqueHLOTOTZHX6%_f zK$ed08s~H{h?*R(6)#a*ZE82+@TGz|T!_Sa5O1lXJp1O7`tW!I`zL3SVdC8OWK~>- zMjyK$(G3z&3h_{p5OT{8Zsi`f)`+tI>3z;1{19*BN3zD^Tvx_$?a$DoxviILg4sw@gDtx=ZzR15erp+8 zW~ot|AG6+bV=#XdsU~a2=C{!!+ljQ11(>BXH+rlCTJ(<9RM=tCvFpEqfqHJ?#FK~W zCOh;*4cxrGvd667aeC|Bu-t;~uG#PVEfWb7nIU`GxL*D!Fp)ZMccaWWsAqq7Oj)wU zNQ}zBrS(=87zOvzcZ=RF@3#Nq=FZa=o0?56z~l8=rS|lb(Qe1eVBDJ8r ze>+ObDl)k3!O!&K1-tw$mn>y^?goktUo+6}D%8xVE`a*3X`3oPmAtKz((JD>&~PZq zlS|M%IL&5o=L-tf_Y8kQ->2ZAU;jx(vo5SY+vH|!Xt%YiwT?|VI-x!*s|;0pAf)X> zae9%ulHrf)_fI}2_nsI&AOGo|p0tmZ>#38|`y74cIc!^1oq3LxWX<#RA1m_b-{gOu zb+&V)RI0*R0*bsdLCC(@t0xn9p`MU?YxMe1J)k6H8YkvOj3^3i#4zefUPeqf5WneEB`Vawbovp6xm}>o)72 zn{u}^df!$B^l3uemP6Kszx>}OX>@k<~qHLvsd=q zKHU3`+tM_o@TP^iFI#gtmsa_`NmZ+x6$VF+)fs%ar+*=9*DImh9+~E;eX1S@DYuTF zt%}K4PYc+6`9I+S9`Eb7Z!RnjMm~A`rIC%_J#d=1_9w?db)_f#-R!PXfo=G-^rXec z?_XSUe{)BaYFT6o3aT(M5nvvPEzB4^TqGn5$8YA_fF+R?w6r*!JRQ9b9pS(b|9l@6 zmxK9)cvoY|Xa!h(vl4t)n4J%YLZi$%RE<&A7y|Mu3g{QfZ)Cv&g9}1xOeA2I!h$7Z z;Wr{%V6+4$GX{ZVVLM?y0OR3sp)HJt$TUL~#1JzU0~?B{F=NrxQ#cXi6cJu8Ml-eq zKN109lj*|-AI=EN@%{@Bi=kS@!bRv!AUZO9O;n8uM>PtG0;3c(ykC6DC=H&X!dO^L zlrcgqo@R?cyDIS2A*Keu5t4(;#Sl|?I2A=T#RRSm&~4%fN`#Mqs6o=`qarOu^+p~< zg~h@uC^aSuJSwiX0ZXC34-P{u0~Yxv2VX|ZNQ=NbQQ;8k<`&u7Dvq@3I|#QsVtTVf0R@MeS_G{ zl0+gQ!k49y3v(@@Tci-9%iHb3N`?ic)!-3vd6-!mn8cvrRvD^EDt3a3 zVd1+{D=ti-+QEbVKSF zqXJ=x3ZM)xwov_p#+Qfdlt3RWyFP&opHWh#e!Rbhj~j<0(sdpIJ}oRy#|{FG9RwJ# zV*?hCrBfvWegiuu%Id%YeWMX5NTaR)M|_}3ttcK3?^2}^oCw$fM$mxrMiv~1I7*p% zt95i|Xh{JDY*6_#C5luwG6M?eU^c;m5;Dw0+K`~AA~x{wK^4Z#5NVkqQZoahrw@`y zLfFhiM5co@4zHzU%b{wdi<0;Ba&$r?KR7&nUWnv>>QXqu|59IOogs}xS{i{jWXOPa zkq0vujA95w?RnbtrbUZcnn$!Lf~653OQR~5#xxv_C^%Y=b9i!B5e+M8<`ZKv`67@b13QxQ2 z-!Ve{i2pMPXwo0{Ql&rqqXqMqN3?HC38Wn$(o7R+nt%I>s<~8K9wJrM_RrQPpA}F9Rw8H)CJT%BNiq;r({RsZCAE2~a{nG_Y9-y?E z)1!z5+70MYvH}WAyU7v?fkww-(Ewgy7>flcEzfi&u)y6?7t*7^-BMH1qrlx#kJ6zy z;BBd8S%fsz}-@V)1$y0VcG4F%i)2$rS_-O1%nKQumJ-7 zPY(lkOYERWfjfjO47$MG5_RZw3BVn3Mb^M`4z=_+qtgYCI0!t}7!8v_Y)hn}S4Qeg zBQ8A(LR+E~Jqp}mX{}0!Qjjt;bP9yVQjj{+NJ6iS)Oke%Q4n`DM$qd5cS~&jhk~~e zKpC@!Lj!M1Tt;-ErCAGJKj3l0kO}ap0VrdBkVg#)#~2!TtpJoUKR66$LPI!1 zn-UOR^}(YAz>GWqj}Cw`<_A1F0JOkIiUeVSI|5_vz-s`Y zEAoSIhOiCM9Y`!AroxYpxoi7lfaX1g(4n23wJy5K`Xxy zk-{>VB~cjjgC>LLC!-!2jb_Xlnv6k~*>Zbi!k<*^_?I6tc>1nT#3O5g;V=cWB4{!Z z?Dx_CbsY*8Sy;<0Q2s}l6bv$q8T6KCllqkc9@i`M@PAqW`ga`)u((3;f8>@zqA==V zz@B8hHu&F#=#>}-c@oeC4Qsiut|%!6Jf0adhC$08PR;cpHwAZW&9VK5kEWi5A%0e=Z9bdiM%Ts8`jD9f(|;=%ytzyw!m7~{H0 z8w_8@<5)ChC5BzmB_LTuJj~mWb>njS;IA}at=MvOqj5#a?_VH}x5)!fU=A4Q^WOE924 goxM;XCKNKA{rnw#{i$OTOT^&G95OO`X8Iie2bhv_>i_@% delta 10292 zcmZ`+cRbbK|3|iLQ3+Qz;qGCVC_5o!W$&46Zlr_?y^D}YMIxeX5-K6d-h?8Nkr1Na z`;JfD^8I=E+kCAb=2115h8h10S<*D zp)eQ@hD0DxSQ#i(0{jR4^t>JZw@Xs&8Xkjzsi;8C`h#4a%t5!mW(*a6EW3GWVOGjt z`9+SFoFH|h+F|$aTI4(s^z`sfo$Z$ga%={b7Y0$;Xo{ zXMY7HUP&3;*5$?9ymi8us&4hoj;CC>vjiK|dO0{_^Xu%7p*p=^FMVO}ngT{@Q21Z$ zDHU43wx=_5f+ub8s$t6#soUFiZ6}K(TBPzaGTHF;V>WEL?w;_CB4ZfNdPK52v1Q$9 z+4>>9)>ZQym#gPWr?Sd15oy0-cy564)?zJuC7kWy z#jg{^Q!eDe;=E(5P-5{#fXpa_Nv@Bz=6tI()#aspH$fAuV({Ade4MwH7wwUPZbdHk1>)rUv2$+Tt zJm7XxmZK4EQZ3QW_*#hh1Jp1;0PI>PK0cEk4I29wI_?mEHiOmys0 zn=IVTX3Zy`SvTE2>u#_m>TeV}mlm$!@Vrr=!=i9x?PVi3#srEsc`4o_uy{kuzerYE6|Vq?s~4s1~#q`k)&^=o`Le==>o_h5v z;}n5w*^8bYd0m&UsY#`vHS<0{_z_fg8=Z`b3a*%QqdPYJqbus9-01X2197`i(Sz1v z6ORXQ=kvx-wl+U`#wbg3m`>S*RgPHWV8W?a;xF z)FS#=ZK#9MthN6$SPchkpr!rwRKwU zJ-S_O7r%xMJ}q-nLKNB#}NoZI*C$T=3Kic80QM+a{&^b4Qj&X4VR>1Ky2 zPBYUh5dULV#rI^FbG9XTo3REvc^Hr3XZM>{U*U~0GnU{pgd2X4%yoXDfRj9;_~xs-dNd2Aa*VKic{TwtO_0Na46;?jEGtg#Mig1@fDF$R?g(v)fqau;g(XIYjuTD%MrJZ3kXp7=HFdI;K^$rp592-@$%V z4D$%~qAr^cW{JzRJG!Cm`{-CyshXms7{m8~N&0 zy6J5zcm`#42lo=xK$?eMnYab;xVe|YT;sEuMDII^k)Irx-Hz(n!%WxB@>WHUC-ctf zu&+9Xe7l4!;nE^MvqH`e7j!62M61DgUA?8%sk(*dXz1vL-H~!A18M85_lDN21?t+% zYN1WvhbkuwTY?L*FSn~ow+^@Cc{{TcYaCe=)h{9lKg@Sp$IE8q$i}oRWAdlIx&!(NzrHh!3%f#S_c?F5c1BKh^?q(j zPj_-Z+LW!;jOkR@78e7qYc=0@I0=_Zy`f7s>)i3$Z0NN6t@m=D>{iQMb=)>XU8lZw zeuy_5Zz{W989egN^l~xHKBaNky@KMNOIfy5es3G36!@|d3%uV?9gT|+9hx0yjNCV6 zdY&a~aB@Hlb>Yo;!<<9)RG+QOg@&^n>c`)|9bA1o7~u4EdHuN*4VpUryxt4(Gu%&@ z`e{9A(D>i>W`3^kzUx`7xy}vrRcVZ`yD^UK@}ffrT&`SI?Lu4;p!vC2<71v`ZfP{O z4Dm0h>-G7*TByNLf6?ZixEyy%Z0k$M3Ak@+g|6jq^BZGIZ8?3NuGd{ht5i!r3uz_! z%e;>}sK@oQBQ#r@&mzh*@NITc?qk&f_4`veN75FbF22j+Ri5W`$Zt6n9$L}lS^IgY zF(cwlOUx;@AC=q3RkJ>wX$<0@Ef@`1?W1;ev1W;7Eibak;g#HsjvDnSRQ;TO&=z>2 z)i3J7g=R{R)@u;|V^*s|WD}PJkFrvi>^`2!9v!Tv;DF!7Fd_U#J^G+DBS)9fu1<}* zdOBJ9Rv&iORL-l9<7{H=0uL`R&U79*blvEC?1WE)ZI2xGi@$z_S(g68<&tUMCkn}? z6#+piOm;d9C7cFJmv7`6SWG`v7WrS(mox>}_)a<>KiO}v40prg^S)R~8Qo4XEZW@m zQ?<-sH=X|UrN%NN)5dA)teWH->H`;yDsq&{b@`(hLa7#+)a<1DYowgmRny?c2Cvs0 zbSASOEzrlHHK4D8UzoG#Jy3gcI=^V~b#S^fd|_aO?b7U8KFhmo~#9_cKt+%31T!`4zm%9I%QymhehkT@1jtukAq*%W#n2Ooa=9R#qIu-vvv0YyzaNwG&kSFwHfhj*0BDXL-hfA2P!2Qna@;0 zi~8kwzm*6WJhC%MEp_Y?Wb%?La}O7|(pzLK5_M|p%sG2e#^{n06E&6V8e@BZ7c!}^ zDa`N}^a`gfgcTclF!`Upr1uCd=Pa&pcY2TVY-`|EEZZ;_ftwN~aP!mx-BaQ+z|H0s z2gj0cM~R=;A}STE!z)9~Pu9-k8D$iebd195chSns@_Z@DDBO}M{`SgBX7ENEBO=MOcD;TC)~?rr_oKz?|d#@WHU4jtB3* zRH;tYet2)gVJj_$kzct4DaKl!Rj8#JE?_HulF6E5e4^=+ zC8tIH^H_^ai1rb6?ixNe=k5`g^rIIhquQeq>&Izo*z_6$Jd3!ig#+*E9>wvaXzph`!E(t=8jo#iwWj^lB9su?wf(1?2uQX7&L&#k&&oX*cT=evaJz~9j#S+pIMpY0al;q?7wv)2AK zA-Fx?)dw?e*vGQNx&phV@yI16S$q=maOSVjhS<+TOg9ry3e`B&jFIPEqboG>Y1ey^C!QO+0TPeq6~#Au0^2V0@SO6Oj9;t9JILt_ zFG%|4emXCao1t!c&OEuwW1JeYVP)PbQjU9Bbr9 zN5d40$KV@TUau>fx5$0)9!RRK!rzKQ@9~)k(XiCswO7My*x<+qExv1!g

Y_u;px%WGDzSQU&m8Fj@lOLn6?$(M%Fp0Sza-7C zoXg`bbjvudnAY4`-LW7&)MYwFqogs{@!^Sqy=X&|$)TKDlee*fni75=V`leyd3_JP zR`~AD^ALgc9_sH+_D!C^E~d)1mc~7vrVA+$UDl%?pD4~~Uj79|?_bj$Y10%so)j4K zMtCDUOK@vL`rFqF`&E~#crq`lmf&$J)|_6^-5tP2W7x&4mUx}w%=#1d44EhsInTit z?3kMAJB80YswQ_gy5Wzh4jxn%T8YA?BiHwm$pYf>7(|TKk9XqT#PQCU$Tg(`! zG+17fxEo`CZfzrL)2gli!SIPYmOZ56$O}o%cE&s_-yQndv1)#C_d(L+jDVvx3E$`iQsXCg0|JeR>t8 zv~_Jw0sh%u_SsV*XR_wzVLt*hwCiy5GmVhAg6egejKxSw7L7TQes2qqjbie*i-Ta8eo%5Rc z&UQ=3ad*Mw64o-_3D46s6VkSH|lZjVgf!Z;z68V zU?96Ux+lXqmYU1ZaQLiM4Mvu2BM39Vp`=+{KQ;zg`(7Iynx?m$b9lSL_<0<(s>8c= zsle*8R2549xPXaIG3L?RI}yyr?+V$4Oh$y(LVdblf5=9`IWiJA+@~W`g5}gWw1Bav!&DXi6l@5n-~Bow zW`xQf_M>rhqok)k0e9<>&NYpdfcSP)d3~2N?UrZ!!tA<)(py2QqSG=VjmIa0urGTJ ze+qJKp74pd?f+wO;6&hCH5_Zj1L^J5eWi`XC2N{ib-i;Obuk4Vz@#9!q%redE$tj$0r{9JhiYB;xO>#kD)~#F`&ds0<`){5=ETm# z%(lHZt9$LiwQ$Y}Cfc;M$BDfXpYOH(&?}39w|cm%H8-BtzAfcdo=$PpXZ$%|UcuH+ zkzWtWy38C{j+!&EIXYn%;#Az~8LzJ8^Df5Vl+J2|DWv+8M~{*BoInOuqiYL(@5P5t zSHFwp)@Zg>^Hm>`t)^-|>8E(Rh4o0X6>u7f7!hN`I4QrGXHLI>T3lY~*wB!6+&9wM zauW!?9^`e4zq=c?E5Wo|+2@eR%*M>e$b$TiLB*UiK|hd81{VwXjdC2=b?;q$o_9)p z>*s^zd+tuZ*h3gZOrB2fm7@lB@k4+_K~`!QFfK0vxb5erg(9F*0P}tv5g6aky$gzf z0j;n@fcF7LfDX0x;oM0OVnk zfZPEt&_)91_Y>E1!y!<}NoIK+hy@Ho_y~bvfkY`D&<74A2pl3Dg$9NLX~Y2<0d5*7 z0?0a`@E;N^KsXErc+3AU85~L^gTu&VC}2pCK*mN5Cvxcv5?11XuL25W;D zrl2s2gTWCbE+m-?3rIjnTqq(}nh;?n9KZ=FP?Hc&2sZ~5iJ%Aw90k}SAb`&S21;Ty zi5Np7M#6w|!bD;~SWFU_h7f2`z(t4x(Y6sL*l6I1(2fJ*h&%`=fd||eNwF~)MnWdQ zjJ<$J0H}l9>`)|@@_XR0xa@ylKR!s<0tak?`u>gqf|w@=m`GxBkQjhdl$0DWmjvbu zBH&UK3=kGkpe7-kB4D~ufc8OI0vFexL?ck7;{t~z_ke4lfQ=ZL3PYso5G7C{0dG+S zY7(L)MnF(NstEC1lw4RcSArCQ3jsV9Cvt&j7?+X&1jWHhLScYfF$JQXOti7UGLYy=MpkO>V1SR_Cu(EcdF!wyBmC?bgjmSiL;j)p{%7zynG z9s&nwNs(lNIZWi20IDTHQD_8UE~!9GLJ(45*`O)22fFSo8ziYwkmN=|!+{iOGBcr3 zBmgF9F!E?1M@oSPOeO(|lqT$j0XiiA)r}%G3Ya-iH>e0jp$J8+4HO)J$|!)#alnc+ zsaqs~b{UWm0|kV@o&OFbiqtJ&{)okhrYtrvs)S+$Gf6nqgF^%kIN&KmhV@ z(!jug>O%@Z0>306APa^DLm3({tK`t^rdUXh2?hZq$({ol&_KT&F*ZPptQ627N7xYq zFv%e)+7L|&0h&B3FepF>N{S~KS}05cNRlTk!vXU01o;Gn2?`=uC~yWO|64wqBp*#4 z78op$11FNVf+Vq^ zNGvE43yQP=1%5{d&Q?$lvAUs1U<-bVm2yDAfFuY6IFHc66IK#txCM-~DU3w*dpr}X z`uBV#S)`M|NekfrjWyDD{|zUi^?QzxED|+>v;aYrjE5jdD}Hw*(e=M>BCYt{0>tlr z*D%Q<9R@`@42r~!B8BsJs)?Szb49SM$afbI2;&AmBlpk~cd`N^puE67lrS;f$|zYr zgcO97Nr3PMg3z`FrLo>!Es zCfNURXe^RY&j0wKup|sm5q=!_qil!c|2Q=kw=>7M{}E~&j4;*z-WUfK5+z2AKMn)k z5iZ>S2sI9c{nrl${U7y#!{ByA28Sa&*#0^U4AhJNH#9Kl-vvTg0fWK`qwSx?JF6N7 z{?Pk3f-oowy8{Cq$x+b$UHveyYW@lhabq0e*8h8B7}&ZyFc|oLC~y-4^4xe-FxVYR zVW=Io0)wG<)ENv8g9Dpx;=sBaHy)1Oxi=h(*&zmwBRmiPJ|hxNcy0ZIA$Bww47oGQ za0HInP-5Wf-@$<+;eCommoncommonStatic Analysis InfrastructuresiBytecode InfrastructurebiBytecode RepresentationbrBytecode DisassemblerdaIFDSifdsAbstract Interpretation FrameworkaiBytecode CreatorbcThree Address CodetacDependency ExtractiondeBytecode AssemblerbaLLVMllAPKapkArchitecture ValidationavFrameworkDemosBugPickerbpHermeshermes \ No newline at end of file +CommoncommonStatic Analysis InfrastructuresiBytecode InfrastructurebiBytecode RepresentationbrBytecode DisassemblerdaIDEideIFDSifdsAbstract Interpretation FrameworkaiBytecode CreatorbcThree Address CodetacDependency ExtractiondeBytecode AssemblerbaLLVMllAPKapkArchitecture ValidationavFrameworkDemosBugPickerbpHermeshermes \ No newline at end of file diff --git a/OPAL/ide/Readme.md b/OPAL/ide/Readme.md new file mode 100644 index 0000000000..3a3cec1ce1 --- /dev/null +++ b/OPAL/ide/Readme.md @@ -0,0 +1,2 @@ +# Overview +The ***IDE*** (ide) module provides a generic implementation for IDE analyses. diff --git a/OPAL/ide/build.sbt b/OPAL/ide/build.sbt new file mode 100644 index 0000000000..b511e98651 --- /dev/null +++ b/OPAL/ide/build.sbt @@ -0,0 +1 @@ +// build settings reside in the opal root build.sbt file diff --git a/OPAL/ide/src/main/resources/reference.conf b/OPAL/ide/src/main/resources/reference.conf new file mode 100644 index 0000000000..49657843d8 --- /dev/null +++ b/OPAL/ide/src/main/resources/reference.conf @@ -0,0 +1,6 @@ +org.opalj { + ide { + debug = false, + trace = false, + } +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/package.scala b/OPAL/ide/src/main/scala/org/opalj/ide/package.scala new file mode 100644 index 0000000000..f6ca262589 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/package.scala @@ -0,0 +1,34 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj + +import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory + +import org.opalj.log.GlobalLogContext +import org.opalj.log.LogContext +import org.opalj.log.OPALLogger.info + +package object ide { + + final val FrameworkName = "OPAL IDE" + + { + implicit val logContext: LogContext = GlobalLogContext + try { + assert(false) // <= test whether assertions are turned on or off... + info(FrameworkName, "Production Build") + } catch { + case _: AssertionError => info(FrameworkName, "Development Build with Assertions") + } + } + + // We want to make sure that the class loader is used which potentially can + // find the config files; the libraries (e.g., Typesafe Config) may have + // been loaded using the parent class loader and, hence, may not be able to + // find the config files at all. + val BaseConfig: Config = ConfigFactory.load(this.getClass.getClassLoader) + + final val ConfigKeyPrefix = "org.opalj.ide." + final val ConfigKeyDebugLog = s"${ConfigKeyPrefix}debug" + final val ConfigKeyTraceLog = s"${ConfigKeyPrefix}trace" +} diff --git a/build.sbt b/build.sbt index 4e5e85a927..8f17b225b5 100644 --- a/build.sbt +++ b/build.sbt @@ -310,6 +310,19 @@ lazy val `IFDS` = (project in file("OPAL/ifds")) .dependsOn(br % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) +lazy val ide = `IDE` +lazy val `IDE` = (project in file("OPAL/ide")) + .settings(buildSettings: _*) + .settings( + name := "IDE", + Compile / doc / scalacOptions ++= Opts.doc.title("OPAL - IDE"), + fork := true, + libraryDependencies ++= Dependencies.ide + ) + .dependsOn(si % "it->it;it->test;test->test;compile->compile") + .dependsOn(br % "it->it;it->test;test->test;compile->compile") + .configs(IntegrationTest) + lazy val tac = `ThreeAddressCode` lazy val `ThreeAddressCode` = (project in file("OPAL/tac")) .settings(buildSettings: _*) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index be2ea989e3..bbdd5a156e 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -77,6 +77,7 @@ object Dependencies { val bi = Seq(commonstext) val br = Seq(scalaparsercombinators, scalaxml) val ifds = Seq() + val ide = Seq() val tools = Seq(txtmark, jacksonDF) val hermes = Seq(txtmark, jacksonDF, javafxBase) val apk = Seq(apkparser, scalaxml) From eb634e45354d494de1ba0b6709432956ddbe1b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 5 Jun 2024 17:07:00 +0200 Subject: [PATCH 002/167] Basic model of IDE problems --- .../org/opalj/ide/problem/EdgeFunction.scala | 89 ++++++++++++++ .../org/opalj/ide/problem/FlowFunction.scala | 13 +++ .../scala/org/opalj/ide/problem/IDEFact.scala | 7 ++ .../org/opalj/ide/problem/IDEProblem.scala | 109 ++++++++++++++++++ .../org/opalj/ide/problem/IDEValue.scala | 7 ++ .../org/opalj/ide/problem/MeetLattice.scala | 22 ++++ 6 files changed, 247 insertions(+) create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEFact.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEValue.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/problem/MeetLattice.scala diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala new file mode 100644 index 0000000000..03235aaecc --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala @@ -0,0 +1,89 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.problem + +/** + * Interface representing IDE edge functions + */ +trait EdgeFunction[Value <: IDEValue] { + /** + * Compute the value of the edge function + * @param sourceValue the incoming parameter value + */ + def compute(sourceValue: Value): Value + + /** + * Compose two edge functions + * @param secondEdgeFunction the edge function that is applied after this one + * @return an edge function computing the same values as first applying this edge function and then applying the + * result to the second edge function + */ + def composeWith(secondEdgeFunction: EdgeFunction[Value]): EdgeFunction[Value] + + /** + * Combine two edge functions via meet semantics + */ + def meetWith(otherEdgeFunction: EdgeFunction[Value]): EdgeFunction[Value] + + /** + * Check whether two edge functions are equal (s.t. they produce the same result for same source values) + */ + def equalTo(otherEdgeFunction: EdgeFunction[Value]): Boolean +} + +/** + * Special edge function representing an identity edge function + */ +case class IdentityEdgeFunction[Value <: IDEValue]() extends EdgeFunction[Value] { + override def compute(sourceValue: Value): Value = + sourceValue + + override def composeWith(secondEdgeFunction: EdgeFunction[Value]): EdgeFunction[Value] = + secondEdgeFunction + + override def meetWith(otherEdgeFunction: EdgeFunction[Value]): EdgeFunction[Value] = { + if (otherEdgeFunction.equalTo(this) || otherEdgeFunction.isInstanceOf[AllTopEdgeFunction[Value]]) { + this + } else if (otherEdgeFunction.isInstanceOf[AllBottomEdgeFunction[Value]]) { + otherEdgeFunction + } else { + otherEdgeFunction.meetWith(this) + } + } + + override def equalTo(otherEdgeFunction: EdgeFunction[Value]): Boolean = + otherEdgeFunction == this || otherEdgeFunction.isInstanceOf[IdentityEdgeFunction[Value]] +} + +/** + * Special edge function representing an edge function where all source values evaluate to the top element. Implementing + * [[composeWith]] is left to the user, as it requires knowledge of the other possible edge functions. + */ +abstract case class AllTopEdgeFunction[Value <: IDEValue](private val top: Value) extends EdgeFunction[Value] { + override def compute(sourceValue: Value): Value = + top + + override def meetWith(otherEdgeFunction: EdgeFunction[Value]): EdgeFunction[Value] = + otherEdgeFunction + + override def equalTo(otherEdgeFunction: EdgeFunction[Value]): Boolean = + otherEdgeFunction == this || + otherEdgeFunction.isInstanceOf[AllTopEdgeFunction[Value]] && + otherEdgeFunction.asInstanceOf[AllTopEdgeFunction[Value]].top == top +} + +/** + * Special edge function representing an edge function where all source values evaluate to the bottom element. + * Implementing [[composeWith]] is left to the user, as it requires knowledge of the other possible edge functions. + */ +abstract case class AllBottomEdgeFunction[Value <: IDEValue](private val bottom: Value) extends EdgeFunction[Value] { + override def compute(sourceValue: Value): Value = + bottom + + override def meetWith(otherEdgeFunction: EdgeFunction[Value]): EdgeFunction[Value] = + this + + override def equalTo(otherEdgeFunction: EdgeFunction[Value]): Boolean = + otherEdgeFunction == this || + otherEdgeFunction.isInstanceOf[AllBottomEdgeFunction[Value]] && + otherEdgeFunction.asInstanceOf[AllBottomEdgeFunction[Value]].bottom == bottom +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala new file mode 100644 index 0000000000..49dd8ac7f4 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala @@ -0,0 +1,13 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.problem + +/** + * Interface representing IDE flow functions + */ +trait FlowFunction[Fact <: IDEFact] { + /** + * Compute the facts that are generated by this flow function when seeing a fact + * @param sourceFact the incoming fact + */ + def compute(sourceFact: Fact): collection.Set[Fact] +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEFact.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEFact.scala new file mode 100644 index 0000000000..05084e66a3 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEFact.scala @@ -0,0 +1,7 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.problem + +/** + * Interface representing IDE facts + */ +trait IDEFact diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala new file mode 100644 index 0000000000..a29b4bcce3 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -0,0 +1,109 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.problem + +import org.opalj.fpcf.Entity + +/** + * Interface for modelling IDE problems + */ +abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity] { + /** + * The null fact to use. Also used to bootstrap the analysis at the entry points. + */ + def nullFact: Fact + + /** + * The lattice that orders the used values + */ + def lattice: MeetLattice[Value] + + /** + * Generate a flow function for a normal flow + * @param source where the normal flow starts + * @param target where the normal flow ends + */ + def getNormalFlowFunction(source: Statement, target: Statement): FlowFunction[Fact] + + /** + * Generate a flow function for a call flow + * @param callSite where the call flow starts (always a call statement) + * @param calleeEntry where the callable starts (the statement which the callable is started with) + * @param callee the callable that is called + */ + def getCallFlowFunction(callSite: Statement, calleeEntry: Statement, callee: Callable): FlowFunction[Fact] + + /** + * Generate a flow function for a return flow + * @param calleeExit where the return flow starts (the statement the callable is exited with) + * @param callee the callable that is returned from + * @param returnSite where the return flow ends (e.g. the next statement after the call in the callers code) + */ + def getReturnFlowFunction(calleeExit: Statement, callee: Callable, returnSite: Statement): FlowFunction[Fact] + + /** + * Generate a flow function for a call-to-return flow + * @param callSite where the call-to-return flow starts (always a call statement) + * @param returnSite where the call-to-return flow ends (e.g. the next statement after the call) + */ + def getCallToReturnFlowFunction(callSite: Statement, returnSite: Statement): FlowFunction[Fact] + + /** + * Generate an edge function for a normal flow + * @param source where the normal flow starts + * @param sourceFact the fact the flow starts with + * @param target where the normal flow ends + * @param targetFact the fact the flow ends with + */ + def getNormalEdgeFunction( + source: Statement, + sourceFact: Fact, + target: Statement, + targetFact: Fact + ): EdgeFunction[Value] + + /** + * Generate an edge function for a call flow + * @param callSite where the call flow starts (always a call statement) + * @param callSiteFact the fact the flow starts with + * @param calleeEntry where the callable starts (the statement which the callable is started with) + * @param calleeEntryFact the fact the flow ends with + * @param callee the callable that is called + */ + def getCallEdgeFunction( + callSite: Statement, + callSiteFact: Fact, + calleeEntry: Statement, + calleeEntryFact: Fact, + callee: Callable + ): EdgeFunction[Value] + + /** + * Generate an edge function for a return flow + * @param calleeExit where the return flow starts (the statement the callable is exited with) + * @param calleeExitFact the fact the flow starts with + * @param callee the callable that is returned from + * @param returnSite where the return flow ends (e.g. the next statement after the call in the callers code) + * @param returnSiteFact the fact the flow ends with + */ + def getReturnEdgeFunction( + calleeExit: Statement, + calleeExitFact: Fact, + callee: Callable, + returnSite: Statement, + returnSiteFact: Fact + ): EdgeFunction[Value] + + /** + * Generate an edge function for a call-to-return flow + * @param callSite where the call-to-return flow starts (always a call statement) + * @param callSiteFact the fact the flow starts with + * @param returnSite where the call-to-return flow ends (e.g. the next statement after the call) + * @param returnSiteFact the fact the flow ends with + */ + def getCallToReturnEdgeFunction( + callSite: Statement, + callSiteFact: Fact, + returnSite: Statement, + returnSiteFact: Fact + ): EdgeFunction[Value] +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEValue.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEValue.scala new file mode 100644 index 0000000000..16a882c52a --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEValue.scala @@ -0,0 +1,7 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.problem + +/** + * Interface representing IDE values + */ +trait IDEValue diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/MeetLattice.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/MeetLattice.scala new file mode 100644 index 0000000000..53d76c9365 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/MeetLattice.scala @@ -0,0 +1,22 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.problem + +/** + * Interface representing the lattice that orders the IDE values + */ +trait MeetLattice[Value <: IDEValue] { + /** + * The top value of the lattice + */ + def top: Value + + /** + * The bottom value of the lattice + */ + def bottom: Value + + /** + * Compute the result of meeting two values + */ + def meet(x: Value, y: Value): Value +} From fdf5928104e64d107b0ac8b3f435997a106eff40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 10 Jun 2024 11:52:39 +0200 Subject: [PATCH 003/167] First working solver --- .../org/opalj/ide/problem/IDEProblem.scala | 5 +- .../scala/org/opalj/ide/solver/ICFG.scala | 51 ++ .../org/opalj/ide/solver/IDEAnalysis.scala | 575 ++++++++++++++++++ 3 files changed, 630 insertions(+), 1 deletion(-) create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index a29b4bcce3..5f785fb139 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -2,11 +2,14 @@ package org.opalj.ide.problem import org.opalj.fpcf.Entity +import org.opalj.ide.solver.ICFG /** * Interface for modelling IDE problems */ -abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity] { +abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( + val icfg: ICFG[Statement, Callable] +) { /** * The null fact to use. Also used to bootstrap the analysis at the entry points. */ diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala new file mode 100644 index 0000000000..790cc45cef --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala @@ -0,0 +1,51 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.solver + +import org.opalj.fpcf.Entity + +/** + * Interface representing the interprocedural control flow graph + */ +trait ICFG[Statement, Callable <: Entity] { + /** + * Get all statements a callable can be entered at + */ + def getStartStatements(callable: Callable): Set[Statement] + + /** + * Get all statements that can directly follow the given one + */ + def getNextStatements(stmt: Statement): Set[Statement] + + /** + * Check whether a statement exits a callable in a normal way (e.g. with a return) + */ + def isNormalExitStatement(stmt: Statement): Boolean + + /** + * Check whether a statement exits a callable in an abnormal way (e.g. by throwing an exception) + */ + def isAbnormalExitStatement(stmt: Statement): Boolean + + /** + * Check whether a statement is a call statement + */ + def isCallStatement(stmt: Statement): Boolean + + /** + * Get all possible callees a call statement could call + */ + def getCalleesIfCallStatement(stmt: Statement): Option[Set[? <: Callable]] + + /** + * Get the callable a statement belongs to + */ + def getCallable(stmt: Statement): Callable + + /** + * Build a string representation of a statement. Only used for debugging purposes! + * @param indent to use on newlines (e.g. indentation for prettier logs) + * @param short whether to build a long or a more compact string + */ + def stringifyStatement(stmt: Statement, indent: String = "", short: Boolean = false): String +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala new file mode 100644 index 0000000000..16ea6c3dc9 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -0,0 +1,575 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.solver + +import scala.collection.mutable + +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.fpcf.Entity +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.Result +import org.opalj.ide.ConfigKeyDebugLog +import org.opalj.ide.ConfigKeyTraceLog +import org.opalj.ide.FrameworkName +import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.ide.problem.AllTopEdgeFunction +import org.opalj.ide.problem.EdgeFunction +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IdentityEdgeFunction +import org.opalj.ide.problem.IDEProblem +import org.opalj.ide.problem.IDEValue +import org.opalj.log.OPALLogger + +/** + * Basic solver for IDE problems. Uses the exhaustive/forward algorithm that was presented in the original IDE paper + * from 1996 as base. + */ +class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( + val project: SomeProject, + val problem: IDEProblem[Fact, Value, Statement, Callable], + val propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] +) extends FPCFAnalysis { + private type Node = (Statement, Fact) + /** + * A 'path' in the graph, denoted by it's start and end node as done in the IDE algorithm + */ + private type Path = (Node, Node) + + private type PathWorkList = mutable.Queue[Path] + + private type JumpFunction = EdgeFunction[Value] + private type JumpFunctions = mutable.Map[Path, JumpFunction] + private type SummaryFunction = EdgeFunction[Value] + private type SummaryFunctions = mutable.Map[Path, SummaryFunction] + + private type NodeWorkList = mutable.Queue[Node] + + private type Values = mutable.Map[Node, Value] + + private val identityEdgeFunction = new IdentityEdgeFunction[Value] + private val allTopEdgeFunction = new AllTopEdgeFunction[Value](problem.lattice.top) { + override def composeWith(secondEdgeFunction: EdgeFunction[Value]): EdgeFunction[Value] = { + /* This method cannot be implemented correctly without knowledge about the other possible edge functions. + * It will never be called on this instance anyway. However, we throw an exception here to be safe. */ + throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") + } + } + + /** + * Container class for simpler interaction and passing of the 'shared' data + */ + private class State { + /** + * The work list for paths used in P1 + */ + private val pathWorkList: PathWorkList = mutable.Queue.empty + + /** + * The jump functions (incrementally calculated) in P1 + */ + private val jumpFunctions: JumpFunctions = mutable.Map.empty + + /** + * The summary functions (incrementally calculated) in P1 + */ + private val summaryFunctions: SummaryFunctions = mutable.Map.empty + + /** + * Collection of all callables that were visited in P1 + */ + private val seenCallables = mutable.Set.empty[Callable] + + /** + * Map call targets to all possible (resp. seen) call sources (similar to a call graph but reversed) + */ + private val callTargetsToSources = mutable.Map.empty[Node, mutable.Set[Node]] + + /** + * The work list for nodes used in P2 + */ + private val nodeWorkList: NodeWorkList = mutable.Queue.empty + + /** + * Store all calculated (intermediate) values + */ + private val values: Values = mutable.Map.empty + + def enqueuePath(path: Path): Unit = { + pathWorkList.enqueue(path) + } + + def dequeuePath(): Path = { + pathWorkList.dequeue() + } + + def isPathWorkListEmpty: Boolean = { + pathWorkList.isEmpty + } + + def getPathWorkListSize: Int = { + pathWorkList.size + } + + def setJumpFunction(path: Path, jumpFunction: JumpFunction): Unit = { + jumpFunctions.put(path, jumpFunction) + } + + def getJumpFunction(path: Path): JumpFunction = { + jumpFunctions.getOrElse(path, allTopEdgeFunction) // else part handles IDE lines 1 - 2 + } + + def lookupJumpFunctions( + source: Option[Statement] = None, + sourceFact: Option[Fact] = None, + target: Option[Statement] = None, + targetFact: Option[Fact] = None + ): collection.Map[Path, JumpFunction] = { + // TODO (IDE) THIS COULD BE OPTIMIZED TO SPEEDUP THE ANALYSIS + jumpFunctions.filter { + case (((s, sf), (t, tf)), _) => + source.forall { source => s == source } && + sourceFact.forall { sourceFact => sf == sourceFact } && + target.forall { target => t == target } && + targetFact.forall { targetFact => tf == targetFact } + } + } + + def setSummaryFunction(path: Path, summaryFunction: SummaryFunction): Unit = { + summaryFunctions.put(path, summaryFunction) + } + + def getSummaryFunction(path: Path): SummaryFunction = { + summaryFunctions.getOrElse(path, allTopEdgeFunction) // else part handels IDE lines 3 - 4 + } + + def rememberCallable(callable: Callable): Unit = { + seenCallables.add(callable) + } + + def getAllSeenCallables: collection.Set[Callable] = { + seenCallables + } + + def rememberCallEdge(path: Path): Unit = { + val (source, target) = path + + val set = callTargetsToSources.getOrElseUpdate(target, mutable.Set.empty) + set.add(source) + } + + def lookupCallSourcesForTarget(target: Statement, targetFact: Fact): collection.Set[Node] = { + callTargetsToSources.getOrElse((target, targetFact), collection.Set.empty) + } + + def enqueueNode(node: Node): Unit = { + nodeWorkList.enqueue(node) + } + + def dequeueNode(): Node = { + nodeWorkList.dequeue() + } + + def isNodeWorkListEmpty: Boolean = { + nodeWorkList.isEmpty + } + + def getNodeWorkListSize: Int = { + nodeWorkList.size + } + + def getValue(node: Node): Value = { + values.getOrElse(node, problem.lattice.top) // else part handles IDE line 1 + } + + def setValue(node: Node, newValue: Value): Unit = { + values.put(node, newValue) + } + + def collectResults(callable: Callable): collection.Map[Statement, collection.Set[(Fact, Value)]] = { + values.filter { + case ((n, d), _) => + icfg.getCallable(n) == callable && icfg.isNormalExitStatement(n) && d != problem.nullFact + }.groupMapReduce { + case ((n, _), _) => n + } { + case ((_, d), value) => Set((d, value)) + } { + (s1, s2) => s1.union(s2) + } + } + } + + private val icfg: ICFG[Statement, Callable] = problem.icfg + + private val isDebug: Boolean = project.config.getBoolean(ConfigKeyDebugLog) + private val isTrace: Boolean = project.config.getBoolean(ConfigKeyTraceLog) + + private def nodeToString(node: Node, indent: String = ""): String = { + s"Node(\n$indent\t${icfg.stringifyStatement(node._1, s"$indent\t")},\n$indent\t${node._2}\n$indent)" + } + + private def pathToString(path: Path, indent: String = ""): String = { + s"Path(\n$indent\t${nodeToString(path._1, s"$indent\t")} ->\n$indent\t${nodeToString(path._2, s"$indent\t")}\n$indent)" + } + + private def logWarn(message: => String): Unit = { + OPALLogger.warn(FrameworkName, message) + } + + private def logDebug(message: => String): Unit = { + OPALLogger.debug({ isDebug }, s"$FrameworkName - debug", message) + } + + private def logTrace(message: => String): Unit = { + OPALLogger.debug({ isTrace }, s"$FrameworkName - trace", message) + } + + /** + * Run the IDE solver and calculate (and return) the results at the end of the requested callable + * @param callable the callable to analyze + */ + // TODO (IDE) WHAT HAPPENS WHEN ANALYZING MULTIPLE CALLABLES? CAN WE CACHE E.G. JUMP/SUMMARY FUNCTIONS? + def performAnalysis(callable: Callable): ProperPropertyComputationResult = { + implicit val state: State = new State + + logDebug("starting phase 1") + + // Phase 1 + seedPhase1(callable) + processPathWorkList() + + logDebug("finished phase 1") + + logDebug("starting phase 2") + + // Phase 2 + seedPhase2(callable) + computeValues() + + logDebug("finished phase 2") + + logDebug("collecting results for property creation") + + // Build and return result + val property = propertyMetaInformation.createProperty(state.collectResults(callable)) + Result(callable, property) + } + + private def seedPhase1(callable: Callable)(implicit s: State): Unit = { + s.rememberCallable(callable) + + // IDE P1 lines 5 - 6 + icfg.getStartStatements(callable).foreach { stmt => + val path = ((stmt, problem.nullFact), (stmt, problem.nullFact)) + s.enqueuePath(path) + s.setJumpFunction(path, identityEdgeFunction) + } + + logDebug(s"seeded with ${s.getPathWorkListSize} path(s)") + } + + private def processPathWorkList()(implicit s: State): Unit = { + while (!s.isPathWorkListEmpty) { // IDE P1 line 7 + val path = s.dequeuePath() // IDE P1 line 8 + val ((_, _), (n, _)) = path + val f = s.getJumpFunction(path) // IDE P1 line 9 + + logDebug("\nprocessing next path") + logTrace(s"path=${pathToString(path)}") + logTrace(s"current jumpFunction=$f") + + icfg.getCalleesIfCallStatement(n) match { // IDE P1 line 11 + case Some(qs) => + processCallFlow(path, f, qs) + case None if icfg.isNormalExitStatement(n) => // IDE P1 line 19 + processExitFlow(path, f) + case None => // IDE P1 line 30 + processNormalFlow(path, f) + } + + logDebug(s"${s.getPathWorkListSize} path(s) remaining after processing last path") + } + } + + private def processCallFlow(path: Path, f: JumpFunction, qs: collection.Set[? <: Callable])(implicit + s: State + ): Unit = { + logDebug("processing as call flow") + + val ((sp, d1), (n, d2)) = path + + if (qs.isEmpty) { + // TODO (IDE) THIS CASE SHOULD NEVER OCCUR -> REQUIRES FURTHER DEBUGGING + logWarn(s"Statement ${icfg.stringifyStatement(n)} is detected as call statement but no callees were found!") + } + + qs.foreach { q => + logDebug(s"handling call target q=$q") + + // TODO (IDE) ALSO COLLECTS JRE METHODS -> P2 part (ii) MAY GET VERY SLOW + s.rememberCallable(q) + + val sqs = icfg.getStartStatements(q) + sqs.foreach { sq => + // IDE P1 lines 12 - 13 + val d3s = problem.getCallFlowFunction(n, sq, q).compute(d2) + + logTrace(s"generated the following d3s=$d3s for start statement sq=${icfg.stringifyStatement(sq)}") + + d3s.foreach { d3 => + s.rememberCallEdge(((n, d2), (sq, d3))) + propagate(((sq, d3), (sq, d3)), identityEdgeFunction) + } + } + + val rs = icfg.getNextStatements(n) // IDE P1 line 14 + rs.foreach { r => + val d3s = problem.getCallToReturnFlowFunction(n, r).compute(d2) + + logTrace(s"generated the following d3s=$d3s for return-site statement r=${icfg.stringifyStatement(r)}") + + // IDE P1 lines 15 - 16 + d3s.foreach { d3 => + propagate(((sp, d1), (r, d3)), f.composeWith(problem.getCallToReturnEdgeFunction(n, d2, r, d3))) + } + + // IDE P1 lines 17 - 18 + d3s.foreach { d3 => + val f3 = s.getSummaryFunction(((n, d2), (r, d3))) + if (!f3.equalTo(allTopEdgeFunction)) { + propagate(((sp, d1), (r, d3)), f.composeWith(f3)) + } + } + } + } + } + + private def processExitFlow(path: Path, f: JumpFunction)(implicit s: State): Unit = { + logDebug("processing as exit flow") + + val ((sp, d1), (n, d2)) = path + val p = icfg.getCallable(n) + + // IDE P1 line 20 + val callSources = s.lookupCallSourcesForTarget(sp, d1) + callSources.foreach { + case (c, d4) => + val rs = icfg.getNextStatements(c) + rs.foreach { r => + logDebug(s"handling calling statement c=${icfg.stringifyStatement(c)}, d4=$d4 and return-site statement r=${icfg.stringifyStatement(r)}") + + // IDE P1 line 21 + val d5s = problem.getReturnFlowFunction(n, p, r).compute(d2) + + logDebug(s"generated the following d5s=$d5s") + + d5s.foreach { d5 => + // IDE P1 lines 22 - 23 + val f4 = problem.getCallEdgeFunction(c, d4, sp, d1, p) + val f5 = problem.getReturnEdgeFunction(n, d2, p, r, d5) + + // IDE P1 line 24 + val callToReturnPath = ((c, d4), (r, d5)) + val oldSummaryFunction = s.getSummaryFunction(callToReturnPath) + val fPrime = f4.composeWith(f).composeWith(f5).meetWith(oldSummaryFunction) + + // IDE P1 lines 25 - 29 + if (!fPrime.equalTo(oldSummaryFunction)) { + s.setSummaryFunction(callToReturnPath, fPrime) + + val sqs = icfg.getStartStatements(icfg.getCallable(c)) + sqs.foreach { sq => + val jumpFunctionsMatchingTarget = + s.lookupJumpFunctions(source = Some(sq), target = Some(c), targetFact = Some(d4)) + jumpFunctionsMatchingTarget.foreach { + case (((_, d3), (_, _)), f3) if !f3.equalTo(allTopEdgeFunction) => + propagate(((sq, d3), (r, d5)), f3.composeWith(fPrime)) + } + } + } + } + } + } + } + + private def processNormalFlow(path: Path, f: JumpFunction)(implicit s: State): Unit = { + logDebug("processing as normal flow") + + val ((sp, d1), (n, d2)) = path + + // IDE P1 lines 31 - 32 + icfg.getNextStatements(n).foreach { m => + val d3s = problem.getNormalFlowFunction(n, m).compute(d2) + + logTrace(s"generated the following d3s=$d3s for next statement m=${icfg.stringifyStatement(m)}") + + d3s.foreach { d3 => + propagate(((sp, d1), (m, d3)), f.composeWith(problem.getNormalEdgeFunction(n, d2, m, d3))) + } + } + } + + private def propagate(e: Path, f: EdgeFunction[Value])(implicit s: State): Unit = { + logTrace(s"handling propagation for path=${pathToString(e)} and f=$f") + + // IDE P1 lines 34 - 37 + val oldJumpFunction = s.getJumpFunction(e) + val fPrime = f.meetWith(oldJumpFunction) + + if (!fPrime.equalTo(oldJumpFunction)) { + logTrace(s"updating and re-enqueuing path as oldJumpFunction=$oldJumpFunction != fPrime=$fPrime") + + s.setJumpFunction(e, fPrime) + s.enqueuePath(e) + } else { + logTrace(s"nothing to do as oldJumpFunction=$oldJumpFunction == fPrime=$fPrime") + } + } + + private def seedPhase2(callable: Callable)(implicit s: State): Unit = { + // IDE P2 lines 2 - 3 + icfg.getStartStatements(callable).foreach { stmt => + val node = (stmt, problem.nullFact) + s.enqueueNode(node) + s.setValue(node, problem.lattice.bottom) + } + + logDebug(s"seeded with ${s.getNodeWorkListSize} node(s)") + } + + // TODO (IDE) TO SPEEDUP THE ANALYSIS WE SHOULD ONLY CALCULATE THE VALUES, WE ARE INTERESTED IN/THE USER REQUESTED. + // ESPECIALLY THE VALUES CALCULATED BY P2 part (ii) ARE NEVER USED CURRENTLY + private def computeValues()(implicit s: State): Unit = { + logDebug("starting phase 2 (i)") + + // IDE P2 part (i) + while (!s.isNodeWorkListEmpty) { // IDE P2 line 4 + val node = s.dequeueNode() // IDE P2 line 5 + + logDebug("processing next node") + logTrace(s"node=${nodeToString(node)}") + + val (n, _) = node + + icfg.getCalleesIfCallStatement(n) match { // IDE P2 line 11 + case Some(qs) => + processCallNode(node, qs) + case None => // IDE P2 line 7 + processStartNode(node) + } + + logDebug(s"${s.getNodeWorkListSize} node(s) remaining after processing last node") + } + + logDebug("finished phase 2 (i)") + logDebug("starting phase 2 (ii)") + + // IDE P2 part (ii) + // IDE P2 lines 15 - 17 + s.getAllSeenCallables.foreach { p => + val sps = icfg.getStartStatements(p) + val ns = collectReachableStmts(sps, stmt => !icfg.isCallStatement(stmt)) + + // IDE P2 line 16 - 17 + sps.foreach { sp => + ns.foreach { n => + val jumpFunctionsMatchingTarget = s.lookupJumpFunctions(source = Some(sp), target = Some(n)) + jumpFunctionsMatchingTarget.foreach { + case (((_, dPrime), (_, d)), fPrime) if !fPrime.equalTo(allTopEdgeFunction) => + val nSharp = (n, d) + val vPrime = problem.lattice.meet( + s.getValue((n, d)), + fPrime.compute(s.getValue((sp, dPrime))) + ) + + logTrace(s"setting value of nSharp=${nodeToString(nSharp)} to vPrime=$vPrime") + + s.setValue(nSharp, vPrime) + } + } + } + } + + logDebug("finished phase 2 (ii)") + } + + /** + * Collect all statements that are reachable from a certain start set of statements + * @param originStmts the statements to start searchgin from + * @param filterPredicate an additional predicate the collected statements have to fulfill + */ + private def collectReachableStmts( + originStmts: collection.Set[Statement], + filterPredicate: Statement => Boolean + ): collection.Set[Statement] = { + val collectedStmts = mutable.Set.empty[Statement] + var workingStmts = originStmts + val seenStmts = mutable.Set.empty[Statement] + + while (workingStmts.nonEmpty) { + collectedStmts.addAll(workingStmts.filter(filterPredicate)) + seenStmts.addAll(workingStmts) + workingStmts = workingStmts.foldLeft(mutable.Set.empty[Statement]) { (nextStmts, stmt) => + nextStmts.addAll(icfg.getNextStatements(stmt)) + }.diff(seenStmts) + } + + collectedStmts + } + + private def processStartNode(node: Node)(implicit s: State): Unit = { + logDebug("processing as start node") + + val (n, d) = node + + // IDE P2 line 8 + val cs = collectReachableStmts(collection.Set(n), stmt => icfg.isCallStatement(stmt)) + + // IDE P2 lines 9 - 10 + cs.foreach { c => + val jumpFunctionsMatchingTarget = + s.lookupJumpFunctions(source = Some(n), sourceFact = Some(d), target = Some(c)) + jumpFunctionsMatchingTarget.foreach { + case (((_, _), (_, dPrime)), fPrime) if !fPrime.equalTo(allTopEdgeFunction) => + propagateValue((c, dPrime), fPrime.compute(s.getValue((n, d)))) + } + } + } + + private def processCallNode(node: Node, qs: collection.Set[? <: Callable])(implicit s: State): Unit = { + logDebug("processing as call node") + + val (n, d) = node + + // IDE P2 lines 12 - 13 + qs.foreach { q => + val sqs = icfg.getStartStatements(q) + sqs.foreach { sq => + val dPrimes = problem.getCallFlowFunction(n, sq, q).compute(d) + dPrimes.foreach { dPrime => + propagateValue( + (sq, dPrime), + problem.getCallEdgeFunction(n, d, sq, dPrime, q).compute(s.getValue(node)) + ) + } + } + } + } + + private def propagateValue(nSharp: Node, v: Value)(implicit s: State): Unit = { + logTrace(s"handling propagation for nSharp=${nodeToString(nSharp)} and v=$v") + + // IDE P2 lines 18 - 21 + val oldValue = s.getValue(nSharp) + val vPrime = problem.lattice.meet(v, oldValue) + + if (vPrime != oldValue) { + logTrace(s"updating and re-enqueuing node as oldValue=$oldValue != vPrime=$vPrime") + + s.setValue(nSharp, vPrime) + s.enqueueNode(nSharp) + } else { + logTrace(s"nothing to do as oldValue=$oldValue == vPrime=$vPrime") + } + } +} From 5bd32930667c5df04878cc3bd21bd13f41255f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 10 Jun 2024 12:00:46 +0200 Subject: [PATCH 004/167] Integrate IDE solver into OPAL --- .../integration/IDEAnalysisScheduler.scala | 39 +++++++++++++++++++ .../opalj/ide/integration/IDEProperty.scala | 34 ++++++++++++++++ .../IDEPropertyMetaInformation.scala | 19 +++++++++ 3 files changed, 92 insertions(+) create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala new file mode 100644 index 0000000000..001cd46dff --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala @@ -0,0 +1,39 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.integration + +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler +import org.opalj.fpcf.Entity +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyStore +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEValue +import org.opalj.ide.solver.IDEAnalysis + +/** + * A base scheduler for IDE analyses adding common default behavior + */ +abstract class IDEAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity] + extends FPCFLazyAnalysisScheduler { + override final type InitializationData = IDEAnalysis[Fact, Value, Statement, Callable] + + def property: IDEPropertyMetaInformation[Statement, Fact, Value] + + override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) + + override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + + override def register( + project: SomeProject, + propertyStore: PropertyStore, + analysis: IDEAnalysis[Fact, Value, Statement, Callable] + ): FPCFAnalysis = { + propertyStore.registerLazyPropertyComputation(property.key, analysis.performAnalysis) + analysis + } + + override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} + + override def afterPhaseCompletion(p: SomeProject, ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala new file mode 100644 index 0000000000..da6c71473a --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala @@ -0,0 +1,34 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.integration + +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyKey +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEValue + +/** + * Base class of properties that are produced by an IDE analysis + */ +abstract class IDEProperty[Statement, Fact <: IDEFact, Value <: IDEValue] extends Property + with IDEPropertyMetaInformation[Statement, Fact, Value] + +/** + * Basic implementation of [[IDEProperty]] that simply wraps the fact-value results of an IDE analysis + * @param results the results produced by the analysis + * @param propertyMetaInformation corresponding to the produced property + */ +class BasicIDEProperty[Statement, Fact <: IDEFact, Value <: IDEValue]( + val results: collection.Map[Statement, collection.Set[(Fact, Value)]], + val propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] +) extends IDEProperty[Statement, Fact, Value] { + + override type Self = propertyMetaInformation.Self + + override def key: PropertyKey[Self] = propertyMetaInformation.key + + override def toString: String = { + s"${PropertyKey.name(propertyMetaInformation.key)}:\n${results.map { case (stmt, result) => + s"\t$stmt:\n\t\t${result.map { case (fact, value) => s"($fact,$value)" }.mkString("\n\t\t")}" + }.mkString("\n")}" + } +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala new file mode 100644 index 0000000000..f52c092f86 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala @@ -0,0 +1,19 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.integration + +import org.opalj.fpcf.PropertyMetaInformation +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEValue + +/** + * Base interface of property meta information of IDE analyses. Creates [[BasicIDEProperty]] by default. + */ +trait IDEPropertyMetaInformation[Statement, Fact <: IDEFact, Value <: IDEValue] extends PropertyMetaInformation { + override type Self = BasicIDEProperty[Statement, Fact, Value] + + def createProperty( + results: collection.Map[Statement, collection.Set[(Fact, Value)]] + ): IDEProperty[Statement, Fact, Value] = { + new BasicIDEProperty(results, this) + } +} From 30084ecea85c7ff65e5d736fa28a88b0224e3901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 10 Jun 2024 15:50:15 +0200 Subject: [PATCH 005/167] Improve debugging possibilities --- .../ide/problem/FlowRecordingIDEProblem.scala | 232 ++++++++++++++++++ .../ide/solver/FlowRecordingIDEAnalysis.scala | 102 ++++++++ 2 files changed, 334 insertions(+) create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala new file mode 100644 index 0000000000..368b65f819 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -0,0 +1,232 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.problem + +import java.io.Writer +import scala.collection.mutable +import org.opalj.fpcf.Entity + +object FlowRecorderModes extends Enumeration { + type FlowRecorderMode = Value + + /** + * A node in the graph is only made up of a statement. The edges are annotated with the propagated facts. + */ + val NODE_AS_STMT: FlowRecorderModes.Value = Value + /** + * A node in the graph is the combination of a statement and a fact. + */ + val NODE_AS_STMT_AND_FACT: FlowRecorderModes.Value = Value +} + +/** + * Wrapper class for a normal IDE problem for debugging purposes. Records the flow paths the IDE solver takes for a + * given base problem as graph and writes it to a file in DOT format. + * DOT files can either be viewed with a suitable local program or online e.g. at + * [[https://dreampuf.github.io/GraphvizOnline]]. + * @param baseProblem the base problem that defines the flows and edge functions that should be analyzed + * @param uniqueFlowsOnly whether to drop or to keep duplicated flows + * @param recordEdgeFunctions whether to record edge functions too or just stick with the flow + */ +class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( + val baseProblem: IDEProblem[Fact, Value, Statement, Callable], + val recorderMode: FlowRecorderModes.FlowRecorderMode = FlowRecorderModes.NODE_AS_STMT, + val uniqueFlowsOnly: Boolean = true, + val recordEdgeFunctions: Boolean = false +) extends IDEProblem[Fact, Value, Statement, Callable](baseProblem.icfg) { + /** + * Wrapper class for flow functions doing the actual recording + */ + private class RecodingFlowFunction( + baseFlowFunction: FlowFunction[Fact], + val source: Statement, + val target: Statement, + val flowType: String + ) extends FlowFunction[Fact] { + override def compute(sourceFact: Fact): collection.Set[Fact] = { + val facts = baseFlowFunction.compute(sourceFact) + facts.foreach { fact => collectedFlows.addOne(createDotEdge(source, sourceFact, target, fact, flowType)) } + facts + } + } + + private type DotEdge = (Statement, Fact, Statement, Fact, String) + + private val collectedFlows = mutable.ListBuffer.empty[DotEdge] + + private val collectedEdgeFunctions = mutable.Map.empty[DotEdge, EdgeFunction[Value]] + + private var writer: Writer = null + + override def nullFact: Fact = baseProblem.nullFact + + override def lattice: MeetLattice[Value] = baseProblem.lattice + + override def getNormalFlowFunction(source: Statement, target: Statement): FlowFunction[Fact] = { + new RecodingFlowFunction(baseProblem.getNormalFlowFunction(source, target), source, target, "normal flow") + } + + override def getCallFlowFunction( + callSite: Statement, + calleeEntry: Statement, + callee: Callable + ): FlowFunction[Fact] = { + new RecodingFlowFunction( + baseProblem.getCallFlowFunction(callSite, calleeEntry, callee), + callSite, + calleeEntry, + "call flow" + ) + } + + override def getReturnFlowFunction( + calleeExit: Statement, + callee: Callable, + returnSite: Statement + ): FlowFunction[Fact] = { + new RecodingFlowFunction( + baseProblem.getReturnFlowFunction(calleeExit, callee, returnSite), + calleeExit, + returnSite, + "return flow" + ) + } + + override def getCallToReturnFlowFunction(callSite: Statement, returnSite: Statement): FlowFunction[Fact] = { + new RecodingFlowFunction( + baseProblem.getCallToReturnFlowFunction(callSite, returnSite), + callSite, + returnSite, + "call-to-return flow" + ) + } + + override def getNormalEdgeFunction( + source: Statement, + sourceFact: Fact, + target: Statement, + targetFact: Fact + ): EdgeFunction[Value] = { + val edgeFunction = baseProblem.getNormalEdgeFunction(source, sourceFact, target, targetFact) + collectedEdgeFunctions.put(createDotEdge(source, sourceFact, target, targetFact, "normal flow"), edgeFunction) + edgeFunction + } + + override def getCallEdgeFunction( + callSite: Statement, + callSiteFact: Fact, + calleeEntry: Statement, + calleeEntryFact: Fact, + callee: Callable + ): EdgeFunction[Value] = { + val edgeFunction = baseProblem.getCallEdgeFunction(callSite, callSiteFact, calleeEntry, calleeEntryFact, callee) + collectedEdgeFunctions.put( + createDotEdge(callSite, callSiteFact, calleeEntry, calleeEntryFact, "call flow"), + edgeFunction + ) + edgeFunction + } + + override def getReturnEdgeFunction( + calleeExit: Statement, + calleeExitFact: Fact, + callee: Callable, + returnSite: Statement, + returnSiteFact: Fact + ): EdgeFunction[Value] = { + val edgeFunction = + baseProblem.getReturnEdgeFunction(calleeExit, calleeExitFact, callee, returnSite, returnSiteFact) + collectedEdgeFunctions.put( + createDotEdge(calleeExit, calleeExitFact, returnSite, returnSiteFact, "return flow"), + edgeFunction + ) + edgeFunction + } + + override def getCallToReturnEdgeFunction( + callSite: Statement, + callSiteFact: Fact, + returnSite: Statement, + returnSiteFact: Fact + ): EdgeFunction[Value] = { + val edgeFunction = baseProblem.getCallToReturnEdgeFunction(callSite, callSiteFact, returnSite, returnSiteFact) + collectedEdgeFunctions.put( + createDotEdge(callSite, callSiteFact, returnSite, returnSiteFact, "call-to-return flow"), + edgeFunction + ) + edgeFunction + } + + private def createDotEdge( + source: Statement, + sourceFact: Fact, + target: Statement, + targetFact: Fact, + flowType: String + ): DotEdge = { + (source, sourceFact, target, targetFact, flowType) + } + + /** + * Start recording + * @param writer to write the graph to + */ + def startRecording(writer: Writer): Unit = { + this.writer = writer + collectedFlows.clear() + collectedEdgeFunctions.clear() + + writer.write("digraph G {\nnodesep=\"2.0\";\nranksep=\"1.5\";\n") + } + + private def stringifyDotEdge(dotEdge: DotEdge): String = { + val (source, sourceFact, target, targetFact, flowType) = dotEdge + + var fromNode: String = null + var toNode: String = null + var label: String = null + recorderMode match { + case FlowRecorderModes.NODE_AS_STMT => + fromNode = s"${icfg.stringifyStatement(source, short = true)}" + toNode = s"${icfg.stringifyStatement(target, short = true)}" + if (recordEdgeFunctions) { + label = s"$targetFact\\n($flowType),\\n${collectedEdgeFunctions(dotEdge)}" + } else { + label = s"$targetFact\\n($flowType)" + } + case FlowRecorderModes.NODE_AS_STMT_AND_FACT => + fromNode = s"(${icfg.stringifyStatement(source, short = true)}, $sourceFact)" + toNode = s"(${icfg.stringifyStatement(target, short = true)}, $targetFact)" + if (recordEdgeFunctions) { + label = s"$flowType,\\n${collectedEdgeFunctions(dotEdge)}" + } else { + label = flowType + } + } + + s"\t\"$fromNode\" -> \"$toNode\" [label=\"$label\"]\n" + } + + /** + * Stop recording and finish writing + */ + def stopRecording(): Unit = { + if (uniqueFlowsOnly) { + val seenFlows = mutable.Set.empty[String] + collectedFlows.foreach { dotEdge => + val stringDotEdge = stringifyDotEdge(dotEdge) + if (!seenFlows.contains(stringDotEdge)) { + seenFlows.add(stringDotEdge) + writer.write(stringDotEdge) + } + } + } else { + collectedFlows.foreach { dotEdge => writer.write(stringifyDotEdge(dotEdge)) } + } + + writer.write("}\n") + writer.flush() + + collectedFlows.clear() + collectedEdgeFunctions.clear() + } +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala new file mode 100644 index 0000000000..49b91b65e3 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala @@ -0,0 +1,102 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.solver + +import scala.annotation.tailrec + +import java.io.File +import java.io.FileWriter +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths + +import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.Entity +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.ide.problem.FlowRecorderModes +import org.opalj.ide.problem.FlowRecorderModes.FlowRecorderMode +import org.opalj.ide.problem.FlowRecordingIDEProblem +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEProblem +import org.opalj.ide.problem.IDEValue + +/** + * Wrapper class for a normal IDE analysis for debugging purposes. Records the flow paths the IDE solver takes for a + * given base problem as graph and writes it to a file in DOT format. + * DOT files can either be viewed with a suitable local program or online e.g. at + * [[https://dreampuf.github.io/GraphvizOnline]]. + * @param baseProblem the base problem that defines the flows and edge functions that should be analyzed + * @param path the location to write the resulting DOT file (either a file ending with `.dot` or a directory) + * @param uniqueFlowsOnly whether to drop or to keep duplicated flows + * @param recordEdgeFunctions whether to record edge functions too or just stick with the flow + */ +class FlowRecordingIDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( + project: SomeProject, + baseProblem: IDEProblem[Fact, Value, Statement, Callable], + propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value], + path: Option[Path] = None, + recorderMode: FlowRecorderMode = FlowRecorderModes.NODE_AS_STMT_AND_FACT, + uniqueFlowsOnly: Boolean = true, + recordEdgeFunctions: Boolean = true +) extends IDEAnalysis( + project, + new FlowRecordingIDEProblem(baseProblem, recorderMode, uniqueFlowsOnly, recordEdgeFunctions), + propertyMetaInformation + ) { + /** + * The passed IDE problem + */ + val flowRecordingProblem: FlowRecordingIDEProblem[Fact, Value, Statement, Callable] = + problem.asInstanceOf[FlowRecordingIDEProblem[Fact, Value, Statement, Callable]] + + /** + * Get the file to write the graph to. If [[path]] references a file then this method will return [[path]]. If + * [[path]] references a directory, a new filename is created (s.t. no file gets overwritten). + */ + private def getFile: File = { + lazy val className = { + val classFQN = project.projectClassFilesWithSources.head._1.thisType.fqn + classFQN.substring(classFQN.lastIndexOf('/') + 1) + } + + @tailrec + def getNextFreeWithBasePath(basePath: Path, index: Int = 1): Path = { + val pathToCheck = + if (index == 0) { + basePath.resolve(s"$className-flow-recording.dot") + } else { + basePath.resolve(s"$className-flow-recording-$index.dot") + } + + if (Files.exists(pathToCheck)) { + getNextFreeWithBasePath(basePath, index + 1) + } else { + pathToCheck + } + } + + val completePath = path match { + case Some(p) => + if (p.toUri.getPath.endsWith(".dot")) { + p + } else { + getNextFreeWithBasePath(p) + } + case None => getNextFreeWithBasePath(Paths.get(".")) + } + + completePath.toFile + } + + override def performAnalysis(callable: Callable): ProperPropertyComputationResult = { + val writer = new FileWriter(getFile) + + flowRecordingProblem.startRecording(writer) + val result = super.performAnalysis(callable) + flowRecordingProblem.stopRecording() + + writer.close() + + result + } +} From 5775d1a603779ecd3a0ea4a49bb3c6793388dee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 10 Jun 2024 15:54:30 +0200 Subject: [PATCH 006/167] Preparations to integrate IDE into TAC --- OPAL/ProjectDependencies.mmd | 1 + OPAL/ProjectDependencies.pdf | Bin 27786 -> 27480 bytes OPAL/ProjectDependencies.svg | 2 +- build.sbt | 1 + 4 files changed, 3 insertions(+), 1 deletion(-) diff --git a/OPAL/ProjectDependencies.mmd b/OPAL/ProjectDependencies.mmd index d43c52eb46..bfa819777f 100644 --- a/OPAL/ProjectDependencies.mmd +++ b/OPAL/ProjectDependencies.mmd @@ -54,6 +54,7 @@ flowchart BT de --> ai tac --> ifds + tac --> ide tac --> ai ll --> tac diff --git a/OPAL/ProjectDependencies.pdf b/OPAL/ProjectDependencies.pdf index 9fa874d11c1509b471ab752f3b53c37f4909009a..5c6e526c22a56a19b9ea13602112f2b413adf90c 100644 GIT binary patch delta 8597 zcmZu%c|276`?pn+C0mg_8dR8lNe0;~d&r)xWNaf_Sx#gp(kTj+h{!HvcP%9;`!0l} zvW$IAenVw^JKfsJGm% z;2zCZc`BJXKcclA1z7ntMkh0acV%v6RX@-q$Fm}XPvN^1NMTHZXo*tITYzgRr>UkS zaiW$$a=iE8R?nU-+ZW);3^TNDL|2pvet#{)lXjIB8=v>YxRN2OiVqkTR-?ZT#rrxA zu1;^yxI3q9`!CoRd~zd{s)lihh2r0zTb%V%3XNaun75A>D8hcHpOG1SF*n%6I~lP| z`$17-Mkw=j#Nk?F>yrbCu6qDYM&l&y91R$(OUCB)Qy146r};i-y%H72x3?rQ$v<<= zAKc<7V)e1JZ?bonJf>BmER*o5uIt_?Ym4m=U!9`g3JDl4-inTv=(&El`L%j?ebqby ze}XkvnmF2!xZIbwcztAHQ}V%u_TamQs&u|3%9*A0n#V07nqN7PQ{PMi=FJpwJgHb( zWZ|Tv@_1kuMTG5x9&Eh1vP)7c#xHhD~&_vI)9e)8O+{h^E$K}{~XQ;A0zZ>Z>) ztd=Ai^yST7@15JsJQDBL1a#*(a)EmSaMm^|ItP|73#?@M3>Rkw)Y(s$9O<~i*DZFK z-FfTaZMDp%O;e9wE8!7|bxQX0nGW9>Ut53Rn$#UEY2PdJRxQ!ZO{@Cs>mHtC@mSX%Nk&^ouEX}v z&e@tcE~hOQe(F(jNH||qT4EZy_5HDUea4{UMM}T*%X+E!+mT_ovJpOFkI5PHhY!9B zXPugUIOFAnih7GT?Ya4E9W!px3^PueFsqnXia?C@rQI$#gvfR%xbjNv8L8RivB#dr zhe85#6~Qwx4v6u+DnQ%=$DqXGzP$PCy@h+dx!I*<9aSa=`y3`?VX+ch18;Inrn}dC z3z81SVQ%d|;~9MUc>3g%xO;BwO&Tyxgv@bv-2GDQAm@W4(#7XP{F>W_)js0M8GFTp z?>?It(?ce`&O3kpzIvAD^n;miWqd8*Xogk9(~5W#rNz5I-C{Jd2VD|J;{1n`MrMMG16&7xlq@ZN`QhA}$Fer~ z7Id7`W>CO5!V}O$61BRsgv9*)Ss=(--i8t{`21XlGSG$n- z0K8$83++H!;H-wCY_no~VibR{^KCqRmZnt2im$@SyCIwmeI}b89_lg@B2QP|jkYn} zTrTlz{1G+kVl6B!j98faqQHok!sVBrl#cEzR^bp22OBiT#K?E?8cAYWzv0>=n^D7g z4Zy=wh=r|9OGoL@z5a_Ik1@I{>x!8j=Hn6WW62wv|CH)LHrx0_CfP|Q^t&by1$8vP zDR}7l3T3sXAG+U8yOOkW*LhFbVaYZF+V0zT2Nc#H4?V1pMVKvVAo%S=gZOTx{8fae z45O47!~;2N!#^pz9>udKF>3uk1YD1lH}=sSJSxYKY`}grht14Ya7^aK zbRa>(k23H@U@*(5!(1&B7o?#m9U|KT%|&zBAlEZ^k)(rKjN(C@b^AwyT!DY^$ez(O zh4)Gi-5H?ItTxQaBh8l>+B)5?Vyt+hWR3B7W#hSg^oFK-xWPBs*JE;@H{dc-D-_G+`sjHMsM)7`vjFkTw0s5E)ib!q^t+49>F%m3ALMhLaSt=| z&|G3Hx`UTg(CP29CUnKLU+sL#ZmoQAijq&^H6QoN<;eN#n8H%^g}#LNLz}ArGkZ<= zNQ285Za#5M^9RhI^EdPxG5vNt;%>{&nH;Utx#40WazWW6Zc{YX7vq;V@Bw_sS%2D# zet!GDxO#SS{3};#$KL%Md(HJV7^hS{oSgGgMOV@Iv({#`H}?s-Uw(xp&K5Y{YAF+> z!_%Z(R9Yzhz}vd#JD^%$`gQ4>bl*pH-vYdlt5b-p7G6Awvt|Ee(6u_kXwq-ExS6iS z#){ND-bSnO*OHLKfw-G*1U@{!&)Cavv1bTjJYGwCoyPE?Fza-{Ejei;a%9^4+=#Zm zk>)mDxxn6BV7SuXo7u>CdA7)X@oU*f$K^*q25Nv4+UYQTFte%a)pv#e9oae{F%J{Y zYeG#V*l5;9(q4 zeIXJ$82?iCqFT%Yx~l=p5*rg)M%8;}5173dHKxfOt;F=f&e7jXiQ%QAsbvzTyPfHG zSoyikftybJjD+t;k~bpWf0II5sRZAmfBNc!YTEKQNmPUw# zF{=DZGN%ipTM6_uHyn^Mwh^UEARlklB?LS6rXT9nc2u#KG!rx8Hco&ku*H&Z;6)h_2?pylMxVLr;U$x}w3x(y!ynZ`&Z11B zswwlCiffgEo6id;J&f~0Km5$^r{FfXF4siW95C{uaBUnbiT0UrsVCpa!AynLad12t z+p~}!dSLIPPp1!+ssN6sSbowd8pIafbXRYBd$_z<#XeEw?ePX0hBLCBjnhnXR_3Jn z5VXAj$0eRGY1daD^DfA(KlatG79RmAO{<@M{(zLR%{#*EbP1b}Cu~PAqyDt#=$XBx(W(nps^?oKTVAd1 zgBx)sQ+PGLXDOK+(&Hl=?T+?gD>iNnJw4}SHYBe{bHAvY^1VvK9A6cp^22WVUE@mR zJe_%`HHj53_NL3lScc9pM=saX0UF<`o}^6+c7 zXm<`wc!IKK3QQNw(C89#_y@|Xb~V*0B?m6zWd4`bFe)#Dw%yS z@?)}-Mf~!OzG*&{6WhIH5 z8rBbxQQ2D?1fVUhzS#&&-b%J|(h5|i4$Jl{g$>1`=qBOQU)37;+ju9h7D<(DZr!_iYd$V1Epiaax(HOP77rhqOWk4?;i?sK7)Ij*rxdv=Dq{ze;xZTHCwcWijX>7 z#`_mX!f9W2c$WZ<_WRz@YVgD>C5P9W9bB2zHrfR@k<@JX8J&*7c68_gKk=YH-rE4 z1U*s9kHWl-R~uiCElIg<<#?R!R~9R*t|FJ&4p}_aNFdD|d7|-=5m}+nn_54vSEY6& zDgMh)g=g4kgR3CTmZLhBPK;3TmXWgk2e0 zERP|ZZV4Ji`AL`>D~Z-bdx{2Wjt_SnHI+Sc()|LT7#oL~lIv2;qIe#9#G_I7!r-I4~b0U11qQ*}lH9+-j*^<3DoIORb0A-dz6jtX+{H zvB-ZDsT}IeoKR3zeUmNbT8?~{vf2C!&|7|~*s48IeNwgTpx|=>8XhbY?kA_*MnF@l zg*m$Mn`&@H%oEFOnQDGB-^rAKIrEjA-mE9>2dsWA`tx4=Gf`L%xwwMRYnIUk%h_T2DGf8EP|7wjgYVuT>jc|CP?qVS<~4GL#p_M} zw{}@Va2>$6Bfz!#CzIH!EDyO&nC44sP!QXB!I0oGg0`#K~P8h4J39*d|JTFKB~8*bvhTG`lGmPpQjqO`}`kgK4xmXle`D}iLe?&hu>g{!k3Pv{lOkN9Qpwbr(FI6uzVFvKc+s!$iE z$kR}|MhLiWKlhgQkY+oXuWh}4ixTag-Wi%>H$!jfBh;*z?4|a-qd1Q}=M-h?^O;qT z zdWiQ7$0CL@r>*O9_?^hDE6;AZy&{CQM5h(Jj36rXOwP5cjoW~s9nS5KpI0oZ4-#rR zpIE&I4ixm*Uox|dsE~BUDdL{Hg#MW6IX-LnMi7|k_7pTpJl^K5wRw2LUQHC>(e9e5 z5yTG`V9k6?N-cR>co#JGHTOTuo$lr}i+c0w{bD5`U|K0NBu_C{<0v5H0jI~GM~Ze= zigSs}e2;eh#Gsavmyln-&uedjSvG6x;jLs@212gYe9XF{qz$fEtNGi@1j^gtShkZU z)*b+z(vt?c**$70YIC&%oNAEF7@ERIhW*T*%qTznjMLpXeicbHSm%fHKd3~h_|*k`R!1AM zWs%tKN>bJyGCn`p5vR|F${F_ZE2oc|_rJ8nc=>Han?%d_Y(d() zTaRS=FL+)57 zh8y2)3H{;vN|2}+b!x2IKO~H=XX>_(@XUmIcYr83bh`O~&EvJ@d9JO`GsclEVmbSZ zRP-_~20dSR$n57hKg_TdzPUJ8@c35HM`d6#-#aBQZ!6ax?y+^aWYkBF>rPIlKYi?s zh+C*ikzMoN@dw=#>#g*yp%ZKCmzbvJ(=V43XLY9X?+luDDQ%lpSQT51>gcv|_zBIi z=!`FkT2^iQdH19e;n(xdsd%Sf`~D!N{GNP=uhk7fw9kc_f!BTHY0A7B!#*jFcCSI; z1*j<3Ex{@8-l*-J(u-UTh@GtXl3q}+WsDU$HU7^0Wu4Q;XZ5KU%6&$oos2)X4hDq< zyrmc!nU0h*T5RW0tdh&8SejQ6=x1MVeLlo;!tC&ZsNZFp?Y^;nxcZMgc_Jr<7ZK$j zJmfp7{cl_-=&3$}-cJ5jXugyz@0Z`KW_6`A*_J37%lclx^G6D|_UdNrv_Wd3|90VboaEs98{q-_T1U5HH1@3owJdd=6{` zxFnItWan(_V(q|$z=2jMVbGOdh7O4UpYnTv{erBZn7~yqSBMo%6wskVqQDt}9VJ^q zPwnIzzUxWlc5M>5$is-Qr z{s@Nx6=6~!^HCyrP>cmMI_kiN_%j<6@>e!_$uyOOaR;zff)y--=|agI5cdStBw3-<$$}@r zN^v46c$^vBE1?G&Vl3eWt!|g_1W6}YzzdSPbZ7+lT+)*cNFS_oTxh!R4JVH0XqY zYJJ&^aHZctau^pSf}}74Eesdd6P9pxv9*Kme2_>4#Nwz}l%y~cPQ_wC3Wi$>2c5%G zorBbogxxH3Ul3R>00qgRFjOZo5R0c`A$c@{iiPCSRLP^Ejac0FE2f!*TFoAy=t9!s8(p57y$i zKoG|P5Ri~%RPqEg#3Fa6Nq_{ARP_@eLF8^hXr792usbi^PV$d&jPcfgQ zhvRYJrOW3TFu0u|dimH16arG%`3O4;GyOV1ps_e;{l7UR9*qGdu80B%Bo+smvFj{) z$BKV(a4Z%M)wqj8;h+k4acDfWUv_bXox8h>!@;2#z%CNM)A#@8a7Y|gGC257+wHmz zv9pPHaacI@bto*Ehjj1S%JB_?N`_a6=?{6~WjpqAcU2?4+J@cCDD2sjeA+XVzPzo;Hy_=-%*yH;sx;^<&+rp`P?WI>UYSDlg>Xh z6hHSOdtQNV);n%E{aLH9*!_UKyBUI`FNzdz*9;ovcQA1tje-({5_z2UrCDfiyH^dwdfDY z_f7V;eo1fAKLdO_bO-XO>6!Q>#Y&=tu4nEKYnJwe+LF*Ls_B8(}-! zWY*F1Z&~~ceEdwgm0kt;AG@#fp00f49^VIJ+;*2(+XRPAvZTFtF>tRf7V&VXHCtyh z)l=cg37T&t+G^*c>m6Opbc9C~$T{h2O&>@L1Ww!^;GWqV5aW?u` zP1uRvCCQztie!`N13!jqDKsw(QIWwY$EK2Vk8`$Gi&Fb{Zocw5Sx1-1amTS&vB+k4 zg0)Z4=nZ+F&9yqkN|qU*W=3~Wqd#H{rOp>1@`_p0^*KG8Y?DT_j6}$&u#IU2tv~EV z_)xwh)>h_E z?0vJCFGFv9?dB~QrDn&k$^Llh+~6wxDTCc}aq1gYqKRlrSMch22Efv6{#P0}+LrmN z__3W;p;RiXq?5{MNFGBVd5XSM*157L-A#-34U4@rJr19A&oEgeYSd~yV!UVa9pBs2 zSZ(W~pJY2|@jl)xM?JzcQ6n>2Vlv?@3}+np`fko?r|atl_8W6APCXM+DVVQU@jU+X z%qjr_Y2vgd4(y$n>M@?)DDYkcDLlOA<^I@@aam@6)A+1aU-DO8hOtF)-4 zq2^MYK5K*hRbzpKh=$xtZeB5plA^yx0>cdxbiP!4RWY*}>6|_FvEA*}FQ>~Up)668 zIQJ|H*05IVSmX<96iX?>y-B4^ye|AFT<&i8%1<|^8^`y$g_T8soJ7uY2+?82to8E4*D8ZcA9`Abf>{8d= z^|7Ad)yBLFiaEJFb6bvNm99Z4+YnWI}I&?11v2`NH}B#2J4uJrpWI2s48xRkmF`}Taying^nTa{H5*DFCPY=DY}^~NY*NH|1n4 zU}L}CH$*PKbfjp?^;bq4(t@iDhWoRv4y8O*%ybA|tXo;)q@l4rwSl6a zD++N{Hj(YR)56|hnPz{6%X#dpB!UV2W6zsUhZlz=eUVIf&Y|34=w_ReAS)xfm}#8+ z5l|f}Da)gOw#H&!N83feX8C;8(=PjMs*AytC|fm!G5bVCye2P+I7KAo>BZ4B4?$O} zW0AH7f@%i(ITl!*!8fRgT?dIq2Acko)|-8a`mRs-{>TCNJEwrhW!lP!0rRYY0iIW~ zju@x&({xlzHPh@f`p;d+J+7!Wi#bO!&m>Yvvc=2upQ?W4u=!AUj`}$^jF&7lbVxyU69tGVVp=>Cn2 z-7hU9Wiq)aj5O>Qh2^nt)E%Me{M+m$m-H2~GV)1j61f!BswB&iab)vDjknhFwq;7Q z?|RX%bpD!s#L$WZ?2}P?>>{yXt>$Qkdn@k0rmwx+=yK$=A*F%$)K-UMlRCdI!+p!! z&h1WYVLf-%T~wzHx%7rR6DBXTglA~vR8w-D_KzG}PcC9aud3eS6pyZURZeU_HO{mM}49lhp&6ANS1few31w);4_fvfxA#C%lw1*wYpEV zE0;F9Jj<^hq?2^qHblTSuNp_ zUr_%~sOu=`!v+KL{Z%bfbhTe_9-pOm#!4j ztPkk6c1iOYUR-6oCve1`@=c2XkAjHgHhOl1fiF8^*+aQUq$cA`t=jeBEUx7v3mKB1 z3m5Dr*mn)chKH%KL`~P-5;Bm1$RqvS*fj8I@H3teQ1}qD>F~_^7k%MIZ`ly51F9 zA&u%)y&$T??fy^qb9m-S#RL2@mP+SO2OrB~1cC-j6ei}(19Hm?61Ns7$~;4Ti@a~} zn|d(GNsW@uJb%zL+ACJ@_RbY5B#_HaF?x{lI^~_mf~a_D6)eb z)KmcSw`YO|9pj}2HT53+F=>R!`^N-Srqrt*av2SNqkHXY6Op%)<77z6S^RpPB~$Sl z*CxUyFfUPkV7?DKf^tv9=_U*7F};jx`(pox$w5Q8$Lk!SNv0R~NJ z$1;KVPDwjcfs5StySCxL3ML*)@ z^zG|5Ng|a8&ke(0x(bS+@@Lkp1}zsNI<|h8*~bOYa&(mEeQ3TJ59@mJ*M0HN^+ERg zDp4qD>Z8cQc660CUiq=}>Jy3)gQ4iBw>w+H_w0R$9C;wglacq@$Cj0y$n+`GfQSpR zRLAHPvFM5JF|O(`65peHfi0|IUE>bJO~=h7&ueTi&BM6+-pl84s~dReF@Kim(GG8D zmyVOhNKX{g##d+#2)f?K)+deWE(`mE|C%2Ze@adslTkmu>>{mGK6`g8h4YKxa>!mV zCa(O&uYI@WPNC`P2f&fSdvr%)FcWl{%87Wf>G4nrGtXf?9<{p$7ccsAYKfQioj`7p z+EdEYa;gh*sUO2Q{}`ejQ2SCy_wz8lLYdb~j%HiR)cs54B^0ugDKp+ykZH8%lr~X>= zU^gxAnEE^!`NRA6Y|j0hPU9o;o&Xn(ye)T_=}Dn)?_msaI`_eKgj;V=gmqmkeqs>t z-x?nbcdgnR|Dm`$!{a_)zEF8EN-sUQORk|@Ahn(4&sKTFtJgcv#qZ&iVKaR+;2*HX zvX)ZGQMn8FGNm`OXi6+L0CYHpK-pA}_iEQ!MP~g}+aH%+f4EH9 z_+nIFJmXP2Sl<=*t{UoV@1k21>!K_RF9TC3%c$;b1YjvVjUUoZ(M zmw9Vf)_huRn6+QaG^y(a8!ygZedWV7kE&zR)MIjGKzfP5XH-|=JbimvTN#xmWm)H( zaprnV?#UyhlPiy9>gQsj?X2I7HAJ0gDvZ=xb>rR0+e}TN6899h9{A;#*I{fg!s{>` zz}GA#auau*6(K(Ln#M?1gNEPEdq2qMvwm-Bw4x4c@Jjka)vczV%#p&@`z~8$tg8*a zMGwqB4gwy}{k#!JKZswc+lt0@P`HuA?JKRVdGzLVSunm^Mf7?j`vq@gO4$dTFfz+L zw=H#^&Wpuss3)YkM}6S+zU)AQkP$+Q9kfiVjBf8mBeH-MSSD*SG{grU)ZLi=;lJC(V#L;@& z_#6I|fE>dr&-&5+bHOPefAYO|VShJRI1^uMEtC4e(th|}`wzQNWiw7K#tYF3N0$tK ztT%PThwRmlTU1dm=AJ7uzwK+iZ+2>O@08c&m6oT#TwF{M9^NZnB@(j3RlN|Y;A@?% zGuUWn5_gb<|B zXRLS)q^o6Furf_1#!OtStE`tDu$O9Hk(~P~8VY*&q+(-pWJOB_7!+f2cSe)w+tw)K z&dQz6DZ5R{^A%a85Y{)4wJ)D{Va*sPm^8XVdsRJpZN8nSR)SpDV)bfEBWXB0mf{M^ zNJ?`1<6YVLm2Rr0(z$p>vEXBueUm7q#KW{1*ZMoofTRq&0 zvF963ZhvPqZ@zsYJ#Nw+XxosGr5cMn?)v-^H`jn%2Fk&yK{o!a=TASQOp;rlx!yc@ zwO2fFuPuNQyFS~0F8^`b$AD_1(0U`e_R_fHE++$6qOolSTV@BE+2*S`i+dp1NOA1) zFtG0yFE6I0Gi>m3W~PgtBju)5=i0C;m%6=c2UXm0tzf0+VZ~)*quakO8>OIE^8+tZ z(tQ{&(-DZUcwDdTKXG|1u0GvxF-~gc?r{@CxR_Jj=Y6;S&u2I2=}Q#3*S9mv;Ki(+ zJA1Q;fa?-cOLr7@_R<-LC5DXsrRxGP0u^7>M1y4q-kd|qoA zF!fMTSMn|YQ>lzTFS(g+t7sRxQ|AM6%x3k<@d%GwEM-&Q1Lihf3dBOQg6%^F-wi^cyeyz#VXgJP91a%czRzMrb?6IS={Dbe|=k?L#ZTZP|L5AsSobo&HM$3 zuANA4q%SAMpV{F%rAWs=fV+LLk&ib0Tw7c!!=kNX6l4@g!Nw&x^SN~X-DQ6F2Wlq0 zk!reTiv50NwezSX8?iKn+R^^e#&N37*O~iS@H7$bTp0{i)}X6*%arN0$KKg@5017R z%#htG5$d9RL{D+XbRt3IY2IXYv-gxdKyNf>TCw3(ohCOjJTbyl7+@W$H0}R!{T4&W z{QXVERnkt?nC<%)o?e^$OmCzcSdn0O-h;BQfli?zbxqnNp;6tzr&IlFs@k0bgG%);Gg)0G{=?7=6IjG_mX3V!R^u0guVUQs5h@$dMH_({5CPi7O1btO1xni zqp}n58${*grtSCqYPnu~Kp$DBV3=RX`j~_iiv#u8d`Mtm4VyU~N=!k4+Q!A&)yjby zjstJOc|d)3EfNHn$nFj5u+f2B90sIt4Cu?xY2dw9z15@}6ND&xtfzO8&h5@hh`+%%` zbl?m>gyKMX0blSQ-*NDxfD^PtQjn0u9YG(kpP!Z#h5TKZw2&5|lmH=f$m6Gw4=5o> z3z`W#L7w%(=1@2h5nu3z5H0vx!~xV7rUL~;4M>p~&|B08tP`dMH$@%66cJj`P3#;U z68A?jXt0Hi7hDk2hL~i zArJ?4oxTJ`my+}aBPEW5?7BB+c;a(u&gq|K&772&wQADBP z5IyQ}c}O^+$IuqSGK(E94@nqM)L~_j2n0xiJ86L+&=G}3Ky>KC$|4a^@@S&agn>j8 z)r*9ZN25VFj#-STT*5%24=as?_Vqg)GY7s!eAJv&c87P9*YH= zYym!sHyn zP=wuk2*bmP{V-Ul0f+ps_``C*a0tR2{>RVdA1z@x7>q~-9Gq}I{!NHO6Dxy5B8gPM zq0qz_9uB%+X9BQ9spGJCA`Fip9EpEN$N!Hk@o)sO-*0UXt%WA_Hy`lt2*L0u!ayH# zgcymi|Di1&i$ClKhY=419Af`V3PQ`_FgW3s`71CShQ=LM0S+S`2sjLdA@(DlVmJ(g zJ**5IhQ$L!Af(`772r_X#26ewjG=KvD!`F{OxS-mD;z~Q9taQc?+%7z@kA=XiT55H z0fQZ$L^vEry!#PI!kJHa{*8fv9hxmje>eg`w11$6{EuS-MKoM+1e$0t;YbA0xWkcX z;ua&(I5==PLlmA^el!esc+CommoncommonStatic Analysis InfrastructuresiBytecode InfrastructurebiBytecode RepresentationbrBytecode DisassemblerdaIDEideIFDSifdsAbstract Interpretation FrameworkaiBytecode CreatorbcThree Address CodetacDependency ExtractiondeBytecode AssemblerbaLLVMllAPKapkArchitecture ValidationavFrameworkDemosBugPickerbpHermeshermes \ No newline at end of file +CommoncommonStatic Analysis InfrastructuresiBytecode InfrastructurebiBytecode RepresentationbrBytecode DisassemblerdaIDEideIFDSifdsAbstract Interpretation FrameworkaiBytecode CreatorbcThree Address CodetacDependency ExtractiondeBytecode AssemblerbaLLVMllAPKapkArchitecture ValidationavFrameworkDemosBugPickerbpHermeshermes \ No newline at end of file diff --git a/build.sbt b/build.sbt index 8f17b225b5..4b1d691433 100644 --- a/build.sbt +++ b/build.sbt @@ -336,6 +336,7 @@ lazy val `ThreeAddressCode` = (project in file("OPAL/tac")) ) .dependsOn(ai % "it->it;it->test;test->test;compile->compile") .dependsOn(ifds % "it->it;it->test;test->test;compile->compile") + .dependsOn(ide % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) lazy val ba = `BytecodeAssembler` From aa4a0f036674444d990139075578f5d29caca127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 12 Jun 2024 17:37:37 +0200 Subject: [PATCH 007/167] Add abstraction layer to reduce complexity of analyses for Java programs --- .../JavaIDEAnalysisScheduler.scala | 14 ++++ .../JavaIDEPropertyMetaInformation.scala | 13 ++++ .../analyses/ide/problem/JavaIDEProblem.scala | 16 ++++ .../fpcf/analyses/ide/solver/JavaICFG.scala | 74 +++++++++++++++++++ .../analyses/ide/solver/JavaIDEAnalysis.scala | 19 +++++ .../analyses/ide/solver/JavaStatement.scala | 45 +++++++++++ 6 files changed, 181 insertions(+) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala new file mode 100644 index 0000000000..b5f16bcc2a --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala @@ -0,0 +1,14 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.integration + +import org.opalj.br.Method +import org.opalj.ide.integration.IDEAnalysisScheduler +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEValue +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement + +/** + * Specialized IDE analysis scheduler for Java programs + */ +abstract class JavaIDEAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue] + extends IDEAnalysisScheduler[Fact, Value, JavaStatement, Method] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala new file mode 100644 index 0000000000..e66da84c9c --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala @@ -0,0 +1,13 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.integration + +import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEValue +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement + +/** + * Specialized IDE property meta information for Java programs + */ +trait JavaIDEPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue] + extends IDEPropertyMetaInformation[JavaStatement, Fact, Value] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala new file mode 100644 index 0000000000..e83e8b1268 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala @@ -0,0 +1,16 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.problem + +import org.opalj.br.Method +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEProblem +import org.opalj.ide.problem.IDEValue +import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement + +/** + * Specialized IDE problem for Java programs + */ +abstract class JavaIDEProblem[Fact <: IDEFact, Value <: IDEValue]( + override val icfg: JavaICFG +) extends IDEProblem[Fact, Value, JavaStatement, Method](icfg) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala new file mode 100644 index 0000000000..bbfcd76404 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala @@ -0,0 +1,74 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.solver + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.ide.solver.ICFG +import org.opalj.tac.fpcf.analyses.ifds.JavaForwardICFG + +/** + * Interprocedural control flow graph for Java programs + */ +class JavaICFG(project: SomeProject) extends ICFG[JavaStatement, Method] { + // TODO (IDE) CURRENTLY DEPENDS ON IMPLEMENTATION FROM IFDS + private val baseICFG = new JavaForwardICFG(project) + + override def getStartStatements(callable: Method): Set[JavaStatement] = + baseICFG.startStatements(callable).map { + case org.opalj.tac.fpcf.analyses.ifds.JavaStatement(method, index, code, cfg) => + JavaStatement(method, index, isReturnNode = false, code, cfg) + } + + override def getNextStatements(stmt: JavaStatement): Set[JavaStatement] = { + if (isCallStatement(stmt)) { + Set(JavaStatement(stmt.method, stmt.index, isReturnNode = true, stmt.code, stmt.cfg)) + } else { + baseICFG.nextStatements( + org.opalj.tac.fpcf.analyses.ifds.JavaStatement(stmt.method, stmt.index, stmt.code, stmt.cfg) + ).map { + case org.opalj.tac.fpcf.analyses.ifds.JavaStatement(method, index, code, cfg) => + JavaStatement(method, index, isReturnNode = false, code, cfg) + } + } + } + + override def isNormalExitStatement(stmt: JavaStatement): Boolean = { + stmt.index == stmt.basicBlock.asBasicBlock.endPC && + stmt.basicBlock.successors.exists(_.isNormalReturnExitNode) + } + + override def isAbnormalExitStatement(stmt: JavaStatement): Boolean = { + stmt.index == stmt.basicBlock.asBasicBlock.endPC && + stmt.basicBlock.successors.exists(_.isAbnormalReturnExitNode) + } + + override def isCallStatement(stmt: JavaStatement): Boolean = { + getCalleesIfCallStatement(stmt).nonEmpty + } + + // TODO (IDE) REFACTOR AS 'getCallees(...): Set[Method]' + override def getCalleesIfCallStatement(stmt: JavaStatement): Option[Set[Method]] = { + if (stmt.isReturnNode) { + None + } else { + baseICFG.getCalleesIfCallStatement( + org.opalj.tac.fpcf.analyses.ifds.JavaStatement(stmt.method, stmt.index, stmt.code, stmt.cfg) + ) + } + } + + override def getCallable(stmt: JavaStatement): Method = stmt.method + + def getCallablesCallableFromOutside: Set[Method] = { + baseICFG.methodsCallableFromOutside.map { declaredMethod => declaredMethod.asDefinedMethod.definedMethod } + } + + override def stringifyStatement(stmt: JavaStatement, indent: String = "", short: Boolean = false): String = { + val stringifiedStatement = stmt.toString + if (short) { + stringifiedStatement.substring(0, stringifiedStatement.indexOf("{")) + } else { + stringifiedStatement + } + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala new file mode 100644 index 0000000000..dd6049bae7 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala @@ -0,0 +1,19 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.solver + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEProblem +import org.opalj.ide.problem.IDEValue +import org.opalj.ide.solver.IDEAnalysis + +/** + * Solver for IDE problems specialized for Java programs + */ +class JavaIDEAnalysis[Fact <: IDEFact, Value <: IDEValue]( + project: SomeProject, + problem: IDEProblem[Fact, Value, JavaStatement, Method], + propertyMetaInformation: IDEPropertyMetaInformation[JavaStatement, Fact, Value] +) extends IDEAnalysis[Fact, Value, JavaStatement, Method](project, problem, propertyMetaInformation) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala new file mode 100644 index 0000000000..7fe8f5d5e8 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala @@ -0,0 +1,45 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.solver + +import org.opalj.br.Method +import org.opalj.br.cfg.BasicBlock +import org.opalj.br.cfg.CFG +import org.opalj.tac.Stmt +import org.opalj.tac.TACStmts +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V + +/** + * Class to model statements used with IDE analyses + * @param index the index of the statement in the code + * @param isReturnNode whether the statement models the return node of a call + */ +case class JavaStatement( + method: Method, + index: Int, + isReturnNode: Boolean = false, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] +) { + // TODO (IDE) DOES THIS GET REEVALUATED EACH CALL? DO WE ALWAYS CALL IT AT LEAST ONCE? MAYBE MAKE IT A (LAZY) + // PROPERTY + def stmt: Stmt[V] = code(index) + + // TODO (IDE) DOES THIS GET REEVALUATED EACH CALL? DO WE ALWAYS CALL IT AT LEAST ONCE? MAYBE MAKE IT A (LAZY) + // PROPERTY + def basicBlock: BasicBlock = cfg.bb(index) + + override def hashCode(): Int = method.hashCode() * 31 + index + + override def equals(obj: Any): Boolean = obj match { + case JavaStatement(method2, index2, isReturnNode2, _, _) => + method == method2 && index == index2 && isReturnNode == isReturnNode2 + case _ => false + } + + override def toString: String = { + val returnOptional = + if (isReturnNode) { "(return)" } + else { "" } + s"${method.classFile.thisType.simpleName}:${method.name}[$index]$returnOptional{$stmt}" + } +} From bfff8cb55fa0b37b4e1e57ee91178459a23b542a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 12 Jun 2024 17:58:21 +0200 Subject: [PATCH 008/167] Replace functions with properties --- .../org/opalj/ide/problem/FlowRecordingIDEProblem.scala | 6 +++--- .../src/main/scala/org/opalj/ide/problem/IDEProblem.scala | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index 368b65f819..ac4acdc52b 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -57,9 +57,9 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal private var writer: Writer = null - override def nullFact: Fact = baseProblem.nullFact + override val nullFact: Fact = baseProblem.nullFact - override def lattice: MeetLattice[Value] = baseProblem.lattice + override val lattice: MeetLattice[Value] = baseProblem.lattice override def getNormalFlowFunction(source: Statement, target: Statement): FlowFunction[Fact] = { new RecodingFlowFunction(baseProblem.getNormalFlowFunction(source, target), source, target, "normal flow") @@ -175,7 +175,7 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal collectedFlows.clear() collectedEdgeFunctions.clear() - writer.write("digraph G {\nnodesep=\"2.0\";\nranksep=\"1.5\";\n") + writer.write("digraph G {\n\tnodesep=\"2.0\";\n\tranksep=\"1.5\";\n") } private def stringifyDotEdge(dotEdge: DotEdge): String = { diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index 5f785fb139..92f29dbb97 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -5,7 +5,7 @@ import org.opalj.fpcf.Entity import org.opalj.ide.solver.ICFG /** - * Interface for modelling IDE problems + * Interface for modeling IDE problems */ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( val icfg: ICFG[Statement, Callable] @@ -13,12 +13,12 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl /** * The null fact to use. Also used to bootstrap the analysis at the entry points. */ - def nullFact: Fact + val nullFact: Fact /** * The lattice that orders the used values */ - def lattice: MeetLattice[Value] + val lattice: MeetLattice[Value] /** * Generate a flow function for a normal flow From 9970be921298af0f4e3605085c08c60430b72486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 14 Jun 2024 11:09:48 +0200 Subject: [PATCH 009/167] Add IDE version of linear constant propagation --- .../LinearConstantPropagationAnalysis.scala | 19 + ...ntPropagationPropertyMetaInformation.scala | 15 + ...nearConstantPropagationEdgeFunctions.scala | 74 ++++ .../LinearConstantPropagationFact.scala | 23 ++ .../LinearConstantPropagationLattice.scala | 25 ++ .../LinearConstantPropagationProblem.scala | 357 ++++++++++++++++++ .../LinearConstantPropagationValue.scala | 24 ++ 7 files changed, 537 insertions(+) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationFact.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationLattice.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationValue.scala diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysis.scala new file mode 100644 index 0000000000..6fec7dbb07 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysis.scala @@ -0,0 +1,19 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation + +import org.opalj.br.analyses.SomeProject +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationProblem +import org.opalj.tac.fpcf.analyses.ide.solver.JavaIDEAnalysis + +/** + * Linear constant propagation as IDE analysis + */ +class LinearConstantPropagationAnalysis(project: SomeProject) + extends JavaIDEAnalysis( + project, + new LinearConstantPropagationProblem(project), + LinearConstantPropagationPropertyMetaInformation + ) { + val lcpProblem: LinearConstantPropagationProblem = + problem.asInstanceOf[LinearConstantPropagationProblem] +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala new file mode 100644 index 0000000000..946edb1659 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala @@ -0,0 +1,15 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation + +import org.opalj.fpcf.PropertyKey +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEPropertyMetaInformation + +/** + * Meta information for linear constant propagation + */ +object LinearConstantPropagationPropertyMetaInformation + extends JavaIDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] { + final val key: PropertyKey[Self] = PropertyKey.create("IDELinearConstantPropagation") +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala new file mode 100644 index 0000000000..f3b349e32d --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala @@ -0,0 +1,74 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem + +import org.opalj.ide.problem.AllBottomEdgeFunction +import org.opalj.ide.problem.AllTopEdgeFunction +import org.opalj.ide.problem.EdgeFunction +import org.opalj.ide.problem.IdentityEdgeFunction + +/** + * Edge function to calculate the value of a variable `i` for a statement `val i = a * x + b` + */ +case class LinearCombinationEdgeFunction(a: Int, b: Int) + extends EdgeFunction[LinearConstantPropagationValue] { + override def compute(sourceValue: LinearConstantPropagationValue): LinearConstantPropagationValue = { + sourceValue match { + case ConstantValue(x) => ConstantValue(a * x + b) + case VariableValue if a == 0 => ConstantValue(b) + case VariableValue => VariableValue + case UnknownValue => UnknownValue + } + } + + override def composeWith( + secondEdgeFunction: EdgeFunction[LinearConstantPropagationValue] + ): EdgeFunction[LinearConstantPropagationValue] = { + secondEdgeFunction match { + case LinearCombinationEdgeFunction(a2, b2) => LinearCombinationEdgeFunction(a2 * a, a2 * b + b2) + case VariableValueEdgeFunction => secondEdgeFunction + + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => secondEdgeFunction + + case _ => + throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") + } + } + + override def meetWith( + otherEdgeFunction: EdgeFunction[LinearConstantPropagationValue] + ): EdgeFunction[LinearConstantPropagationValue] = otherEdgeFunction match { + case LinearCombinationEdgeFunction(a2, b2) if a2 == a && b2 == b => this + case LinearCombinationEdgeFunction(_, _) => VariableValueEdgeFunction + + case VariableValueEdgeFunction => otherEdgeFunction + + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => this + + case _ => + throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + } + + override def equalTo(otherEdgeFunction: EdgeFunction[LinearConstantPropagationValue]): Boolean = { + otherEdgeFunction == this || + (otherEdgeFunction match { + case otherLinearCombinationEdgeFunction: LinearCombinationEdgeFunction => + otherLinearCombinationEdgeFunction.a == a && otherLinearCombinationEdgeFunction.b == b + case _ => false + }) + } +} + +/** + * Edge function for a variable that is definitely not constant + */ +object VariableValueEdgeFunction extends AllBottomEdgeFunction[LinearConstantPropagationValue](VariableValue) { + override def composeWith( + secondEdgeFunction: EdgeFunction[LinearConstantPropagationValue] + ): EdgeFunction[LinearConstantPropagationValue] = secondEdgeFunction match { + case LinearCombinationEdgeFunction(0, _) => secondEdgeFunction + case LinearCombinationEdgeFunction(_, _) => this + case _ => this + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationFact.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationFact.scala new file mode 100644 index 0000000000..fa8021c923 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationFact.scala @@ -0,0 +1,23 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem + +import org.opalj.ide.problem.IDEFact + +/** + * Type for modeling facts for linear constant propagation + */ +trait LinearConstantPropagationFact extends IDEFact + +/** + * Fact to use as null fact + */ +case object NullFact extends LinearConstantPropagationFact + +/** + * Fact representing a seen variable + * @param name the name of the variable (e.g. `lv0`) + * @param definedAtIndex where the variable is defined (used to uniquely identify a variable/variable fact) + */ +case class VariableFact(name: String, definedAtIndex: Int) extends LinearConstantPropagationFact { + override def toString: String = s"VariableFact($name)" +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationLattice.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationLattice.scala new file mode 100644 index 0000000000..403dad281d --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationLattice.scala @@ -0,0 +1,25 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem + +import org.opalj.ide.problem.MeetLattice + +/** + * Lattice used for linear constant propagation + */ +object LinearConstantPropagationLattice extends MeetLattice[LinearConstantPropagationValue] { + override def top: LinearConstantPropagationValue = UnknownValue + + override def bottom: LinearConstantPropagationValue = VariableValue + + override def meet( + x: LinearConstantPropagationValue, + y: LinearConstantPropagationValue + ): LinearConstantPropagationValue = (x, y) match { + case (UnknownValue, y) => y + case (x, UnknownValue) => x + case (VariableValue, _) => VariableValue + case (_, VariableValue) => VariableValue + case (ConstantValue(xc), ConstantValue(yc)) if xc == yc => x + case _ => VariableValue + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala new file mode 100644 index 0000000000..32363788eb --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -0,0 +1,357 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem + +import scala.collection.immutable + +import org.opalj.BinaryArithmeticOperators +import org.opalj.ai.domain.l1.DefaultIntegerRangeValues +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.ide.problem.EdgeFunction +import org.opalj.ide.problem.FlowFunction +import org.opalj.ide.problem.IdentityEdgeFunction +import org.opalj.ide.problem.MeetLattice +import org.opalj.tac.ArrayLength +import org.opalj.tac.Assignment +import org.opalj.tac.BinaryExpr +import org.opalj.tac.Expr +import org.opalj.tac.GetField +import org.opalj.tac.GetStatic +import org.opalj.tac.IntConst +import org.opalj.tac.Var +import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem +import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem + +/** + * Definition of the linear constant propagation problem + */ +class LinearConstantPropagationProblem(project: SomeProject) + extends JavaIDEProblem[LinearConstantPropagationFact, LinearConstantPropagationValue]( + new JavaICFG(project) + ) { + private val identityEdgeFunction = new IdentityEdgeFunction[LinearConstantPropagationValue] + + /** + * @return all methods from the project class files that can be called from outside + */ + def getEntryPoints: Set[Method] = { + icfg.getCallablesCallableFromOutside + .filter { method => + val packageOfEntryPoint = method.classFile.thisType.packageName + project.allProjectClassFiles.exists { classFile => + packageOfEntryPoint.startsWith(classFile.thisType.packageName) + } + } + } + + override val nullFact: LinearConstantPropagationFact = + NullFact + + override val lattice: MeetLattice[LinearConstantPropagationValue] = + LinearConstantPropagationLattice + + override def getNormalFlowFunction( + source: JavaStatement, + target: JavaStatement + ): FlowFunction[LinearConstantPropagationFact] = { + (sourceFact: LinearConstantPropagationFact) => + { + source.stmt.astID match { + case Assignment.ASTID => + val assignment = source.stmt.asAssignment + if (isExpressionInfluencedByFact(assignment.expr, sourceFact)) { + /* Generate fact for target of assignment if the expression is influenced by the source + * fact */ + immutable.Set(sourceFact, VariableFact(assignment.targetVar.name, source.index)) + } else { + immutable.Set(sourceFact) + } + + case _ => immutable.Set(sourceFact) + } + } + } + + private def isExpressionInfluencedByFact( + expr: Expr[JavaIFDSProblem.V], + sourceFact: LinearConstantPropagationFact + ): Boolean = { + expr.astID match { + case IntConst.ASTID => + /* Only generate fact for constants from null fact */ + sourceFact == nullFact + + case BinaryExpr.ASTID => + val binaryExpr = expr.asBinaryExpr + val leftExpr = binaryExpr.left + val rightExpr = binaryExpr.right + + if (sourceFact == nullFact) { + /* Only generate fact by null fact for binary expressions if both subexpressions are influenced. + * This is needed for binary expressions with one constant and one variable. */ + isExpressionInfluencedByFact(leftExpr, sourceFact) + && isExpressionInfluencedByFact (rightExpr, sourceFact) + } else { + /* If source fact is not null fact, generate new fact if one subexpression is influenced by the + * source fact */ + isExpressionInfluencedByFact(leftExpr, sourceFact) + || isExpressionInfluencedByFact (rightExpr, sourceFact) + } + + case Var.ASTID => + val varExpr = expr.asVar + + val hasConstantValue = + varExpr.value match { + case intRange: DefaultIntegerRangeValues#IntegerRange => + intRange.lowerBound == intRange.upperBound + case _ => false + } + + sourceFact match { + case NullFact => + /* Generate fact by null fact for variables if it is definitely a constant */ + hasConstantValue + case VariableFact(_, definedAtIndex) => + /* Generate fact only if it is not detected as constant (by the value analysis) and the variable + * represented by the source fact is one possible initializer of the target variable */ + !hasConstantValue && varExpr.definedBy.contains(definedAtIndex) + } + + case ArrayLength.ASTID => + /* Generate for array length expressions only by null fact */ + sourceFact == nullFact + + case GetField.ASTID => + val getFieldExpr = expr.asGetField + /* Generate for field access expressions only by null fact and if field is of type integer */ + sourceFact == nullFact && getFieldExpr.declaredFieldType.isIntegerType + + case GetStatic.ASTID => + val getStaticExpr = expr.asGetStatic + /* Generate for field access expressions only by null fact and if field is of type integer */ + sourceFact == nullFact && getStaticExpr.declaredFieldType.isIntegerType + + case _ => false + } + } + + override def getCallFlowFunction( + callSite: JavaStatement, + calleeEntry: JavaStatement, + callee: Method + ): FlowFunction[LinearConstantPropagationFact] = { + (sourceFact: LinearConstantPropagationFact) => + { + /* Only propagate to callees that return integers */ + if (!callee.returnType.isIntegerType) { + immutable.Set.empty + } else { + sourceFact match { + case NullFact => + /* Always propagate null facts */ + immutable.Set(sourceFact) + + case VariableFact(_, definedAtIndex) => + val callStmt = JavaIFDSProblem.asCall(callSite.stmt) + + /* Parameters and their types (excluding the implicit 'this' reference) */ + val params = callStmt.params + val paramTypes = callee.descriptor.parameterTypes + + params + .zipWithIndex + .filter { case (param, index) => + /* Only parameters that are of type integer and where the variable represented by + * the source fact is one possible initializer */ + paramTypes(index).isIntegerType && param.asVar.definedBy.contains(definedAtIndex) + } + .map { case (_, index) => + VariableFact(s"param${index + 1}", -(index + 2)) + } + .toSet + } + } + } + } + + override def getReturnFlowFunction( + calleeExit: JavaStatement, + callee: Method, + returnSite: JavaStatement + ): FlowFunction[LinearConstantPropagationFact] = { + (sourceFact: LinearConstantPropagationFact) => + { + /* Only propagate to return site if callee returns an integer */ + if (!callee.returnType.isIntegerType) { + immutable.Set.empty + } else { + sourceFact match { + case NullFact => + /* Always propagate null fact */ + immutable.Set(sourceFact) + + case VariableFact(_, definedAtIndex) => + returnSite.stmt.astID match { + case Assignment.ASTID => + val assignment = returnSite.stmt.asAssignment + + val returnExpr = calleeExit.stmt.asReturnValue.expr + /* Only propagate if the variable represented by the source fact is one possible + * initializer of the variable at the return site */ + if (returnExpr.asVar.definedBy.contains(definedAtIndex)) { + immutable.Set(VariableFact(assignment.targetVar.name, returnSite.index)) + } else { + immutable.Set.empty + } + + case _ => immutable.Set.empty + } + } + } + } + } + + override def getCallToReturnFlowFunction( + callSite: JavaStatement, + returnSite: JavaStatement + ): FlowFunction[LinearConstantPropagationFact] = { + (sourceFact: LinearConstantPropagationFact) => + { + immutable.Set(sourceFact) + } + } + + override def getNormalEdgeFunction( + source: JavaStatement, + sourceFact: LinearConstantPropagationFact, + target: JavaStatement, + targetFact: LinearConstantPropagationFact + ): EdgeFunction[LinearConstantPropagationValue] = { + if (sourceFact == targetFact) { + /* Simply propagates a fact through the method */ + return identityEdgeFunction + } + + source.stmt.astID match { + case Assignment.ASTID => + val assignment = source.stmt.asAssignment + getEdgeFunctionForExpression(assignment.expr) + case _ => identityEdgeFunction + } + } + + private def getEdgeFunctionForExpression( + expr: Expr[JavaIFDSProblem.V] + ): EdgeFunction[LinearConstantPropagationValue] = { + expr.astID match { + case IntConst.ASTID => + LinearCombinationEdgeFunction(0, expr.asIntConst.value) + + case BinaryExpr.ASTID => + val binaryExpr = expr.asBinaryExpr + val leftExpr = binaryExpr.left + val rightExpr = binaryExpr.right + + if (leftExpr.astID != Var.ASTID && leftExpr.astID != IntConst.ASTID + || rightExpr.astID != Var.ASTID && rightExpr.astID != IntConst.ASTID + ) { + throw new IllegalArgumentException(s"Combination ($leftExpr, $rightExpr) should not occur here!") + } + + /* Try to resolve an constant or variable expression to a constant value */ + val getValueForExpr: Expr[JavaIFDSProblem.V] => Option[Int] = expr => { + expr.astID match { + case Var.ASTID => + val var0 = expr.asVar + var0.value match { + case intRange: DefaultIntegerRangeValues#IntegerRange => + if (intRange.lowerBound == intRange.upperBound) { + /* If boundaries are equal, the value is constant */ + Some(intRange.lowerBound) + } else if (var0.definedBy.size > 1) { + // TODO (IDE) ADD TESTS FOR THIS (CAN WE REFACTOR THIS/DOES THIS REDUCE THE + // ACCURACY) + return VariableValueEdgeFunction + } else { + None + } + case _ => + None + } + + case IntConst.ASTID => Some(expr.asIntConst.value) + } + } + + val leftValue = getValueForExpr(leftExpr) + val rightValue = getValueForExpr(rightExpr) + + (leftValue, rightValue, binaryExpr.op) match { + case (Some(l), Some(r), BinaryArithmeticOperators.Add) => + LinearCombinationEdgeFunction(0, l + r) + case (Some(l), None, BinaryArithmeticOperators.Add) => + LinearCombinationEdgeFunction(1, l) + case (None, Some(r), BinaryArithmeticOperators.Add) => + LinearCombinationEdgeFunction(1, r) + + case (Some(l), Some(r), BinaryArithmeticOperators.Subtract) => + LinearCombinationEdgeFunction(0, l - r) + case (Some(l), None, BinaryArithmeticOperators.Subtract) => + LinearCombinationEdgeFunction(-1, l) + case (None, Some(r), BinaryArithmeticOperators.Subtract) => + LinearCombinationEdgeFunction(1, -r) + + case (Some(l), Some(r), BinaryArithmeticOperators.Multiply) => + LinearCombinationEdgeFunction(0, l * r) + case (Some(l), None, BinaryArithmeticOperators.Multiply) => + LinearCombinationEdgeFunction(l, 0) + case (None, Some(r), BinaryArithmeticOperators.Multiply) => + LinearCombinationEdgeFunction(r, 0) + + case (None, None, _) => + VariableValueEdgeFunction + + case (_, _, op) => + throw new UnsupportedOperationException(s"Operator $op is not implemented!") + } + + case ArrayLength.ASTID => + VariableValueEdgeFunction + + case GetField.ASTID => + VariableValueEdgeFunction + + case GetStatic.ASTID => + VariableValueEdgeFunction + + case _ => + throw new IllegalArgumentException(s"Expression $expr should not occur here!") + } + } + + override def getCallEdgeFunction( + callSite: JavaStatement, + callSiteFact: LinearConstantPropagationFact, + calleeEntry: JavaStatement, + calleeEntryFact: LinearConstantPropagationFact, + callee: Method + ): EdgeFunction[LinearConstantPropagationValue] = identityEdgeFunction + + override def getReturnEdgeFunction( + calleeExit: JavaStatement, + calleeExitFact: LinearConstantPropagationFact, + callee: Method, + returnSite: JavaStatement, + returnSiteFact: LinearConstantPropagationFact + ): EdgeFunction[LinearConstantPropagationValue] = identityEdgeFunction + + override def getCallToReturnEdgeFunction( + callSite: JavaStatement, + callSiteFact: LinearConstantPropagationFact, + returnSite: JavaStatement, + returnSiteFact: LinearConstantPropagationFact + ): EdgeFunction[LinearConstantPropagationValue] = identityEdgeFunction +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationValue.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationValue.scala new file mode 100644 index 0000000000..3c743c4537 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationValue.scala @@ -0,0 +1,24 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem + +import org.opalj.ide.problem.IDEValue + +/** + * Type for modeling values for linear constant propagation + */ +trait LinearConstantPropagationValue extends IDEValue + +/** + * Value not known (yet) + */ +case object UnknownValue extends LinearConstantPropagationValue + +/** + * A constant value + */ +case class ConstantValue(c: Int) extends LinearConstantPropagationValue + +/** + * Value is variable + */ +case object VariableValue extends LinearConstantPropagationValue From 6c35b5e92e64f4c8cd63ce3be6bc16ab8ed667c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 14 Jun 2024 17:01:38 +0200 Subject: [PATCH 010/167] Prepare test environment for linear constant propagation --- .../ConstantValue.java | 26 ++++++ .../ConstantValues.java | 18 ++++ .../LinearConstantPropagationProperty.java | 6 ++ .../VariableValue.java | 21 +++++ .../VariableValues.java | 18 ++++ .../AbstractRepeatablePropertyMatcher.scala | 67 ++++++++++++++ .../LinearConstantPropagationMatcher.scala | 92 +++++++++++++++++++ 7 files changed, 248 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValue.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValues.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationProperty.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValue.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValues.java create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/AbstractRepeatablePropertyMatcher.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValue.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValue.java new file mode 100644 index 0000000000..c6891eebbe --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValue.java @@ -0,0 +1,26 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.linear_constant_propagation; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.*; + +/** + * Annotation to state that a variable has a constant value + */ +@PropertyValidator(key = LinearConstantPropagationProperty.KEY, validator = ConstantValueMatcher.class) +@Repeatable(ConstantValues.class) +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface ConstantValue { + /** + * The name of the variable + */ + String variable(); + + /** + * The constant value of the variable + */ + int value(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValues.java new file mode 100644 index 0000000000..15b14d12aa --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValues.java @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.linear_constant_propagation; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.*; + +/** + * Container annotation for {@link ConstantValue} annotations + */ +@PropertyValidator(key = LinearConstantPropagationProperty.KEY, validator = ConstantValueMatcher.class) +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface ConstantValues { + ConstantValue[] value(); +} + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationProperty.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationProperty.java new file mode 100644 index 0000000000..a952bdab5f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationProperty.java @@ -0,0 +1,6 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.linear_constant_propagation; + +public class LinearConstantPropagationProperty { + public static final String KEY = "LinearConstantPropagation"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValue.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValue.java new file mode 100644 index 0000000000..59764549b6 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValue.java @@ -0,0 +1,21 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.linear_constant_propagation; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.*; + +/** + * Annotation to state that a variable has a non-constant value + */ +@PropertyValidator(key = LinearConstantPropagationProperty.KEY, validator = VariableValueMatcher.class) +@Repeatable(VariableValues.class) +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface VariableValue { + /** + * The name of the variable + */ + String variable() default ""; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValues.java new file mode 100644 index 0000000000..1d13b978cd --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValues.java @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.linear_constant_propagation; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.*; + +/** + * Container annotation for {@link VariableValue} annotations + */ +@PropertyValidator(key = LinearConstantPropagationProperty.KEY, validator = VariableValueMatcher.class) +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface VariableValues { + VariableValue[] value(); +} + diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/AbstractRepeatablePropertyMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/AbstractRepeatablePropertyMatcher.scala new file mode 100644 index 0000000000..c7d5bdf4b2 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/AbstractRepeatablePropertyMatcher.scala @@ -0,0 +1,67 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties + +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.Project +import org.opalj.fpcf.Property + +/** + * Property matcher for repeatable annotations + */ +abstract class AbstractRepeatablePropertyMatcher extends AbstractPropertyMatcher { + /** + * Type for identifying the single annotation + */ + val singleAnnotationType: ObjectType + /** + * Type for identifying the container annotation + */ + val containerAnnotationType: ObjectType + + /** + * The name of the field of the container annotation that holds the single annotations + */ + val containerAnnotationFieldName: String = "value" + + override def validateProperty( + p: Project[?], + as: Set[ObjectType], + entity: Any, + a: AnnotationLike, + properties: Iterable[Property] + ): Option[String] = { + if (a.annotationType == singleAnnotationType) { + validateSingleProperty(p, as, entity, a, properties) + } else if (a.annotationType == containerAnnotationType) { + val subAnnotations = + getValue(p, containerAnnotationType, a.elementValuePairs, containerAnnotationFieldName) + .asArrayValue.values.map { a => a.asAnnotationValue.annotation } + + val errors = subAnnotations + .map { a => validateSingleProperty(p, as, entity, a, properties) } + .filter { result => result.isDefined } + .map { result => result.get } + + if (errors.nonEmpty) { + Some(errors.mkString(" ")) + } else { + None + } + } else { + Some(s"Invalid annotation '${a.annotationType}' for ${this.getClass.getName}!") + } + } + + /** + * Test if the computed properties are matched by this matcher. Called for each single annotation of a container + * annotation once. + */ + def validateSingleProperty( + p: Project[?], + as: Set[ObjectType], + entity: Any, + a: AnnotationLike, + properties: Iterable[Property] + ): Option[String] +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala new file mode 100644 index 0000000000..e43a08511c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala @@ -0,0 +1,92 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.linear_constant_propagation + +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.Project +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractRepeatablePropertyMatcher +import org.opalj.ide.integration.BasicIDEProperty +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.{problem => LCPProblem} + +/** + * Matcher for [[ConstantValue]] and [[ConstantValues]] annotations + */ +class ConstantValueMatcher extends AbstractRepeatablePropertyMatcher { + override val singleAnnotationType: ObjectType = + ObjectType("org/opalj/fpcf/properties/linear_constant_propagation/ConstantValue") + override val containerAnnotationType: ObjectType = + ObjectType("org/opalj/fpcf/properties/linear_constant_propagation/ConstantValues") + + override def validateSingleProperty( + p: Project[?], + as: Set[ObjectType], + entity: Any, + a: AnnotationLike, + properties: Iterable[Property] + ): Option[String] = { + val expectedVariableName = + getValue(p, singleAnnotationType, a.elementValuePairs, "variable").asStringValue.value + val expectedVariableValue = + getValue(p, singleAnnotationType, a.elementValuePairs, "value").asIntValue.value + + if (properties.exists { + case property: BasicIDEProperty[?, ?, ?] => + property.results.exists { + case (_, results) => + results.exists { + case (LCPProblem.VariableFact(name, _), LCPProblem.ConstantValue(value)) => + expectedVariableName == name && expectedVariableValue == value + + case _ => false + } + } + + case _ => false + } + ) { + None + } else { + Some(s"Result should contain (VariableFact(${expectedVariableName}),ConstantValue(${expectedVariableValue}))!") + } + } +} + +/** + * Matcher for [[VariableValue]] and [[VariableValues]] annotations + */ +class VariableValueMatcher extends AbstractRepeatablePropertyMatcher { + override val singleAnnotationType: ObjectType = + ObjectType("org/opalj/fpcf/properties/linear_constant_propagation/VariableValue") + override val containerAnnotationType: ObjectType = + ObjectType("org/opalj/fpcf/properties/linear_constant_propagation/VariableValues") + + override def validateSingleProperty( + p: Project[?], + as: Set[ObjectType], + entity: Any, + a: AnnotationLike, + properties: Iterable[Property] + ): Option[String] = { + val expectedVariableName = + getValue(p, singleAnnotationType, a.elementValuePairs, "variable").asStringValue.value + + if (properties.exists { + case property: BasicIDEProperty[?, ?, ?] => + property.results.exists { + case (_, results) => + results.exists { + case (LCPProblem.VariableFact(name, _), LCPProblem.VariableValue) => + expectedVariableName == name + + case _ => false + } + } + } + ) { + None + } else { + Some(s"Result should contain (VariableFact(${expectedVariableName}),VariableValue)!") + } + } +} From 146b123197e14b3bcdee635637583107aad4f2b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 19 Jun 2024 15:43:07 +0200 Subject: [PATCH 011/167] Add tests for linear constant propagation --- .../BranchingExample.java | 29 ++++++++++ .../ConstantsWithinMethodExample.java | 22 +++++++ .../FieldAccessExample.java | 34 +++++++++++ .../LoopExample.java | 39 +++++++++++++ .../PropagationAcrossMethodsExample.java | 42 ++++++++++++++ .../RecursionExample.java | 22 +++++++ .../VariablesWithinMethodExample.java | 20 +++++++ ...ConstantPropagationAnalysisScheduler.scala | 41 +++++++++++++ .../LinearConstantPropagationTests.scala | 57 +++++++++++++++++++ 9 files changed, 306 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/ConstantsWithinMethodExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/LoopExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/RecursionExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/VariablesWithinMethodExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingExample.java new file mode 100644 index 0000000000..c6ad122118 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingExample.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.linear_constant_propagation; + +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; + +public class BranchingExample { + @ConstantValue(variable = "lva", value = 8) + @VariableValues({ + @VariableValue(variable = "lv2"), + @VariableValue(variable = "lv8") + }) + public static void main(String[] args) { + int a = 23; + int b = 7; + + if (args.length == 0) { + a = 42; + b = 6; + b++; + } + + int c = 1 + a; + int d = b + 1; + + System.out.println("a: " + a + ", b: " + b + ", c: " + c + ", d: " + d); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/ConstantsWithinMethodExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/ConstantsWithinMethodExample.java new file mode 100644 index 0000000000..05e0cadcbe --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/ConstantsWithinMethodExample.java @@ -0,0 +1,22 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.linear_constant_propagation; + +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValues; + +public class ConstantsWithinMethodExample { + @ConstantValues({ + @ConstantValue(variable = "lv0", value = 4), + @ConstantValue(variable = "lv1", value = 3), + @ConstantValue(variable = "lv2", value = 12), + @ConstantValue(variable = "lv3", value = 4), + @ConstantValue(variable = "lv4", value = 16) + }) + public static void main(String[] args) { + int a = 4; + int b = a; + int c = 3 * b + 4; + + System.out.println("a: " + a + ", b: " + b + ", c: " + c); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java new file mode 100644 index 0000000000..e20bba027b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java @@ -0,0 +1,34 @@ +package org.opalj.fpcf.fixtures.linear_constant_propagation; + +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; + +public class FieldAccessExample { + private final int a; + int b; + static int c = 42; + + public FieldAccessExample(int a, int b) { + this.a = a; + this.b = b; + } + + public int getA() { + return a; + } + + @VariableValues({ + @VariableValue(variable = "lv4"), + @VariableValue(variable = "lv5"), + @VariableValue(variable = "lv6") + }) + public static void main(String[] args) { + FieldAccessExample example = new FieldAccessExample(11, 22); + + int i = example.getA(); + int j = example.b; + int k = c; + + System.out.println("i: " + i + ", j: " + j + ", k: " + k); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/LoopExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/LoopExample.java new file mode 100644 index 0000000000..2ab470e946 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/LoopExample.java @@ -0,0 +1,39 @@ +package org.opalj.fpcf.fixtures.linear_constant_propagation; + +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; + +public class LoopExample { + public static int loop1(int a) { + int res = 0; + + while (a > 0) { + a--; + res++; + } + + return res; + } + + public static int loop2(int a) { + int res = a - 1; + + while (a > 0) { + a--; + res += 2; + System.out.println(res); + res -= 2; + } + + return res; + } + + @ConstantValue(variable = "lv3", value = 22) + @VariableValue(variable = "lv1") + public static void main(String[] args) { + int i = loop1(42); + int j = loop2(23); + + System.out.println("i: " + i + ", j: " + j); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java new file mode 100644 index 0000000000..e8ef1d907b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java @@ -0,0 +1,42 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.linear_constant_propagation; + +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValues; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; + +public class PropagationAcrossMethodsExample { + public int linearCalculation1(String msg, int a, int b) { + System.out.println(msg + ": " + a); + return 42 - 5 * b; + } + + public static int linearCalculation2(String msg, int a) { + System.out.println(msg); + return 12 - 4 * 4 + a; + } + + @ConstantValues({ + @ConstantValue(variable = "lv5", value = -18), + @ConstantValue(variable = "lv8", value = 132), + @ConstantValue(variable = "lva", value = 128) + }) + @VariableValues({ + @VariableValue(variable = "lvd"), + @VariableValue(variable = "lv10") + }) + public static void main(String[] args) { + PropagationAcrossMethodsExample example = new PropagationAcrossMethodsExample(); + + int i = example.linearCalculation1("First call", 23, 12); + int j = example.linearCalculation1("Second call", 2, i); + + int k = linearCalculation2("Third call", j); + int l = linearCalculation2("Fourth call", args.length); + + int m = example.linearCalculation1("Fifth call", 12, l); + + System.out.println("i: " + i + ", j: " + j + ", k:" + k + ", l: " + l + ", m: " + m); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/RecursionExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/RecursionExample.java new file mode 100644 index 0000000000..25b279a798 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/RecursionExample.java @@ -0,0 +1,22 @@ +package org.opalj.fpcf.fixtures.linear_constant_propagation; + +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; + +public class RecursionExample { + public static int recursive1(int a) { + if (a > 0) { + a -= 2; + System.out.println(recursive1(a)); + a += 2; + } + + return a + 3; + } + + @ConstantValue(variable = "lv1", value = 14) + public static void main(String[] args) { + int i = recursive1(11); + + System.out.println("i: " + i); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/VariablesWithinMethodExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/VariablesWithinMethodExample.java new file mode 100644 index 0000000000..980de0dddb --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/VariablesWithinMethodExample.java @@ -0,0 +1,20 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.linear_constant_propagation; + +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; + +public class VariablesWithinMethodExample { + @VariableValues({ + @VariableValue(variable = "lv0"), + @VariableValue(variable = "lv3"), + @VariableValue(variable = "lv6") + }) + public static void main(String[] args) { + int a = args.length; + int b = args[0].length(); + int c = Integer.valueOf(42).hashCode(); + + System.out.println("a: " + a + ", b: " + b + ", c: " + c); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala new file mode 100644 index 0000000000..90b400b739 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala @@ -0,0 +1,41 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ide.linear_constant_propagation + +import scala.collection.immutable + +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.cg.TypeIteratorKey +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationAnalysis +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ide.solver.JavaIDEAnalysis +import org.opalj.tac.fpcf.properties.TACAI + +object LinearConstantPropagationAnalysisScheduler + extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] { + override def property: JavaIDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] = + LinearConstantPropagationPropertyMetaInformation + + override def requiredProjectInformation: ProjectInformationKeys = + Seq(DeclaredMethodsKey, TypeIteratorKey, PropertyStoreKey, RTACallGraphKey) + + override def init( + project: SomeProject, + ps: PropertyStore + ): JavaIDEAnalysis[LinearConstantPropagationFact, LinearConstantPropagationValue] = { + new LinearConstantPropagationAnalysis(project) + } + + override def uses: immutable.Set[PropertyBounds] = + immutable.Set(PropertyBounds.finalP(TACAI), PropertyBounds.finalP(Callers)) +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala new file mode 100644 index 0000000000..803d3d87c3 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala @@ -0,0 +1,57 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ide.linear_constant_propagation + +import com.typesafe.config.Config +import com.typesafe.config.ConfigValueFactory +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.analyses.Project +import org.opalj.fpcf.PropertiesTest +import org.opalj.fpcf.properties.linear_constant_propagation.LinearConstantPropagationProperty +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationAnalysis + +import java.net.URL + +class LinearConstantPropagationTests extends PropertiesTest { + override def withRT: Boolean = true + + override def createConfig(): Config = { + super.createConfig() + .withValue("org.opalj.ide.debug", ConfigValueFactory.fromAnyRef(true)) + .withValue("org.opalj.ide.trace", ConfigValueFactory.fromAnyRef(false)) + } + + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey)(_ => + Set[Class[? <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + ) + p.get(RTACallGraphKey) + } + + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures/linear_constant_propagation") + } + + describe("Execute the o.o.t.f.a.i.l.LinearConstantPropagationAnalysis") { + val testContext = executeAnalyses(Set( + LinearConstantPropagationAnalysisScheduler + )) + + testContext.analyses.foreach { + case analysis: LinearConstantPropagationAnalysis => + analysis.lcpProblem.getEntryPoints + .foreach { method => + testContext.propertyStore.force(method, analysis.propertyMetaInformation.key) + } + } + + testContext.propertyStore.shutdown() + + validateProperties( + testContext, + methodsWithAnnotations(testContext.project), + Set(LinearConstantPropagationProperty.KEY) + ) + } +} From f0580ab1be201835d9dab9210427b3ef0d916d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 19 Jun 2024 15:44:59 +0200 Subject: [PATCH 012/167] Fix IDE solver/linear constant propagation (temporary) --- .../LinearConstantPropagationProblem.scala | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 32363788eb..a7bdc26408 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -154,7 +154,7 @@ class LinearConstantPropagationProblem(project: SomeProject) /* Always propagate null facts */ immutable.Set(sourceFact) - case VariableFact(_, definedAtIndex) => + case VariableFact(name, definedAtIndex) => val callStmt = JavaIFDSProblem.asCall(callSite.stmt) /* Parameters and their types (excluding the implicit 'this' reference) */ @@ -169,7 +169,25 @@ class LinearConstantPropagationProblem(project: SomeProject) paramTypes(index).isIntegerType && param.asVar.definedBy.contains(definedAtIndex) } .map { case (_, index) => - VariableFact(s"param${index + 1}", -(index + 2)) + // TODO (IDE) CREATING A FACT WITH A NAME MADE UP OF THE ORIGINAL NAME AND THE + // PARAMETER NAME IS REQUIRED AS ANALYZING A CALL TO A CALLEE THAT ALREADY HAS BEEN + // ANALYZED WOULD STOP IMMEDIATELY OTHERWISE + // REASON: + // - THE PATH THAT IS PROPAGATED IS MADE UP OF THE START STATEMENT AND THE + // PARAMETER AS THE FACT AND THUS ONLY DEPENDS ON THE CALLEE AND IS ALREADY KNOWN + // - CALLING 'propagate' RETURNS WITHOUT ENQUEUING BECAUSE IT FINDS THE IDENTITY + // FUNCTION FOR EXACTLY THIS PATH + // - WE NEVER REACH THE RETURN STATEMENT OF THE CALLEE AGAIN + // - THE CALL STATEMENT IS ADDED AS POSSIBLE CALL SOURCE BUT IT IS NEVER PROCESSED + // IN THE RETURN FLOW + // - THERE WILL NEVER BE A COMPLETE SUMMARY FUNCTION FOR THE CALL + // - IF THE CALL HAPPENED IN AN ASSIGNMENT: THE ASSIGNED VARIABLE WILL NEVER BE + // GENERATED A FACT FOR + // SOLUTION: + // - EITHER KEEP FACTS UNIQUE (THE CURRENT SOLUTION) -> ALWAYS REANALYZES THE + // CALLEE COMPLETELY + // - ADJUST SOLVER TO USE A CONCEPT LIKE ENDSUMMARIES (SEE naeem2010practical) + VariableFact(s"$name-param${index + 1}", -(index + 2)) } .toSet } From 1c4062bf6efc2c458d7bdeee6fb9153384a80eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 24 Jun 2024 08:29:36 +0200 Subject: [PATCH 013/167] Fix linear constant propagation tests (some calls are identified as such but no callees are found) --- .../LinearConstantPropagationTests.scala | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala index 803d3d87c3..0642efa234 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala @@ -1,25 +1,31 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.ide.linear_constant_propagation +import java.net.URL import com.typesafe.config.Config import com.typesafe.config.ConfigValueFactory import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.br.analyses.Project +import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey import org.opalj.fpcf.PropertiesTest import org.opalj.fpcf.properties.linear_constant_propagation.LinearConstantPropagationProperty +import org.opalj.ide.ConfigKeyDebugLog +import org.opalj.ide.ConfigKeyTraceLog import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationAnalysis -import java.net.URL - class LinearConstantPropagationTests extends PropertiesTest { override def withRT: Boolean = true override def createConfig(): Config = { super.createConfig() - .withValue("org.opalj.ide.debug", ConfigValueFactory.fromAnyRef(true)) - .withValue("org.opalj.ide.trace", ConfigValueFactory.fromAnyRef(false)) + .withValue( + InitialInstantiatedTypesKey.ConfigKeyPrefix + "AllInstantiatedTypesFinder.projectClassesOnly", + ConfigValueFactory.fromAnyRef(false) + ) + .withValue(ConfigKeyDebugLog, ConfigValueFactory.fromAnyRef(true)) + .withValue(ConfigKeyTraceLog, ConfigValueFactory.fromAnyRef(false)) } override def init(p: Project[URL]): Unit = { @@ -41,9 +47,7 @@ class LinearConstantPropagationTests extends PropertiesTest { testContext.analyses.foreach { case analysis: LinearConstantPropagationAnalysis => analysis.lcpProblem.getEntryPoints - .foreach { method => - testContext.propertyStore.force(method, analysis.propertyMetaInformation.key) - } + .foreach { method => testContext.propertyStore.force(method, analysis.propertyMetaInformation.key) } } testContext.propertyStore.shutdown() From 95b3225d4376f0352f575f41e1ad5b4a93cdbf14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 24 Jun 2024 12:27:29 +0200 Subject: [PATCH 014/167] Add abstraction for IDE tests --- .../opalj/fpcf/ide/IDEPropertiesTest.scala | 47 +++++++++++++++++++ .../LinearConstantPropagationTests.scala | 37 ++------------- .../LinearConstantPropagationProblem.scala | 13 ----- 3 files changed, 51 insertions(+), 46 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala new file mode 100644 index 0000000000..ea96dcc5e0 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala @@ -0,0 +1,47 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ide + +import java.net.URL +import com.typesafe.config.Config +import com.typesafe.config.ConfigValueFactory +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.Method +import org.opalj.br.analyses.Project +import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey +import org.opalj.fpcf.PropertiesTest +import org.opalj.ide.ConfigKeyDebugLog +import org.opalj.ide.ConfigKeyTraceLog +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG + +class IDEPropertiesTest extends PropertiesTest { + override def withRT: Boolean = true + + override def createConfig(): Config = { + super.createConfig() + .withValue( + InitialInstantiatedTypesKey.ConfigKeyPrefix + "AllInstantiatedTypesFinder.projectClassesOnly", + ConfigValueFactory.fromAnyRef(false) + ) + .withValue(ConfigKeyDebugLog, ConfigValueFactory.fromAnyRef(true)) + .withValue(ConfigKeyTraceLog, ConfigValueFactory.fromAnyRef(false)) + } + + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey)(_ => + Set[Class[? <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + ) + p.get(RTACallGraphKey) + } + + def getEntryPointsByICFG(icfg: JavaICFG, project: Project[URL]): Set[Method] = { + icfg.getCallablesCallableFromOutside + .filter { method => + val packageOfEntryPoint = method.classFile.thisType.packageName + project.allProjectClassFiles.exists { classFile => + packageOfEntryPoint.startsWith(classFile.thisType.packageName) + } + } + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala index 0642efa234..a911a4025d 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala @@ -1,52 +1,23 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.ide.linear_constant_propagation -import java.net.URL -import com.typesafe.config.Config -import com.typesafe.config.ConfigValueFactory -import org.opalj.ai.domain.l2 -import org.opalj.ai.fpcf.properties.AIDomainFactoryKey -import org.opalj.br.analyses.Project -import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey -import org.opalj.fpcf.PropertiesTest +import org.opalj.fpcf.ide.IDEPropertiesTest import org.opalj.fpcf.properties.linear_constant_propagation.LinearConstantPropagationProperty -import org.opalj.ide.ConfigKeyDebugLog -import org.opalj.ide.ConfigKeyTraceLog -import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationAnalysis -class LinearConstantPropagationTests extends PropertiesTest { - override def withRT: Boolean = true - - override def createConfig(): Config = { - super.createConfig() - .withValue( - InitialInstantiatedTypesKey.ConfigKeyPrefix + "AllInstantiatedTypesFinder.projectClassesOnly", - ConfigValueFactory.fromAnyRef(false) - ) - .withValue(ConfigKeyDebugLog, ConfigValueFactory.fromAnyRef(true)) - .withValue(ConfigKeyTraceLog, ConfigValueFactory.fromAnyRef(false)) - } - - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey)(_ => - Set[Class[? <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) - ) - p.get(RTACallGraphKey) - } - +class LinearConstantPropagationTests extends IDEPropertiesTest { override def fixtureProjectPackage: List[String] = { List("org/opalj/fpcf/fixtures/linear_constant_propagation") } - describe("Execute the o.o.t.f.a.i.l.LinearConstantPropagationAnalysis") { + describe("Execute the o.o.t.f.a.i.i.l.LinearConstantPropagationAnalysis") { val testContext = executeAnalyses(Set( LinearConstantPropagationAnalysisScheduler )) testContext.analyses.foreach { case analysis: LinearConstantPropagationAnalysis => - analysis.lcpProblem.getEntryPoints + getEntryPointsByICFG(analysis.lcpProblem.icfg, testContext.project) .foreach { method => testContext.propertyStore.force(method, analysis.propertyMetaInformation.key) } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index a7bdc26408..9527b796c4 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -33,19 +33,6 @@ class LinearConstantPropagationProblem(project: SomeProject) ) { private val identityEdgeFunction = new IdentityEdgeFunction[LinearConstantPropagationValue] - /** - * @return all methods from the project class files that can be called from outside - */ - def getEntryPoints: Set[Method] = { - icfg.getCallablesCallableFromOutside - .filter { method => - val packageOfEntryPoint = method.classFile.thisType.packageName - project.allProjectClassFiles.exists { classFile => - packageOfEntryPoint.startsWith(classFile.thisType.packageName) - } - } - } - override val nullFact: LinearConstantPropagationFact = NullFact From 2d80f58d4a66bfefbd70eae0f00f3e560e099af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 26 Jun 2024 15:55:31 +0200 Subject: [PATCH 015/167] Extend IDE problem function signatures to allow passing/using more information --- .../ide/problem/FlowRecordingIDEProblem.scala | 26 ++++++++++++------- .../org/opalj/ide/problem/IDEProblem.scala | 26 +++++++++++++------ .../org/opalj/ide/solver/IDEAnalysis.scala | 2 +- .../LinearConstantPropagationProblem.scala | 18 +++++++------ 4 files changed, 45 insertions(+), 27 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index ac4acdc52b..7c0b594390 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -3,7 +3,9 @@ package org.opalj.ide.problem import java.io.Writer import scala.collection.mutable + import org.opalj.fpcf.Entity +import org.opalj.fpcf.PropertyStore object FlowRecorderModes extends Enumeration { type FlowRecorderMode = Value @@ -29,7 +31,7 @@ object FlowRecorderModes extends Enumeration { */ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( val baseProblem: IDEProblem[Fact, Value, Statement, Callable], - val recorderMode: FlowRecorderModes.FlowRecorderMode = FlowRecorderModes.NODE_AS_STMT, + val recorderMode: FlowRecorderModes.FlowRecorderMode = FlowRecorderModes.NODE_AS_STMT, val uniqueFlowsOnly: Boolean = true, val recordEdgeFunctions: Boolean = false ) extends IDEProblem[Fact, Value, Statement, Callable](baseProblem.icfg) { @@ -61,7 +63,9 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal override val lattice: MeetLattice[Value] = baseProblem.lattice - override def getNormalFlowFunction(source: Statement, target: Statement): FlowFunction[Fact] = { + override def getNormalFlowFunction(source: Statement, target: Statement)( + implicit propertyStore: PropertyStore + ): FlowFunction[Fact] = { new RecodingFlowFunction(baseProblem.getNormalFlowFunction(source, target), source, target, "normal flow") } @@ -69,7 +73,7 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal callSite: Statement, calleeEntry: Statement, callee: Callable - ): FlowFunction[Fact] = { + )(implicit propertyStore: PropertyStore): FlowFunction[Fact] = { new RecodingFlowFunction( baseProblem.getCallFlowFunction(callSite, calleeEntry, callee), callSite, @@ -82,7 +86,7 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal calleeExit: Statement, callee: Callable, returnSite: Statement - ): FlowFunction[Fact] = { + )(implicit propertyStore: PropertyStore): FlowFunction[Fact] = { new RecodingFlowFunction( baseProblem.getReturnFlowFunction(calleeExit, callee, returnSite), calleeExit, @@ -91,9 +95,11 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal ) } - override def getCallToReturnFlowFunction(callSite: Statement, returnSite: Statement): FlowFunction[Fact] = { + override def getCallToReturnFlowFunction(callSite: Statement, callee: Callable, returnSite: Statement)( + implicit propertyStore: PropertyStore + ): FlowFunction[Fact] = { new RecodingFlowFunction( - baseProblem.getCallToReturnFlowFunction(callSite, returnSite), + baseProblem.getCallToReturnFlowFunction(callSite, callee, returnSite), callSite, returnSite, "call-to-return flow" @@ -105,7 +111,7 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal sourceFact: Fact, target: Statement, targetFact: Fact - ): EdgeFunction[Value] = { + )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { val edgeFunction = baseProblem.getNormalEdgeFunction(source, sourceFact, target, targetFact) collectedEdgeFunctions.put(createDotEdge(source, sourceFact, target, targetFact, "normal flow"), edgeFunction) edgeFunction @@ -117,7 +123,7 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal calleeEntry: Statement, calleeEntryFact: Fact, callee: Callable - ): EdgeFunction[Value] = { + )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { val edgeFunction = baseProblem.getCallEdgeFunction(callSite, callSiteFact, calleeEntry, calleeEntryFact, callee) collectedEdgeFunctions.put( createDotEdge(callSite, callSiteFact, calleeEntry, calleeEntryFact, "call flow"), @@ -132,7 +138,7 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal callee: Callable, returnSite: Statement, returnSiteFact: Fact - ): EdgeFunction[Value] = { + )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { val edgeFunction = baseProblem.getReturnEdgeFunction(calleeExit, calleeExitFact, callee, returnSite, returnSiteFact) collectedEdgeFunctions.put( @@ -147,7 +153,7 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal callSiteFact: Fact, returnSite: Statement, returnSiteFact: Fact - ): EdgeFunction[Value] = { + )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { val edgeFunction = baseProblem.getCallToReturnEdgeFunction(callSite, callSiteFact, returnSite, returnSiteFact) collectedEdgeFunctions.put( createDotEdge(callSite, callSiteFact, returnSite, returnSiteFact, "call-to-return flow"), diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index 92f29dbb97..908d66ddbf 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -2,6 +2,7 @@ package org.opalj.ide.problem import org.opalj.fpcf.Entity +import org.opalj.fpcf.PropertyStore import org.opalj.ide.solver.ICFG /** @@ -25,7 +26,9 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl * @param source where the normal flow starts * @param target where the normal flow ends */ - def getNormalFlowFunction(source: Statement, target: Statement): FlowFunction[Fact] + def getNormalFlowFunction(source: Statement, target: Statement)( + implicit propertyStore: PropertyStore + ): FlowFunction[Fact] /** * Generate a flow function for a call flow @@ -33,7 +36,9 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl * @param calleeEntry where the callable starts (the statement which the callable is started with) * @param callee the callable that is called */ - def getCallFlowFunction(callSite: Statement, calleeEntry: Statement, callee: Callable): FlowFunction[Fact] + def getCallFlowFunction(callSite: Statement, calleeEntry: Statement, callee: Callable)( + implicit propertyStore: PropertyStore + ): FlowFunction[Fact] /** * Generate a flow function for a return flow @@ -41,14 +46,19 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl * @param callee the callable that is returned from * @param returnSite where the return flow ends (e.g. the next statement after the call in the callers code) */ - def getReturnFlowFunction(calleeExit: Statement, callee: Callable, returnSite: Statement): FlowFunction[Fact] + def getReturnFlowFunction(calleeExit: Statement, callee: Callable, returnSite: Statement)( + implicit propertyStore: PropertyStore + ): FlowFunction[Fact] /** * Generate a flow function for a call-to-return flow * @param callSite where the call-to-return flow starts (always a call statement) + * @param callee the callable this flow is about * @param returnSite where the call-to-return flow ends (e.g. the next statement after the call) */ - def getCallToReturnFlowFunction(callSite: Statement, returnSite: Statement): FlowFunction[Fact] + def getCallToReturnFlowFunction(callSite: Statement, callee: Callable, returnSite: Statement)( + implicit propertyStore: PropertyStore + ): FlowFunction[Fact] /** * Generate an edge function for a normal flow @@ -62,7 +72,7 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl sourceFact: Fact, target: Statement, targetFact: Fact - ): EdgeFunction[Value] + )(implicit propertyStore: PropertyStore): EdgeFunction[Value] /** * Generate an edge function for a call flow @@ -78,7 +88,7 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl calleeEntry: Statement, calleeEntryFact: Fact, callee: Callable - ): EdgeFunction[Value] + )(implicit propertyStore: PropertyStore): EdgeFunction[Value] /** * Generate an edge function for a return flow @@ -94,7 +104,7 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl callee: Callable, returnSite: Statement, returnSiteFact: Fact - ): EdgeFunction[Value] + )(implicit propertyStore: PropertyStore): EdgeFunction[Value] /** * Generate an edge function for a call-to-return flow @@ -108,5 +118,5 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl callSiteFact: Fact, returnSite: Statement, returnSiteFact: Fact - ): EdgeFunction[Value] + )(implicit propertyStore: PropertyStore): EdgeFunction[Value] } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 16ea6c3dc9..ee0c3d4f34 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -324,7 +324,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val rs = icfg.getNextStatements(n) // IDE P1 line 14 rs.foreach { r => - val d3s = problem.getCallToReturnFlowFunction(n, r).compute(d2) + val d3s = problem.getCallToReturnFlowFunction(n, q, r).compute(d2) logTrace(s"generated the following d3s=$d3s for return-site statement r=${icfg.stringifyStatement(r)}") diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 9527b796c4..01443fede2 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -7,6 +7,7 @@ import org.opalj.BinaryArithmeticOperators import org.opalj.ai.domain.l1.DefaultIntegerRangeValues import org.opalj.br.Method import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.PropertyStore import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.FlowFunction import org.opalj.ide.problem.IdentityEdgeFunction @@ -42,7 +43,7 @@ class LinearConstantPropagationProblem(project: SomeProject) override def getNormalFlowFunction( source: JavaStatement, target: JavaStatement - ): FlowFunction[LinearConstantPropagationFact] = { + )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { (sourceFact: LinearConstantPropagationFact) => { source.stmt.astID match { @@ -129,7 +130,7 @@ class LinearConstantPropagationProblem(project: SomeProject) callSite: JavaStatement, calleeEntry: JavaStatement, callee: Method - ): FlowFunction[LinearConstantPropagationFact] = { + )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { (sourceFact: LinearConstantPropagationFact) => { /* Only propagate to callees that return integers */ @@ -186,7 +187,7 @@ class LinearConstantPropagationProblem(project: SomeProject) calleeExit: JavaStatement, callee: Method, returnSite: JavaStatement - ): FlowFunction[LinearConstantPropagationFact] = { + )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { (sourceFact: LinearConstantPropagationFact) => { /* Only propagate to return site if callee returns an integer */ @@ -221,8 +222,9 @@ class LinearConstantPropagationProblem(project: SomeProject) override def getCallToReturnFlowFunction( callSite: JavaStatement, + callee: Method, returnSite: JavaStatement - ): FlowFunction[LinearConstantPropagationFact] = { + )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { (sourceFact: LinearConstantPropagationFact) => { immutable.Set(sourceFact) @@ -234,7 +236,7 @@ class LinearConstantPropagationProblem(project: SomeProject) sourceFact: LinearConstantPropagationFact, target: JavaStatement, targetFact: LinearConstantPropagationFact - ): EdgeFunction[LinearConstantPropagationValue] = { + )(implicit propertyStore: PropertyStore): EdgeFunction[LinearConstantPropagationValue] = { if (sourceFact == targetFact) { /* Simply propagates a fact through the method */ return identityEdgeFunction @@ -343,7 +345,7 @@ class LinearConstantPropagationProblem(project: SomeProject) calleeEntry: JavaStatement, calleeEntryFact: LinearConstantPropagationFact, callee: Method - ): EdgeFunction[LinearConstantPropagationValue] = identityEdgeFunction + )(implicit propertyStore: PropertyStore): EdgeFunction[LinearConstantPropagationValue] = identityEdgeFunction override def getReturnEdgeFunction( calleeExit: JavaStatement, @@ -351,12 +353,12 @@ class LinearConstantPropagationProblem(project: SomeProject) callee: Method, returnSite: JavaStatement, returnSiteFact: LinearConstantPropagationFact - ): EdgeFunction[LinearConstantPropagationValue] = identityEdgeFunction + )(implicit propertyStore: PropertyStore): EdgeFunction[LinearConstantPropagationValue] = identityEdgeFunction override def getCallToReturnEdgeFunction( callSite: JavaStatement, callSiteFact: LinearConstantPropagationFact, returnSite: JavaStatement, returnSiteFact: LinearConstantPropagationFact - ): EdgeFunction[LinearConstantPropagationValue] = identityEdgeFunction + )(implicit propertyStore: PropertyStore): EdgeFunction[LinearConstantPropagationValue] = identityEdgeFunction } From 8a08d82dd0fb1029268f59354f53018976c31c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 26 Jun 2024 15:57:27 +0200 Subject: [PATCH 016/167] Refactor linear constant propagation edge functions --- ...nearConstantPropagationEdgeFunctions.scala | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala index f3b349e32d..49559f46e6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala @@ -37,25 +37,26 @@ case class LinearCombinationEdgeFunction(a: Int, b: Int) override def meetWith( otherEdgeFunction: EdgeFunction[LinearConstantPropagationValue] - ): EdgeFunction[LinearConstantPropagationValue] = otherEdgeFunction match { - case LinearCombinationEdgeFunction(a2, b2) if a2 == a && b2 == b => this - case LinearCombinationEdgeFunction(_, _) => VariableValueEdgeFunction + ): EdgeFunction[LinearConstantPropagationValue] = { + otherEdgeFunction match { + case LinearCombinationEdgeFunction(a2, b2) if a2 == a && b2 == b => this + case LinearCombinationEdgeFunction(_, _) => VariableValueEdgeFunction - case VariableValueEdgeFunction => otherEdgeFunction + case VariableValueEdgeFunction => otherEdgeFunction - case IdentityEdgeFunction() => this - case AllTopEdgeFunction(_) => this + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => this - case _ => - throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + case _ => + throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + } } override def equalTo(otherEdgeFunction: EdgeFunction[LinearConstantPropagationValue]): Boolean = { otherEdgeFunction == this || (otherEdgeFunction match { - case otherLinearCombinationEdgeFunction: LinearCombinationEdgeFunction => - otherLinearCombinationEdgeFunction.a == a && otherLinearCombinationEdgeFunction.b == b - case _ => false + case LinearCombinationEdgeFunction(a2, b2) => a == a2 && b == b2 + case _ => false }) } } @@ -66,9 +67,11 @@ case class LinearCombinationEdgeFunction(a: Int, b: Int) object VariableValueEdgeFunction extends AllBottomEdgeFunction[LinearConstantPropagationValue](VariableValue) { override def composeWith( secondEdgeFunction: EdgeFunction[LinearConstantPropagationValue] - ): EdgeFunction[LinearConstantPropagationValue] = secondEdgeFunction match { - case LinearCombinationEdgeFunction(0, _) => secondEdgeFunction - case LinearCombinationEdgeFunction(_, _) => this - case _ => this + ): EdgeFunction[LinearConstantPropagationValue] = { + secondEdgeFunction match { + case LinearCombinationEdgeFunction(0, _) => secondEdgeFunction + case LinearCombinationEdgeFunction(_, _) => this + case _ => this + } } } From ae2fde6c58b6c4654de1afcbe404dd0ea8622517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 12 Jul 2024 13:18:05 +0200 Subject: [PATCH 017/167] Adjust/Fix linear constant propagation edge functions to find more constants --- ...le.java => BranchingConstantsExample.java} | 18 ++++-- .../BranchingLinearCombinationExample.java | 61 ++++++++++++++++++ ...nearConstantPropagationEdgeFunctions.scala | 63 ++++++++++++++----- 3 files changed, 119 insertions(+), 23 deletions(-) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/{BranchingExample.java => BranchingConstantsExample.java} (65%) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingLinearCombinationExample.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingConstantsExample.java similarity index 65% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingExample.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingConstantsExample.java index c6ad122118..e9589d1a7d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingConstantsExample.java @@ -5,25 +5,31 @@ import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; -public class BranchingExample { - @ConstantValue(variable = "lva", value = 8) +public class BranchingConstantsExample { + @ConstantValue(variable = "lvd", value = 8) @VariableValues({ @VariableValue(variable = "lv2"), - @VariableValue(variable = "lv8") + @VariableValue(variable = "lvb"), + @VariableValue(variable = "lvf") }) public static void main(String[] args) { int a = 23; int b = 7; + int c; if (args.length == 0) { a = 42; b = 6; b++; + c = 1; + } else { + c = 2; } - int c = 1 + a; - int d = b + 1; + int d = 1 + a; + int e = b + 1; + int f = 1 - c; - System.out.println("a: " + a + ", b: " + b + ", c: " + c + ", d: " + d); + System.out.println("a: " + a + ", b: " + b + ", c: " + c + ", d: " + d + ", e: " + e + ", f: " + f); } } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingLinearCombinationExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingLinearCombinationExample.java new file mode 100644 index 0000000000..eb95664761 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingLinearCombinationExample.java @@ -0,0 +1,61 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.linear_constant_propagation; + +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValues; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; + +public class BranchingLinearCombinationExample { + private static int linearCalculation1(int y, int x) { + int z; + if (y > 0) { + z = 2 * x + 4; + } else { + z = 3 * x - 3; + } + return z; + } + + private static int linearCalculation2(int y, int x) { + if (y < 0) { + return 4 * x - 13; + } else { + return x + 2; + } + } + + @ConstantValues({ + @ConstantValue(variable = "lv12", value = 19), + @ConstantValue(variable = "lv14", value = 18), + @ConstantValue(variable = "lv1b", value = 7) + }) + @VariableValues({ + @VariableValue(variable = "lv17"), + @VariableValue(variable = "lv1e") + }) + public static void main(String[] args) { + int a = 7; + + if (args.length == 0) { + a = 6; + a++; + } + + int b; + if (args.length == 1) { + b = 2 * a + 6; + } else { + b = 3 * a - 1; + } + + int c = b - 1; + + int d = linearCalculation1(args.length, a); + int e = linearCalculation1(args.length, 4); + int f = linearCalculation2(args.length, a - 2); + int g = linearCalculation2(args.length, 2); + + System.out.println("a: " + a + ", b: " + b + ", c: " + c + ", d: " + d + ", e: " + e + ", f: " + f + ", g: " + g); + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala index 49559f46e6..9189d07486 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala @@ -9,23 +9,41 @@ import org.opalj.ide.problem.IdentityEdgeFunction /** * Edge function to calculate the value of a variable `i` for a statement `val i = a * x + b` */ -case class LinearCombinationEdgeFunction(a: Int, b: Int) - extends EdgeFunction[LinearConstantPropagationValue] { +case class LinearCombinationEdgeFunction( + a: Int, + b: Int, + c: LinearConstantPropagationValue = LinearConstantPropagationLattice.top +) extends EdgeFunction[LinearConstantPropagationValue] { override def compute(sourceValue: LinearConstantPropagationValue): LinearConstantPropagationValue = { - sourceValue match { - case ConstantValue(x) => ConstantValue(a * x + b) - case VariableValue if a == 0 => ConstantValue(b) - case VariableValue => VariableValue - case UnknownValue => UnknownValue - } + LinearConstantPropagationLattice.meet( + sourceValue match { + case ConstantValue(x) => ConstantValue(a * x + b) + case VariableValue if a == 0 => ConstantValue(b) + case VariableValue => VariableValue + case UnknownValue => UnknownValue + }, + c + ) } override def composeWith( secondEdgeFunction: EdgeFunction[LinearConstantPropagationValue] ): EdgeFunction[LinearConstantPropagationValue] = { secondEdgeFunction match { - case LinearCombinationEdgeFunction(a2, b2) => LinearCombinationEdgeFunction(a2 * a, a2 * b + b2) - case VariableValueEdgeFunction => secondEdgeFunction + case LinearCombinationEdgeFunction(a2, b2, c2) => + LinearCombinationEdgeFunction( + a2 * a, + a2 * b + b2, + LinearConstantPropagationLattice.meet( + c match { + case UnknownValue => UnknownValue + case ConstantValue(cValue) => ConstantValue(a2 * cValue + b2) + case VariableValue => VariableValue + }, + c2 + ) + ) + case VariableValueEdgeFunction => secondEdgeFunction case IdentityEdgeFunction() => this case AllTopEdgeFunction(_) => secondEdgeFunction @@ -39,8 +57,19 @@ case class LinearCombinationEdgeFunction(a: Int, b: Int) otherEdgeFunction: EdgeFunction[LinearConstantPropagationValue] ): EdgeFunction[LinearConstantPropagationValue] = { otherEdgeFunction match { - case LinearCombinationEdgeFunction(a2, b2) if a2 == a && b2 == b => this - case LinearCombinationEdgeFunction(_, _) => VariableValueEdgeFunction + case LinearCombinationEdgeFunction(a2, b2, c2) if a2 == a && b2 == b => + LinearCombinationEdgeFunction(a, b, LinearConstantPropagationLattice.meet(c, c2)) + case LinearCombinationEdgeFunction(a2, b2, c2) if a2 != a && (b - b2) % (a2 - a) == 0 => + val cNew = LinearConstantPropagationLattice.meet( + ConstantValue(a * ((b - b2) / (a2 - a)) + b), + LinearConstantPropagationLattice.meet(c, c2) + ) + cNew match { + case VariableValue => VariableValueEdgeFunction + case _ => LinearCombinationEdgeFunction(a, b, cNew) + } + case LinearCombinationEdgeFunction(_, _, _) => + VariableValueEdgeFunction case VariableValueEdgeFunction => otherEdgeFunction @@ -55,8 +84,8 @@ case class LinearCombinationEdgeFunction(a: Int, b: Int) override def equalTo(otherEdgeFunction: EdgeFunction[LinearConstantPropagationValue]): Boolean = { otherEdgeFunction == this || (otherEdgeFunction match { - case LinearCombinationEdgeFunction(a2, b2) => a == a2 && b == b2 - case _ => false + case LinearCombinationEdgeFunction(a2, b2, c2) => a == a2 && b == b2 && c == c2 + case _ => false }) } } @@ -69,9 +98,9 @@ object VariableValueEdgeFunction extends AllBottomEdgeFunction[LinearConstantPro secondEdgeFunction: EdgeFunction[LinearConstantPropagationValue] ): EdgeFunction[LinearConstantPropagationValue] = { secondEdgeFunction match { - case LinearCombinationEdgeFunction(0, _) => secondEdgeFunction - case LinearCombinationEdgeFunction(_, _) => this - case _ => this + case LinearCombinationEdgeFunction(0, _, _) => secondEdgeFunction + case LinearCombinationEdgeFunction(_, _, _) => this + case _ => this } } } From 9e9326c82931580d03be4b28b92705d81b0b7002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 12 Jul 2024 17:50:17 +0200 Subject: [PATCH 018/167] Implement end summaries extension --- .../org/opalj/ide/solver/IDEAnalysis.scala | 52 +++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index ee0c3d4f34..e6429bc582 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -74,13 +74,19 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent */ private val summaryFunctions: SummaryFunctions = mutable.Map.empty + /** + * Collection of seen end nodes with corresponding jump function (needed for endSummaries extension) + */ + private val endSummaries = mutable.Map.empty[Node, mutable.Set[(Node, JumpFunction)]] + /** * Collection of all callables that were visited in P1 */ private val seenCallables = mutable.Set.empty[Callable] /** - * Map call targets to all possible (resp. seen) call sources (similar to a call graph but reversed) + * Map call targets to all seen call sources (similar to a call graph but reversed; needed for endSummaries + * extension) */ private val callTargetsToSources = mutable.Map.empty[Node, mutable.Set[Node]] @@ -142,6 +148,16 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent summaryFunctions.getOrElse(path, allTopEdgeFunction) // else part handels IDE lines 3 - 4 } + def addEndSummary(path: Path, jumpFunction: JumpFunction): Unit = { + val (start, end) = path + val set = endSummaries.getOrElseUpdate(start, mutable.Set.empty) + set.add((end, jumpFunction)) + } + + def getEndSummaries(start: Node): collection.Set[(Node, JumpFunction)] = { + endSummaries.getOrElse(start, collection.Set.empty) + } + def rememberCallable(callable: Callable): Unit = { seenCallables.add(callable) } @@ -303,6 +319,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logWarn(s"Statement ${icfg.stringifyStatement(n)} is detected as call statement but no callees were found!") } + val rs = icfg.getNextStatements(n) // IDE P1 line 14 + qs.foreach { q => logDebug(s"handling call target q=$q") @@ -318,11 +336,36 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent d3s.foreach { d3 => s.rememberCallEdge(((n, d2), (sq, d3))) - propagate(((sq, d3), (sq, d3)), identityEdgeFunction) + + val endSummaries = s.getEndSummaries((sq, d3)) + // Handling for end summaries extension + if (endSummaries.nonEmpty) { + endSummaries.foreach { case ((eq, d4), fEndSummary) => + val f4 = problem.getCallEdgeFunction(n, d2, sq, d3, q) + rs.foreach { r => + val d5s = problem.getReturnFlowFunction(eq, q, r).compute(d4) + d5s.foreach { d5 => + val f5 = problem.getReturnEdgeFunction(eq, d4, q, r, d5) + val callToReturnPath = ((n, d2), (r, d5)) + val oldSummaryFunction = s.getSummaryFunction(callToReturnPath) + val fPrime = + f4.composeWith(fEndSummary).composeWith(f5).meetWith(oldSummaryFunction) + + if (!fPrime.equalTo(oldSummaryFunction)) { + s.setSummaryFunction(callToReturnPath, fPrime) + + propagate(((sp, d1), (r, d5)), f.composeWith(fPrime)) + } + } + } + } + } else { + // Default algorithm behavior + propagate(((sq, d3), (sq, d3)), identityEdgeFunction) + } } } - val rs = icfg.getNextStatements(n) // IDE P1 line 14 rs.foreach { r => val d3s = problem.getCallToReturnFlowFunction(n, q, r).compute(d2) @@ -350,6 +393,9 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val ((sp, d1), (n, d2)) = path val p = icfg.getCallable(n) + // Handling for end summaries extension + s.addEndSummary(path, f) + // IDE P1 line 20 val callSources = s.lookupCallSourcesForTarget(sp, d1) callSources.foreach { From f61124e039f6fa5c99f6ca37cf2ceed96dd80de2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 12 Jul 2024 17:52:21 +0200 Subject: [PATCH 019/167] Use end summaries in linear constant propagation --- .../LinearConstantPropagationProblem.scala | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 01443fede2..73f8012601 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -142,7 +142,7 @@ class LinearConstantPropagationProblem(project: SomeProject) /* Always propagate null facts */ immutable.Set(sourceFact) - case VariableFact(name, definedAtIndex) => + case VariableFact(_, definedAtIndex) => val callStmt = JavaIFDSProblem.asCall(callSite.stmt) /* Parameters and their types (excluding the implicit 'this' reference) */ @@ -157,25 +157,7 @@ class LinearConstantPropagationProblem(project: SomeProject) paramTypes(index).isIntegerType && param.asVar.definedBy.contains(definedAtIndex) } .map { case (_, index) => - // TODO (IDE) CREATING A FACT WITH A NAME MADE UP OF THE ORIGINAL NAME AND THE - // PARAMETER NAME IS REQUIRED AS ANALYZING A CALL TO A CALLEE THAT ALREADY HAS BEEN - // ANALYZED WOULD STOP IMMEDIATELY OTHERWISE - // REASON: - // - THE PATH THAT IS PROPAGATED IS MADE UP OF THE START STATEMENT AND THE - // PARAMETER AS THE FACT AND THUS ONLY DEPENDS ON THE CALLEE AND IS ALREADY KNOWN - // - CALLING 'propagate' RETURNS WITHOUT ENQUEUING BECAUSE IT FINDS THE IDENTITY - // FUNCTION FOR EXACTLY THIS PATH - // - WE NEVER REACH THE RETURN STATEMENT OF THE CALLEE AGAIN - // - THE CALL STATEMENT IS ADDED AS POSSIBLE CALL SOURCE BUT IT IS NEVER PROCESSED - // IN THE RETURN FLOW - // - THERE WILL NEVER BE A COMPLETE SUMMARY FUNCTION FOR THE CALL - // - IF THE CALL HAPPENED IN AN ASSIGNMENT: THE ASSIGNED VARIABLE WILL NEVER BE - // GENERATED A FACT FOR - // SOLUTION: - // - EITHER KEEP FACTS UNIQUE (THE CURRENT SOLUTION) -> ALWAYS REANALYZES THE - // CALLEE COMPLETELY - // - ADJUST SOLVER TO USE A CONCEPT LIKE ENDSUMMARIES (SEE naeem2010practical) - VariableFact(s"$name-param${index + 1}", -(index + 2)) + VariableFact(s"param${index + 1}", -(index + 2)) } .toSet } From 9bed114b2c6aa577f576e55419c61a41057382af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 15 Jul 2024 08:17:16 +0200 Subject: [PATCH 020/167] Move safety check to ICFG implementation --- .../scala/org/opalj/ide/solver/IDEAnalysis.scala | 5 ----- .../tac/fpcf/analyses/ide/solver/JavaICFG.scala | 13 ++++++++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index e6429bc582..3e5fd4a77b 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -314,11 +314,6 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val ((sp, d1), (n, d2)) = path - if (qs.isEmpty) { - // TODO (IDE) THIS CASE SHOULD NEVER OCCUR -> REQUIRES FURTHER DEBUGGING - logWarn(s"Statement ${icfg.stringifyStatement(n)} is detected as call statement but no callees were found!") - } - val rs = icfg.getNextStatements(n) // IDE P1 line 14 qs.foreach { q => diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala index bbfcd76404..ad301cd72d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala @@ -51,9 +51,20 @@ class JavaICFG(project: SomeProject) extends ICFG[JavaStatement, Method] { if (stmt.isReturnNode) { None } else { - baseICFG.getCalleesIfCallStatement( + val calleesOption = baseICFG.getCalleesIfCallStatement( org.opalj.tac.fpcf.analyses.ifds.JavaStatement(stmt.method, stmt.index, stmt.code, stmt.cfg) ) + calleesOption match { + case None => None + case Some(callees) => + if (callees.isEmpty) { + throw new IllegalStateException( + s"Statement ${stringifyStatement(stmt)} is detected as call statement but no callees were found!" + ) + } else { + Some(callees) + } + } } } From 75603251912f09493799a025688d4361c7455051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 17 Jul 2024 11:42:00 +0200 Subject: [PATCH 021/167] Refactor flow recording --- .../opalj/ide/problem/FlowRecordingIDEProblem.scala | 7 ++++++- .../opalj/ide/solver/FlowRecordingIDEAnalysis.scala | 10 +++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index 7c0b594390..aeaf37275a 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -215,7 +215,7 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal /** * Stop recording and finish writing */ - def stopRecording(): Unit = { + def stopRecording(): Writer = { if (uniqueFlowsOnly) { val seenFlows = mutable.Set.empty[String] collectedFlows.foreach { dotEdge => @@ -232,7 +232,12 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal writer.write("}\n") writer.flush() + val writerTmp = writer + this.writer = null + collectedFlows.clear() collectedEdgeFunctions.clear() + + writerTmp } } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala index 49b91b65e3..2e466b445c 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala @@ -88,15 +88,15 @@ class FlowRecordingIDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Ca completePath.toFile } - override def performAnalysis(callable: Callable): ProperPropertyComputationResult = { + def startRecording(): Unit = { val writer = new FileWriter(getFile) flowRecordingProblem.startRecording(writer) - val result = super.performAnalysis(callable) - flowRecordingProblem.stopRecording() + } - writer.close() + def stopRecording(): Unit = { + val writer = flowRecordingProblem.stopRecording() - result + writer.close() } } From b518340bec6cd468110d3c8805d059b31abd6071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 17 Jul 2024 11:58:28 +0200 Subject: [PATCH 022/167] Add more logging --- .../opalj/ide/solver/FlowRecordingIDEAnalysis.scala | 5 ++++- .../main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 11 ++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala index 2e466b445c..93b93ef00a 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala @@ -11,7 +11,6 @@ import java.nio.file.Paths import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.Entity -import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.ide.problem.FlowRecorderModes import org.opalj.ide.problem.FlowRecorderModes.FlowRecorderMode @@ -89,12 +88,16 @@ class FlowRecordingIDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Ca } def startRecording(): Unit = { + logDebug("Starting recording") + val writer = new FileWriter(getFile) flowRecordingProblem.startRecording(writer) } def stopRecording(): Unit = { + logDebug("Stopping recording") + val writer = flowRecordingProblem.stopRecording() writer.close() diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 3e5fd4a77b..880323541a 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -1,6 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ide.solver +import scala.annotation.unused + import scala.collection.mutable import org.opalj.br.analyses.SomeProject @@ -228,15 +230,16 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent s"Path(\n$indent\t${nodeToString(path._1, s"$indent\t")} ->\n$indent\t${nodeToString(path._2, s"$indent\t")}\n$indent)" } - private def logWarn(message: => String): Unit = { + @unused + protected def logWarn(message: => String): Unit = { OPALLogger.warn(FrameworkName, message) } - private def logDebug(message: => String): Unit = { + protected def logDebug(message: => String): Unit = { OPALLogger.debug({ isDebug }, s"$FrameworkName - debug", message) } - private def logTrace(message: => String): Unit = { + protected def logTrace(message: => String): Unit = { OPALLogger.debug({ isTrace }, s"$FrameworkName - trace", message) } @@ -246,6 +249,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent */ // TODO (IDE) WHAT HAPPENS WHEN ANALYZING MULTIPLE CALLABLES? CAN WE CACHE E.G. JUMP/SUMMARY FUNCTIONS? def performAnalysis(callable: Callable): ProperPropertyComputationResult = { + logDebug(s"performing ${getClass.getSimpleName} for $callable") + implicit val state: State = new State logDebug("starting phase 1") From 1cd65516f864d1648cf11f87df624df5536d22b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 19 Jul 2024 14:56:09 +0200 Subject: [PATCH 023/167] Introduce EdgeFunctionResult to allow usage of lazily computed properties when generating edge functions --- .../ide/problem/EdgeFunctionResult.scala | 22 +++ .../ide/problem/FlowRecordingIDEProblem.scala | 45 +++-- .../org/opalj/ide/problem/IDEProblem.scala | 14 +- .../org/opalj/ide/solver/IDEAnalysis.scala | 157 +++++++++++++++--- .../LinearConstantPropagationProblem.scala | 9 +- 5 files changed, 201 insertions(+), 46 deletions(-) create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala new file mode 100644 index 0000000000..d1c09e4095 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala @@ -0,0 +1,22 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.problem + +import org.opalj.fpcf.SomeEPK + +/** + * Interface for encapsulating different states of edge functions + */ +trait EdgeFunctionResult[Value <: IDEValue] + +/** + * Represent an edge function that is final + */ +case class FinalEdgeFunction[Value <: IDEValue](edgeFunction: EdgeFunction[Value]) extends EdgeFunctionResult[Value] + +/** + * Represent an interim edge function that may change when the result of one of the dependees changes + * @param interimEdgeFunction an interim edge function to use until new results are present (has to be an upper bound of + * the final edge function) + */ +case class InterimEdgeFunction[Value <: IDEValue](interimEdgeFunction: EdgeFunction[Value], dependees: Set[SomeEPK]) + extends EdgeFunctionResult[Value] diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index aeaf37275a..582131b2ef 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -111,10 +111,13 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal sourceFact: Fact, target: Statement, targetFact: Fact - )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { - val edgeFunction = baseProblem.getNormalEdgeFunction(source, sourceFact, target, targetFact) - collectedEdgeFunctions.put(createDotEdge(source, sourceFact, target, targetFact, "normal flow"), edgeFunction) - edgeFunction + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] = { + val edgeFunctionResult = baseProblem.getNormalEdgeFunction(source, sourceFact, target, targetFact) + collectedEdgeFunctions.put( + createDotEdge(source, sourceFact, target, targetFact, "normal flow"), + getEdgeFunctionFromEdgeFunctionResult(edgeFunctionResult) + ) + edgeFunctionResult } override def getCallEdgeFunction( @@ -123,13 +126,13 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal calleeEntry: Statement, calleeEntryFact: Fact, callee: Callable - )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { - val edgeFunction = baseProblem.getCallEdgeFunction(callSite, callSiteFact, calleeEntry, calleeEntryFact, callee) + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] = { + val edgeFunctionResult = baseProblem.getCallEdgeFunction(callSite, callSiteFact, calleeEntry, calleeEntryFact, callee) collectedEdgeFunctions.put( createDotEdge(callSite, callSiteFact, calleeEntry, calleeEntryFact, "call flow"), - edgeFunction + getEdgeFunctionFromEdgeFunctionResult(edgeFunctionResult) ) - edgeFunction + edgeFunctionResult } override def getReturnEdgeFunction( @@ -138,14 +141,14 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal callee: Callable, returnSite: Statement, returnSiteFact: Fact - )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { - val edgeFunction = + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] = { + val edgeFunctionResult = baseProblem.getReturnEdgeFunction(calleeExit, calleeExitFact, callee, returnSite, returnSiteFact) collectedEdgeFunctions.put( createDotEdge(calleeExit, calleeExitFact, returnSite, returnSiteFact, "return flow"), - edgeFunction + getEdgeFunctionFromEdgeFunctionResult(edgeFunctionResult) ) - edgeFunction + edgeFunctionResult } override def getCallToReturnEdgeFunction( @@ -153,13 +156,14 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal callSiteFact: Fact, returnSite: Statement, returnSiteFact: Fact - )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { - val edgeFunction = baseProblem.getCallToReturnEdgeFunction(callSite, callSiteFact, returnSite, returnSiteFact) + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] = { + val edgeFunctionResult = + baseProblem.getCallToReturnEdgeFunction(callSite, callSiteFact, returnSite, returnSiteFact) collectedEdgeFunctions.put( createDotEdge(callSite, callSiteFact, returnSite, returnSiteFact, "call-to-return flow"), - edgeFunction + getEdgeFunctionFromEdgeFunctionResult(edgeFunctionResult) ) - edgeFunction + edgeFunctionResult } private def createDotEdge( @@ -172,6 +176,15 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal (source, sourceFact, target, targetFact, flowType) } + private def getEdgeFunctionFromEdgeFunctionResult( + edgeFunctionResult: EdgeFunctionResult[Value] + ): EdgeFunction[Value] = { + edgeFunctionResult match { + case FinalEdgeFunction(edgeFunction) => edgeFunction + case InterimEdgeFunction(interimEdgeFunction, _) => interimEdgeFunction + } + } + /** * Start recording * @param writer to write the graph to diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index 908d66ddbf..8cf8d4ef2b 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -1,6 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ide.problem +import scala.language.implicitConversions + import org.opalj.fpcf.Entity import org.opalj.fpcf.PropertyStore import org.opalj.ide.solver.ICFG @@ -11,6 +13,10 @@ import org.opalj.ide.solver.ICFG abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( val icfg: ICFG[Statement, Callable] ) { + implicit def edgeFunctionToFinalEdgeFunction(edgeFunction: EdgeFunction[Value]): EdgeFunctionResult[Value] = { + FinalEdgeFunction(edgeFunction) + } + /** * The null fact to use. Also used to bootstrap the analysis at the entry points. */ @@ -72,7 +78,7 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl sourceFact: Fact, target: Statement, targetFact: Fact - )(implicit propertyStore: PropertyStore): EdgeFunction[Value] + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] /** * Generate an edge function for a call flow @@ -88,7 +94,7 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl calleeEntry: Statement, calleeEntryFact: Fact, callee: Callable - )(implicit propertyStore: PropertyStore): EdgeFunction[Value] + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] /** * Generate an edge function for a return flow @@ -104,7 +110,7 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl callee: Callable, returnSite: Statement, returnSiteFact: Fact - )(implicit propertyStore: PropertyStore): EdgeFunction[Value] + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] /** * Generate an edge function for a call-to-return flow @@ -118,5 +124,5 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl callSiteFact: Fact, returnSite: Statement, returnSiteFact: Fact - )(implicit propertyStore: PropertyStore): EdgeFunction[Value] + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 880323541a..c3cab52652 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -8,18 +8,23 @@ import scala.collection.mutable import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.fpcf.Entity +import org.opalj.fpcf.InterimResult import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.Result +import org.opalj.fpcf.SomeEPK import org.opalj.ide.ConfigKeyDebugLog import org.opalj.ide.ConfigKeyTraceLog import org.opalj.ide.FrameworkName import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.ide.problem.AllTopEdgeFunction import org.opalj.ide.problem.EdgeFunction +import org.opalj.ide.problem.EdgeFunctionResult +import org.opalj.ide.problem.FinalEdgeFunction import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IdentityEdgeFunction import org.opalj.ide.problem.IDEProblem import org.opalj.ide.problem.IDEValue +import org.opalj.ide.problem.InterimEdgeFunction import org.opalj.log.OPALLogger /** @@ -102,6 +107,11 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent */ private val values: Values = mutable.Map.empty + /** + * Map outstanding EPKs to the continuations to be executed when a new result is available + */ + private val dependees = mutable.Map.empty[SomeEPK, mutable.Set[() => Unit]] + def enqueuePath(path: Path): Unit = { pathWorkList.enqueue(path) } @@ -215,6 +225,27 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent (s1, s2) => s1.union(s2) } } + + def addDependee(epk: SomeEPK, c: () => Unit): Unit = { + val set = dependees.getOrElseUpdate(epk, mutable.Set.empty) + set.add(c) + } + + def areDependeesEmpty: Boolean = { + dependees.isEmpty + } + + def getDependeesSize: Int = { + dependees.size + } + + def getDependees: collection.Set[SomeEPK] = { + dependees.keySet + } + + def getAndRemoveDependeeContinuations(epk: SomeEPK): Set[() => Unit] = { + dependees.remove(epk).getOrElse(Set.empty).toSet + } } private val icfg: ICFG[Statement, Callable] = problem.icfg @@ -259,21 +290,57 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent seedPhase1(callable) processPathWorkList() - logDebug("finished phase 1") - - logDebug("starting phase 2") - - // Phase 2 - seedPhase2(callable) - computeValues() - - logDebug("finished phase 2") - - logDebug("collecting results for property creation") - - // Build and return result - val property = propertyMetaInformation.createProperty(state.collectResults(callable)) - Result(callable, property) + def c(): ProperPropertyComputationResult = { + logDebug("finished phase 1") + + logDebug("starting phase 2") + + // Phase 2 + seedPhase2(callable) + computeValues() + + logDebug("finished phase 2") + + logDebug("collecting results for property creation") + + // Build and return result + val property = propertyMetaInformation.createProperty(state.collectResults(callable)) + Result(callable, property) + } + + if (!state.areDependeesEmpty) { + logDebug(s"there are ${state.getDependeesSize} outstanding dependees") + + def createInterimResult(): ProperPropertyComputationResult = { + InterimResult.forUB( + callable, + // TODO (IDE) THIS WILL BE AN 'EMPTY' PROPERTY -> PROBLEMATIC WITH CYCLIC IDE ANALYSES + // - WHAT IF WE RUN PHASE 2 BEFORE RETURNING? + // - DOES THIS PRODUCE VALID RESULTS (I.E. ALWAYS UPPER BOUND)? + // - DO WE NEED TO RERUN PHASE 2 FROM SCRATCH EACH TIME? + propertyMetaInformation.createProperty(state.collectResults(callable)), + state.getDependees.toSet, + eps => { + // Get and call continuations that are remembered for the EPK + val cs = state.getAndRemoveDependeeContinuations(eps.toEPK) + cs.foreach(c => c()) + // The continuations could have enqueued paths to the path work list + processPathWorkList() + + if (state.areDependeesEmpty) { + logDebug(s"all outstanding dependees have been processed") + c() + } else { + logDebug(s"there are ${state.getDependeesSize} outstanding dependees left") + createInterimResult() + } + } + ) + } + createInterimResult() + } else { + c() + } } private def seedPhase1(callable: Callable)(implicit s: State): Unit = { @@ -341,11 +408,14 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // Handling for end summaries extension if (endSummaries.nonEmpty) { endSummaries.foreach { case ((eq, d4), fEndSummary) => - val f4 = problem.getCallEdgeFunction(n, d2, sq, d3, q) + val f4 = handleEdgeFunctionResult(problem.getCallEdgeFunction(n, d2, sq, d3, q), path) rs.foreach { r => val d5s = problem.getReturnFlowFunction(eq, q, r).compute(d4) d5s.foreach { d5 => - val f5 = problem.getReturnEdgeFunction(eq, d4, q, r, d5) + val f5 = handleEdgeFunctionResult( + problem.getReturnEdgeFunction(eq, d4, q, r, d5), + path + ) val callToReturnPath = ((n, d2), (r, d5)) val oldSummaryFunction = s.getSummaryFunction(callToReturnPath) val fPrime = @@ -373,7 +443,10 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // IDE P1 lines 15 - 16 d3s.foreach { d3 => - propagate(((sp, d1), (r, d3)), f.composeWith(problem.getCallToReturnEdgeFunction(n, d2, r, d3))) + propagate( + ((sp, d1), (r, d3)), + f.composeWith(handleEdgeFunctionResult(problem.getCallToReturnEdgeFunction(n, d2, r, d3), path)) + ) } // IDE P1 lines 17 - 18 @@ -411,8 +484,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent d5s.foreach { d5 => // IDE P1 lines 22 - 23 - val f4 = problem.getCallEdgeFunction(c, d4, sp, d1, p) - val f5 = problem.getReturnEdgeFunction(n, d2, p, r, d5) + val f4 = handleEdgeFunctionResult(problem.getCallEdgeFunction(c, d4, sp, d1, p), path) + val f5 = handleEdgeFunctionResult(problem.getReturnEdgeFunction(n, d2, p, r, d5), path) // IDE P1 line 24 val callToReturnPath = ((c, d4), (r, d5)) @@ -450,7 +523,10 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logTrace(s"generated the following d3s=$d3s for next statement m=${icfg.stringifyStatement(m)}") d3s.foreach { d3 => - propagate(((sp, d1), (m, d3)), f.composeWith(problem.getNormalEdgeFunction(n, d2, m, d3))) + propagate( + ((sp, d1), (m, d3)), + f.composeWith(handleEdgeFunctionResult(problem.getNormalEdgeFunction(n, d2, m, d3), path)) + ) } } } @@ -472,6 +548,30 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } } + /** + * @param path the path to re-enqueue when getting an interim edge function + * @return the (interim) edge function from the result + */ + private def handleEdgeFunctionResult( + edgeFunctionResult: EdgeFunctionResult[Value], + path: Path + )( + implicit s: State + ): EdgeFunction[Value] = { + edgeFunctionResult match { + case FinalEdgeFunction(edgeFunction) => + edgeFunction + case InterimEdgeFunction(intermediateEdgeFunction, dependees) => + dependees.foreach { dependee => + s.addDependee( + dependee, + () => s.enqueuePath(path) + ) + } + intermediateEdgeFunction + } + } + private def seedPhase2(callable: Callable)(implicit s: State): Unit = { // IDE P2 lines 2 - 3 icfg.getStartStatements(callable).foreach { stmt => @@ -595,7 +695,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent dPrimes.foreach { dPrime => propagateValue( (sq, dPrime), - problem.getCallEdgeFunction(n, d, sq, dPrime, q).compute(s.getValue(node)) + enforceFinalEdgeFunction(problem.getCallEdgeFunction(n, d, sq, dPrime, q)) + .compute(s.getValue(node)) ) } } @@ -618,4 +719,16 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logTrace(s"nothing to do as oldValue=$oldValue == vPrime=$vPrime") } } + + // TODO (IDE) THIS WILL NOT BE POSSIBLE ANY LONGER IF RETURNING AN INTERIM RESULT INVOLVES EXECUTING PHASE 2 + private def enforceFinalEdgeFunction(edgeFunctionResult: EdgeFunctionResult[Value]): EdgeFunction[Value] = { + edgeFunctionResult match { + case FinalEdgeFunction(edgeFunction) => + edgeFunction + case _ => + throw new IllegalStateException( + s"All edge functions should be final in phase 2 but got $edgeFunctionResult!" + ) + } + } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 73f8012601..7adb66fa07 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -9,6 +9,7 @@ import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.PropertyStore import org.opalj.ide.problem.EdgeFunction +import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.FlowFunction import org.opalj.ide.problem.IdentityEdgeFunction import org.opalj.ide.problem.MeetLattice @@ -218,7 +219,7 @@ class LinearConstantPropagationProblem(project: SomeProject) sourceFact: LinearConstantPropagationFact, target: JavaStatement, targetFact: LinearConstantPropagationFact - )(implicit propertyStore: PropertyStore): EdgeFunction[LinearConstantPropagationValue] = { + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { if (sourceFact == targetFact) { /* Simply propagates a fact through the method */ return identityEdgeFunction @@ -327,7 +328,7 @@ class LinearConstantPropagationProblem(project: SomeProject) calleeEntry: JavaStatement, calleeEntryFact: LinearConstantPropagationFact, callee: Method - )(implicit propertyStore: PropertyStore): EdgeFunction[LinearConstantPropagationValue] = identityEdgeFunction + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = identityEdgeFunction override def getReturnEdgeFunction( calleeExit: JavaStatement, @@ -335,12 +336,12 @@ class LinearConstantPropagationProblem(project: SomeProject) callee: Method, returnSite: JavaStatement, returnSiteFact: LinearConstantPropagationFact - )(implicit propertyStore: PropertyStore): EdgeFunction[LinearConstantPropagationValue] = identityEdgeFunction + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = identityEdgeFunction override def getCallToReturnEdgeFunction( callSite: JavaStatement, callSiteFact: LinearConstantPropagationFact, returnSite: JavaStatement, returnSiteFact: LinearConstantPropagationFact - )(implicit propertyStore: PropertyStore): EdgeFunction[LinearConstantPropagationValue] = identityEdgeFunction + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = identityEdgeFunction } From 542b49e7f6ac5b7064c4c4c9338146bc6e854c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 19 Jul 2024 15:00:30 +0200 Subject: [PATCH 024/167] Small fixes --- .../org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala | 4 ++-- .../src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala index 93b93ef00a..8740d65516 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala @@ -88,7 +88,7 @@ class FlowRecordingIDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Ca } def startRecording(): Unit = { - logDebug("Starting recording") + logDebug("starting recording") val writer = new FileWriter(getFile) @@ -96,7 +96,7 @@ class FlowRecordingIDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Ca } def stopRecording(): Unit = { - logDebug("Stopping recording") + logDebug("stopping recording") val writer = flowRecordingProblem.stopRecording() diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index c3cab52652..fcb22a7f79 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -167,7 +167,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } def getEndSummaries(start: Node): collection.Set[(Node, JumpFunction)] = { - endSummaries.getOrElse(start, collection.Set.empty) + endSummaries.getOrElse(start, Set.empty) } def rememberCallable(callable: Callable): Unit = { @@ -186,7 +186,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } def lookupCallSourcesForTarget(target: Statement, targetFact: Fact): collection.Set[Node] = { - callTargetsToSources.getOrElse((target, targetFact), collection.Set.empty) + callTargetsToSources.getOrElse((target, targetFact), Set.empty) } def enqueueNode(node: Node): Unit = { @@ -379,8 +379,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } } - private def processCallFlow(path: Path, f: JumpFunction, qs: collection.Set[? <: Callable])(implicit - s: State + private def processCallFlow(path: Path, f: JumpFunction, qs: collection.Set[? <: Callable])( + implicit s: State ): Unit = { logDebug("processing as call flow") From 1e778011e741f9d32ace9d3431f001c1c2e1363b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 24 Jul 2024 13:25:06 +0200 Subject: [PATCH 025/167] Add equality check for properties --- .../org/opalj/ide/integration/IDEProperty.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala index da6c71473a..f88d61bf44 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala @@ -31,4 +31,16 @@ class BasicIDEProperty[Statement, Fact <: IDEFact, Value <: IDEValue]( s"\t$stmt:\n\t\t${result.map { case (fact, value) => s"($fact,$value)" }.mkString("\n\t\t")}" }.mkString("\n")}" } + + override def equals(obj: Any): Boolean = { + obj match { + case basicIDEProperty: BasicIDEProperty[?, ?, ?] => + results == basicIDEProperty.results && propertyMetaInformation == basicIDEProperty.propertyMetaInformation + case _ => false + } + } + + override def hashCode(): Int = { + results.hashCode() * 31 + propertyMetaInformation.hashCode() + } } From 7615102d993d4d4df357cce6ae3a22a6fc3a26d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 24 Jul 2024 14:08:45 +0200 Subject: [PATCH 026/167] Reduce dependencies to IFDS --- .../LinearConstantPropagationProblem.scala | 10 ++++---- .../analyses/ide/solver/JavaStatement.scala | 24 +++++++++++++++---- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 7adb66fa07..abdb163b50 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -24,7 +24,7 @@ import org.opalj.tac.Var import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement -import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall /** * Definition of the linear constant propagation problem @@ -64,7 +64,7 @@ class LinearConstantPropagationProblem(project: SomeProject) } private def isExpressionInfluencedByFact( - expr: Expr[JavaIFDSProblem.V], + expr: Expr[JavaStatement.V], sourceFact: LinearConstantPropagationFact ): Boolean = { expr.astID match { @@ -144,7 +144,7 @@ class LinearConstantPropagationProblem(project: SomeProject) immutable.Set(sourceFact) case VariableFact(_, definedAtIndex) => - val callStmt = JavaIFDSProblem.asCall(callSite.stmt) + val callStmt = callSite.stmt.asCall() /* Parameters and their types (excluding the implicit 'this' reference) */ val params = callStmt.params @@ -234,8 +234,8 @@ class LinearConstantPropagationProblem(project: SomeProject) } private def getEdgeFunctionForExpression( - expr: Expr[JavaIFDSProblem.V] ): EdgeFunction[LinearConstantPropagationValue] = { + expr: Expr[JavaStatement.V] expr.astID match { case IntConst.ASTID => LinearCombinationEdgeFunction(0, expr.asIntConst.value) @@ -252,7 +252,7 @@ class LinearConstantPropagationProblem(project: SomeProject) } /* Try to resolve an constant or variable expression to a constant value */ - val getValueForExpr: Expr[JavaIFDSProblem.V] => Option[Int] = expr => { + val getValueForExpr: Expr[JavaStatement.V] => Option[Int] = expr => { expr.astID match { case Var.ASTID => val var0 = expr.asVar diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala index 7fe8f5d5e8..7b1daabac5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala @@ -4,9 +4,13 @@ package org.opalj.tac.fpcf.analyses.ide.solver import org.opalj.br.Method import org.opalj.br.cfg.BasicBlock import org.opalj.br.cfg.CFG +import org.opalj.tac.Assignment +import org.opalj.tac.Call +import org.opalj.tac.DUVar +import org.opalj.tac.ExprStmt import org.opalj.tac.Stmt import org.opalj.tac.TACStmts -import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V +import org.opalj.value.ValueInformation /** * Class to model statements used with IDE analyses @@ -17,12 +21,12 @@ case class JavaStatement( method: Method, index: Int, isReturnNode: Boolean = false, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] + code: Array[Stmt[JavaStatement.V]], + cfg: CFG[Stmt[JavaStatement.V], TACStmts[JavaStatement.V]] ) { // TODO (IDE) DOES THIS GET REEVALUATED EACH CALL? DO WE ALWAYS CALL IT AT LEAST ONCE? MAYBE MAKE IT A (LAZY) // PROPERTY - def stmt: Stmt[V] = code(index) + def stmt: Stmt[JavaStatement.V] = code(index) // TODO (IDE) DOES THIS GET REEVALUATED EACH CALL? DO WE ALWAYS CALL IT AT LEAST ONCE? MAYBE MAKE IT A (LAZY) // PROPERTY @@ -43,3 +47,15 @@ case class JavaStatement( s"${method.classFile.thisType.simpleName}:${method.name}[$index]$returnOptional{$stmt}" } } + +object JavaStatement { + type V = DUVar[ValueInformation] + + implicit class StmtAsCall(stmt: Stmt[JavaStatement.V]) { + def asCall(): Call[V] = stmt.astID match { + case Assignment.ASTID => stmt.asAssignment.expr.asFunctionCall + case ExprStmt.ASTID => stmt.asExprStmt.expr.asFunctionCall + case _ => stmt.asMethodCall + } + } +} From 84c24c0c790988476141857d0337c155a9677210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 24 Jul 2024 17:25:35 +0200 Subject: [PATCH 027/167] Fix dependee handling --- .../ide/problem/EdgeFunctionResult.scala | 8 ++++--- .../org/opalj/ide/solver/IDEAnalysis.scala | 22 +++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala index d1c09e4095..083d1a0b88 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ide.problem -import org.opalj.fpcf.SomeEPK +import org.opalj.fpcf.SomeEOptionP /** * Interface for encapsulating different states of edge functions @@ -18,5 +18,7 @@ case class FinalEdgeFunction[Value <: IDEValue](edgeFunction: EdgeFunction[Value * @param interimEdgeFunction an interim edge function to use until new results are present (has to be an upper bound of * the final edge function) */ -case class InterimEdgeFunction[Value <: IDEValue](interimEdgeFunction: EdgeFunction[Value], dependees: Set[SomeEPK]) - extends EdgeFunctionResult[Value] +case class InterimEdgeFunction[Value <: IDEValue]( + interimEdgeFunction: EdgeFunction[Value], + dependees: Set[SomeEOptionP] +) extends EdgeFunctionResult[Value] diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index fcb22a7f79..952995a69e 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -11,6 +11,7 @@ import org.opalj.fpcf.Entity import org.opalj.fpcf.InterimResult import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.Result +import org.opalj.fpcf.SomeEOptionP import org.opalj.fpcf.SomeEPK import org.opalj.ide.ConfigKeyDebugLog import org.opalj.ide.ConfigKeyTraceLog @@ -108,9 +109,10 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent private val values: Values = mutable.Map.empty /** - * Map outstanding EPKs to the continuations to be executed when a new result is available + * Map outstanding EPKs to the last processed property result and the continuations to be executed when a new + * result is available */ - private val dependees = mutable.Map.empty[SomeEPK, mutable.Set[() => Unit]] + private val dependees = mutable.Map.empty[SomeEPK, (SomeEOptionP, mutable.Set[() => Unit])] def enqueuePath(path: Path): Unit = { pathWorkList.enqueue(path) @@ -226,8 +228,10 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } } - def addDependee(epk: SomeEPK, c: () => Unit): Unit = { - val set = dependees.getOrElseUpdate(epk, mutable.Set.empty) + def addDependee(eOptionP: SomeEOptionP, c: () => Unit): Unit = { + // The eOptionP is only inserted the first time the corresponding EPK occurs. Consequently, it is the most + // precise property result that is seen by all dependents. + val (_, set) = dependees.getOrElseUpdate(eOptionP.toEPK, (eOptionP, mutable.Set.empty)) set.add(c) } @@ -239,12 +243,12 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent dependees.size } - def getDependees: collection.Set[SomeEPK] = { - dependees.keySet + def getDependees: collection.Set[SomeEOptionP] = { + dependees.values.map(_._1).toSet } - def getAndRemoveDependeeContinuations(epk: SomeEPK): Set[() => Unit] = { - dependees.remove(epk).getOrElse(Set.empty).toSet + def getAndRemoveDependeeContinuations(eOptionP: SomeEOptionP): Set[() => Unit] = { + dependees.remove(eOptionP.toEPK).map(_._2).getOrElse(Set.empty).toSet } } @@ -322,7 +326,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent state.getDependees.toSet, eps => { // Get and call continuations that are remembered for the EPK - val cs = state.getAndRemoveDependeeContinuations(eps.toEPK) + val cs = state.getAndRemoveDependeeContinuations(eps) cs.foreach(c => c()) // The continuations could have enqueued paths to the path work list processPathWorkList() From 978f0b7ce1439be56670bb850a00ef27a3c7c8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 26 Jul 2024 10:10:56 +0200 Subject: [PATCH 028/167] Remove statement part from IDE properties Currently, there will only be more than one entry/statement if there are multiple return sites. In the future, there will be a possibility to query for a single statement, so the property store can be used to realize this distinction. --- ...ConstantPropagationAnalysisScheduler.scala | 5 ++-- .../LinearConstantPropagationMatcher.scala | 22 +++++++---------- .../integration/IDEAnalysisScheduler.scala | 2 +- .../opalj/ide/integration/IDEProperty.scala | 20 ++++++++-------- .../IDEPropertyMetaInformation.scala | 8 +++---- .../ide/solver/FlowRecordingIDEAnalysis.scala | 2 +- .../org/opalj/ide/solver/IDEAnalysis.scala | 24 ++++++++++--------- ...ntPropagationPropertyMetaInformation.scala | 4 ++-- .../JavaIDEPropertyMetaInformation.scala | 13 ---------- .../analyses/ide/solver/JavaIDEAnalysis.scala | 2 +- 10 files changed, 42 insertions(+), 60 deletions(-) delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala index 90b400b739..254f1e041e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala @@ -2,7 +2,6 @@ package org.opalj.fpcf.ide.linear_constant_propagation import scala.collection.immutable - import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.analyses.SomeProject @@ -10,6 +9,7 @@ import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.properties.cg.Callers import org.opalj.fpcf.PropertyBounds import org.opalj.fpcf.PropertyStore +import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationAnalysis @@ -17,13 +17,12 @@ import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.Lin import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler -import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.solver.JavaIDEAnalysis import org.opalj.tac.fpcf.properties.TACAI object LinearConstantPropagationAnalysisScheduler extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] { - override def property: JavaIDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] = + override def property: IDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] = LinearConstantPropagationPropertyMetaInformation override def requiredProjectInformation: ProjectInformationKeys = diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala index e43a08511c..6a6f380563 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala @@ -31,15 +31,12 @@ class ConstantValueMatcher extends AbstractRepeatablePropertyMatcher { getValue(p, singleAnnotationType, a.elementValuePairs, "value").asIntValue.value if (properties.exists { - case property: BasicIDEProperty[?, ?, ?] => + case property: BasicIDEProperty[?, ?] => property.results.exists { - case (_, results) => - results.exists { - case (LCPProblem.VariableFact(name, _), LCPProblem.ConstantValue(value)) => - expectedVariableName == name && expectedVariableValue == value + case (LCPProblem.VariableFact(name, _), LCPProblem.ConstantValue(value)) => + expectedVariableName == name && expectedVariableValue == value - case _ => false - } + case _ => false } case _ => false @@ -72,15 +69,12 @@ class VariableValueMatcher extends AbstractRepeatablePropertyMatcher { getValue(p, singleAnnotationType, a.elementValuePairs, "variable").asStringValue.value if (properties.exists { - case property: BasicIDEProperty[?, ?, ?] => + case property: BasicIDEProperty[?, ?] => property.results.exists { - case (_, results) => - results.exists { - case (LCPProblem.VariableFact(name, _), LCPProblem.VariableValue) => - expectedVariableName == name + case (LCPProblem.VariableFact(name, _), LCPProblem.VariableValue) => + expectedVariableName == name - case _ => false - } + case _ => false } } ) { diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala index 001cd46dff..400cbd65b3 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala @@ -18,7 +18,7 @@ abstract class IDEAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue, Statemen extends FPCFLazyAnalysisScheduler { override final type InitializationData = IDEAnalysis[Fact, Value, Statement, Callable] - def property: IDEPropertyMetaInformation[Statement, Fact, Value] + def property: IDEPropertyMetaInformation[Fact, Value] override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala index f88d61bf44..26eb0dce34 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala @@ -9,32 +9,32 @@ import org.opalj.ide.problem.IDEValue /** * Base class of properties that are produced by an IDE analysis */ -abstract class IDEProperty[Statement, Fact <: IDEFact, Value <: IDEValue] extends Property - with IDEPropertyMetaInformation[Statement, Fact, Value] +abstract class IDEProperty[Fact <: IDEFact, Value <: IDEValue] extends Property + with IDEPropertyMetaInformation[Fact, Value] /** * Basic implementation of [[IDEProperty]] that simply wraps the fact-value results of an IDE analysis * @param results the results produced by the analysis * @param propertyMetaInformation corresponding to the produced property */ -class BasicIDEProperty[Statement, Fact <: IDEFact, Value <: IDEValue]( - val results: collection.Map[Statement, collection.Set[(Fact, Value)]], - val propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] -) extends IDEProperty[Statement, Fact, Value] { +class BasicIDEProperty[Fact <: IDEFact, Value <: IDEValue]( + val results: collection.Set[(Fact, Value)], + val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] +) extends IDEProperty[Fact, Value] { override type Self = propertyMetaInformation.Self override def key: PropertyKey[Self] = propertyMetaInformation.key override def toString: String = { - s"${PropertyKey.name(propertyMetaInformation.key)}:\n${results.map { case (stmt, result) => - s"\t$stmt:\n\t\t${result.map { case (fact, value) => s"($fact,$value)" }.mkString("\n\t\t")}" - }.mkString("\n")}" + s"${PropertyKey.name(propertyMetaInformation.key)}:\n${ + results.map { case (fact, value) => s"\t($fact,$value)" }.mkString("\n") + }" } override def equals(obj: Any): Boolean = { obj match { - case basicIDEProperty: BasicIDEProperty[?, ?, ?] => + case basicIDEProperty: BasicIDEProperty[?, ?] => results == basicIDEProperty.results && propertyMetaInformation == basicIDEProperty.propertyMetaInformation case _ => false } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala index f52c092f86..420dd325cf 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala @@ -8,12 +8,12 @@ import org.opalj.ide.problem.IDEValue /** * Base interface of property meta information of IDE analyses. Creates [[BasicIDEProperty]] by default. */ -trait IDEPropertyMetaInformation[Statement, Fact <: IDEFact, Value <: IDEValue] extends PropertyMetaInformation { - override type Self = BasicIDEProperty[Statement, Fact, Value] +trait IDEPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue] extends PropertyMetaInformation { + override type Self = BasicIDEProperty[Fact, Value] def createProperty( - results: collection.Map[Statement, collection.Set[(Fact, Value)]] - ): IDEProperty[Statement, Fact, Value] = { + results: collection.Set[(Fact, Value)] + ): IDEProperty[Fact, Value] = { new BasicIDEProperty(results, this) } } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala index 8740d65516..108e16ebfd 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala @@ -32,7 +32,7 @@ import org.opalj.ide.problem.IDEValue class FlowRecordingIDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( project: SomeProject, baseProblem: IDEProblem[Fact, Value, Statement, Callable], - propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value], + propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value], path: Option[Path] = None, recorderMode: FlowRecorderMode = FlowRecorderModes.NODE_AS_STMT_AND_FACT, uniqueFlowsOnly: Boolean = true, diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 952995a69e..1a76ff9960 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -35,7 +35,7 @@ import org.opalj.log.OPALLogger class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( val project: SomeProject, val problem: IDEProblem[Fact, Value, Statement, Callable], - val propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] + val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] ) extends FPCFAnalysis { private type Node = (Statement, Fact) /** @@ -215,17 +215,19 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent values.put(node, newValue) } - def collectResults(callable: Callable): collection.Map[Statement, collection.Set[(Fact, Value)]] = { - values.filter { - case ((n, d), _) => + def collectResults(callable: Callable): collection.Set[(Fact, Value)] = { + values + .filter { case ((n, d), _) => icfg.getCallable(n) == callable && icfg.isNormalExitStatement(n) && d != problem.nullFact - }.groupMapReduce { - case ((n, _), _) => n - } { - case ((_, d), value) => Set((d, value)) - } { - (s1, s2) => s1.union(s2) - } + } + .groupMapReduce { + case ((_, d), _) => d + } { + case (_, value) => value + } { + (value1, value2) => problem.lattice.meet(value1, value2) + } + .toSet } def addDependee(eOptionP: SomeEOptionP, c: () => Unit): Unit = { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala index 946edb1659..9aaf5fcd01 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala @@ -2,14 +2,14 @@ package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation import org.opalj.fpcf.PropertyKey +import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue -import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEPropertyMetaInformation /** * Meta information for linear constant propagation */ object LinearConstantPropagationPropertyMetaInformation - extends JavaIDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] { + extends IDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] { final val key: PropertyKey[Self] = PropertyKey.create("IDELinearConstantPropagation") } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala deleted file mode 100644 index e66da84c9c..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala +++ /dev/null @@ -1,13 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.integration - -import org.opalj.ide.integration.IDEPropertyMetaInformation -import org.opalj.ide.problem.IDEFact -import org.opalj.ide.problem.IDEValue -import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement - -/** - * Specialized IDE property meta information for Java programs - */ -trait JavaIDEPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue] - extends IDEPropertyMetaInformation[JavaStatement, Fact, Value] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala index dd6049bae7..ebdcb1c2a0 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala @@ -15,5 +15,5 @@ import org.opalj.ide.solver.IDEAnalysis class JavaIDEAnalysis[Fact <: IDEFact, Value <: IDEValue]( project: SomeProject, problem: IDEProblem[Fact, Value, JavaStatement, Method], - propertyMetaInformation: IDEPropertyMetaInformation[JavaStatement, Fact, Value] + propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] ) extends IDEAnalysis[Fact, Value, JavaStatement, Method](project, problem, propertyMetaInformation) From 88717cc98572f16eb9360bed8a9cee74ec228360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 26 Jul 2024 11:01:38 +0200 Subject: [PATCH 029/167] Enable analyzing a certain statement inside a callable --- .../org/opalj/ide/solver/IDEAnalysis.scala | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 1a76ff9960..fb63b239d3 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -215,11 +215,17 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent values.put(node, newValue) } - def collectResults(callable: Callable): collection.Set[(Fact, Value)] = { - values - .filter { case ((n, d), _) => - icfg.getCallable(n) == callable && icfg.isNormalExitStatement(n) && d != problem.nullFact - } + def collectResults(callable: Callable, stmtOption: Option[Statement]): collection.Set[(Fact, Value)] = { + val relevantValues = stmtOption match { + case Some(stmt) => + values.filter { case ((n, d), _) => n == stmt && d != problem.nullFact } + case None => + values.filter { case ((n, d), _) => + icfg.getCallable(n) == callable && icfg.isNormalExitStatement(n) && d != problem.nullFact + } + } + + relevantValues .groupMapReduce { case ((_, d), _) => d } { @@ -281,12 +287,18 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } /** - * Run the IDE solver and calculate (and return) the results at the end of the requested callable - * @param callable the callable to analyze + * Run the IDE solver and calculate (and return) the result + * @param entity either only a callable or a pair of callable and statement that should be analyzed (if no statement + * is given, the result for all exit statements is calculated) */ // TODO (IDE) WHAT HAPPENS WHEN ANALYZING MULTIPLE CALLABLES? CAN WE CACHE E.G. JUMP/SUMMARY FUNCTIONS? - def performAnalysis(callable: Callable): ProperPropertyComputationResult = { - logDebug(s"performing ${getClass.getSimpleName} for $callable") + def performAnalysis(entity: Entity): ProperPropertyComputationResult = { + logDebug(s"performing ${getClass.getSimpleName} for $entity") + + val (callable, stmt) = entity match { + case (c: Entity, s: Entity) => (c.asInstanceOf[Callable], Some(s.asInstanceOf[Statement])) + case c => (c.asInstanceOf[Callable], None) + } implicit val state: State = new State @@ -310,8 +322,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logDebug("collecting results for property creation") // Build and return result - val property = propertyMetaInformation.createProperty(state.collectResults(callable)) - Result(callable, property) + val property = propertyMetaInformation.createProperty(state.collectResults(callable, stmt)) + Result(entity, property) } if (!state.areDependeesEmpty) { @@ -319,12 +331,12 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent def createInterimResult(): ProperPropertyComputationResult = { InterimResult.forUB( - callable, + entity, // TODO (IDE) THIS WILL BE AN 'EMPTY' PROPERTY -> PROBLEMATIC WITH CYCLIC IDE ANALYSES // - WHAT IF WE RUN PHASE 2 BEFORE RETURNING? // - DOES THIS PRODUCE VALID RESULTS (I.E. ALWAYS UPPER BOUND)? // - DO WE NEED TO RERUN PHASE 2 FROM SCRATCH EACH TIME? - propertyMetaInformation.createProperty(state.collectResults(callable)), + propertyMetaInformation.createProperty(state.collectResults(callable, stmt)), state.getDependees.toSet, eps => { // Get and call continuations that are remembered for the EPK From 3c1c6f1e937533599237a028a113897aa7725090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 26 Jul 2024 13:08:12 +0200 Subject: [PATCH 030/167] Make cyclic analyses produce more precise results (and remove lambdas/inner functions) --- .../org/opalj/ide/solver/IDEAnalysis.scala | 140 ++++++++++++------ 1 file changed, 93 insertions(+), 47 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index fb63b239d3..52e2f31e15 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -13,6 +13,7 @@ import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEOptionP import org.opalj.fpcf.SomeEPK +import org.opalj.fpcf.SomeEPS import org.opalj.ide.ConfigKeyDebugLog import org.opalj.ide.ConfigKeyTraceLog import org.opalj.ide.FrameworkName @@ -215,10 +216,14 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent values.put(node, newValue) } - def collectResults(callable: Callable, stmtOption: Option[Statement]): collection.Set[(Fact, Value)] = { - val relevantValues = stmtOption match { - case Some(stmt) => - values.filter { case ((n, d), _) => n == stmt && d != problem.nullFact } + def clearValues(): Unit = { + values.clear() + } + + def collectResults(callable: Callable, stmt: Option[Statement]): collection.Set[(Fact, Value)] = { + val relevantValues = stmt match { + case Some(statement) => + values.filter { case ((n, d), _) => n == statement && d != problem.nullFact } case None => values.filter { case ((n, d), _) => icfg.getCallable(n) == callable && icfg.isNormalExitStatement(n) && d != problem.nullFact @@ -302,65 +307,106 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent implicit val state: State = new State + performPhase1(callable) + performPhase2(callable) + + createResult(entity, callable, stmt) + } + + /** + * @return whether the phase is finished or has to be continued once the dependees are resolved + */ + private def performPhase1(callable: Callable)(implicit s: State): Boolean = { logDebug("starting phase 1") - // Phase 1 seedPhase1(callable) processPathWorkList() - def c(): ProperPropertyComputationResult = { + if (s.areDependeesEmpty) { logDebug("finished phase 1") + true + } else { + logDebug(s"there are ${s.getDependeesSize} outstanding dependees") + logDebug("pausing phase 1") + false + } + } - logDebug("starting phase 2") + /** + * @return whether the phase is finished or has to be continued once the dependees are resolved + */ + private def continuePhase1()(implicit s: State): Boolean = { + logDebug("continuing phase 1") - // Phase 2 - seedPhase2(callable) - computeValues() + processPathWorkList() - logDebug("finished phase 2") + if (s.areDependeesEmpty) { + logDebug("all outstanding dependees have been processed") + logDebug("finished phase 1") + true + } else { + logDebug(s"there are ${s.getDependeesSize} outstanding dependees left") + logDebug("pausing phase 1 again") + false + } + } - logDebug("collecting results for property creation") + /** + * Perform phase 2 from scratch + */ + private def performPhase2(callable: Callable)(implicit s: State): Unit = { + logDebug("starting phase 2") - // Build and return result - val property = propertyMetaInformation.createProperty(state.collectResults(callable, stmt)) - Result(entity, property) - } + // TODO (IDE) PHASE 2 IS PERFORMED ALSO FOR INTERIM RESULTS TO MAKE CYCLIC ANALYSES POSSIBLE + // - PHASE 2 IS PERFORMED FROM SCRATCH ON EACH UPDATE AT THE MOMENT (THIS SHOULD ALWAYS PROCUDE AN UPPER BOUND + // OF THE FINAL VALUE) + // - DO WE NEED TO RERUN IT FROM SCRATCH EACH TIME OR IS THERE AN INCREMENTAL SOLUTION? + s.clearValues() - if (!state.areDependeesEmpty) { - logDebug(s"there are ${state.getDependeesSize} outstanding dependees") - - def createInterimResult(): ProperPropertyComputationResult = { - InterimResult.forUB( - entity, - // TODO (IDE) THIS WILL BE AN 'EMPTY' PROPERTY -> PROBLEMATIC WITH CYCLIC IDE ANALYSES - // - WHAT IF WE RUN PHASE 2 BEFORE RETURNING? - // - DOES THIS PRODUCE VALID RESULTS (I.E. ALWAYS UPPER BOUND)? - // - DO WE NEED TO RERUN PHASE 2 FROM SCRATCH EACH TIME? - propertyMetaInformation.createProperty(state.collectResults(callable, stmt)), - state.getDependees.toSet, - eps => { - // Get and call continuations that are remembered for the EPK - val cs = state.getAndRemoveDependeeContinuations(eps) - cs.foreach(c => c()) - // The continuations could have enqueued paths to the path work list - processPathWorkList() - - if (state.areDependeesEmpty) { - logDebug(s"all outstanding dependees have been processed") - c() - } else { - logDebug(s"there are ${state.getDependeesSize} outstanding dependees left") - createInterimResult() - } - } - ) - } - createInterimResult() + seedPhase2(callable) + computeValues() + + logDebug("finished phase 2") + } + + // TODO (IDE) REMOVE/SIMPLIFY PARAMETERS WHEN USING MultiResult/Results + private def createResult(entity: Entity, callable: Callable, stmt: Option[Statement])( + implicit s: State + ): ProperPropertyComputationResult = { + logDebug("starting creation of properties") + + val property = propertyMetaInformation.createProperty(s.collectResults(callable, stmt)) + + logDebug("finished creation of properties") + + if (s.areDependeesEmpty) { + Result(entity, property) } else { - c() + InterimResult.forUB( + entity, + property, + s.getDependees.toSet, + onDependeeUpdateContinuation(entity, callable, stmt) + ) } } + private def onDependeeUpdateContinuation( + entity: Entity, + callable: Callable, + stmt: Option[Statement] + )(eps: SomeEPS)(implicit s: State): ProperPropertyComputationResult = { + // Call and remove all continuations that are remembered for the EPS + val cs = s.getAndRemoveDependeeContinuations(eps) + cs.foreach(c => c()) + + // The continuations can have enqueued paths to the path work list + continuePhase1() + performPhase2(callable) + + createResult(entity, callable, stmt) + } + private def seedPhase1(callable: Callable)(implicit s: State): Unit = { s.rememberCallable(callable) From 8be9534cd0f49620603b11b33f4e983004531327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 31 Jul 2024 15:17:17 +0200 Subject: [PATCH 031/167] Generate properties for all computed results Required to refactor handling of analyzing a certain statement directly. --- .../LinearConstantPropagationTests.scala | 1 + .../integration/IDEAnalysisScheduler.scala | 14 +- .../org/opalj/ide/solver/IDEAnalysis.scala | 120 ++++++++++++------ .../opalj/ide/solver/IDEAnalysisProxy.scala | 90 +++++++++++++ 4 files changed, 183 insertions(+), 42 deletions(-) create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala index a911a4025d..5ba0f70378 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala @@ -21,6 +21,7 @@ class LinearConstantPropagationTests extends IDEPropertiesTest { .foreach { method => testContext.propertyStore.force(method, analysis.propertyMetaInformation.key) } } + testContext.propertyStore.waitOnPhaseCompletion() testContext.propertyStore.shutdown() validateProperties( diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala index 400cbd65b3..cca571b045 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala @@ -6,10 +6,12 @@ import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler import org.opalj.fpcf.Entity import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyStore import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEValue import org.opalj.ide.solver.IDEAnalysis +import org.opalj.ide.solver.IDEAnalysisProxy /** * A base scheduler for IDE analyses adding common default behavior @@ -24,12 +26,22 @@ abstract class IDEAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue, Statemen override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + private lazy val backingPropertyKey: PropertyKey[IDEPropertyMetaInformation[Fact, Value]#Self] = { + PropertyKey.create(s"${PropertyKey.name(property.key)}_Backing") + } + override def register( project: SomeProject, propertyStore: PropertyStore, analysis: IDEAnalysis[Fact, Value, Statement, Callable] ): FPCFAnalysis = { - propertyStore.registerLazyPropertyComputation(property.key, analysis.performAnalysis) + propertyStore.registerLazyPropertyComputation( + property.key, + new IDEAnalysisProxy[Fact, Value, Statement, Callable](project, property, backingPropertyKey).proxyAnalysis + ) + + propertyStore.registerLazyPropertyComputation(backingPropertyKey, analysis.performAnalysis) + analysis } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 52e2f31e15..af3234ccf4 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -3,14 +3,17 @@ package org.opalj.ide.solver import scala.annotation.unused +import scala.collection.immutable import scala.collection.mutable import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.fpcf.Entity +import org.opalj.fpcf.InterimPartialResult import org.opalj.fpcf.InterimResult import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.Result +import org.opalj.fpcf.Results import org.opalj.fpcf.SomeEOptionP import org.opalj.fpcf.SomeEPK import org.opalj.fpcf.SomeEPS @@ -220,25 +223,38 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent values.clear() } - def collectResults(callable: Callable, stmt: Option[Statement]): collection.Set[(Fact, Value)] = { - val relevantValues = stmt match { - case Some(statement) => - values.filter { case ((n, d), _) => n == statement && d != problem.nullFact } - case None => - values.filter { case ((n, d), _) => - icfg.getCallable(n) == callable && icfg.isNormalExitStatement(n) && d != problem.nullFact - } - } + /** + * @return a map from statements to results and the results for the callable in total + */ + def collectResults(callable: Callable): ( + collection.Map[Statement, collection.Set[(Fact, Value)]], + collection.Set[(Fact, Value)] + ) = { + val relevantValues = values + .filter { case ((n, d), _) => + icfg.getCallable(n) == callable && d != problem.nullFact + } - relevantValues - .groupMapReduce { - case ((_, d), _) => d - } { - case (_, value) => value - } { + val resultsByStatement = relevantValues + .groupMap(_._1._1) { case ((_, d), value) => (d, value) } + .map { case (n, dValuePairs) => + ( + n, + dValuePairs.groupMapReduce(_._1)(_._2) { (value1, value2) => + problem.lattice.meet(value1, value2) + }.toSet + ) + } + + val resultsForExit = resultsByStatement + .filter { case (n, _) => icfg.isNormalExitStatement(n) } + .flatMap(_._2.toList) + .groupMapReduce(_._1)(_._2) { (value1, value2) => problem.lattice.meet(value1, value2) } .toSet + + (resultsByStatement, resultsForExit) } def addDependee(eOptionP: SomeEOptionP, c: () => Unit): Unit = { @@ -293,24 +309,20 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent /** * Run the IDE solver and calculate (and return) the result - * @param entity either only a callable or a pair of callable and statement that should be analyzed (if no statement - * is given, the result for all exit statements is calculated) + * @param callable the callable that should be analyzed + * @return a result for each statement of the callable plus a result for the callable itself (combining the results + * of all exit statements) */ // TODO (IDE) WHAT HAPPENS WHEN ANALYZING MULTIPLE CALLABLES? CAN WE CACHE E.G. JUMP/SUMMARY FUNCTIONS? - def performAnalysis(entity: Entity): ProperPropertyComputationResult = { - logDebug(s"performing ${getClass.getSimpleName} for $entity") - - val (callable, stmt) = entity match { - case (c: Entity, s: Entity) => (c.asInstanceOf[Callable], Some(s.asInstanceOf[Statement])) - case c => (c.asInstanceOf[Callable], None) - } + def performAnalysis(callable: Callable): ProperPropertyComputationResult = { + logDebug(s"performing ${getClass.getSimpleName} for $callable") implicit val state: State = new State performPhase1(callable) performPhase2(callable) - createResult(entity, callable, stmt) + createResult(callable) } /** @@ -369,42 +381,68 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logDebug("finished phase 2") } - // TODO (IDE) REMOVE/SIMPLIFY PARAMETERS WHEN USING MultiResult/Results - private def createResult(entity: Entity, callable: Callable, stmt: Option[Statement])( + private def createResult(callable: Callable)( implicit s: State ): ProperPropertyComputationResult = { logDebug("starting creation of properties") - val property = propertyMetaInformation.createProperty(s.collectResults(callable, stmt)) + val (resultsByStatement, resultsForExit) = s.collectResults(callable) + val propertiesByStatement = resultsByStatement.map { case (stmt, results) => + (stmt, propertyMetaInformation.createProperty(results)) + } + val propertyForExit = propertyMetaInformation.createProperty(resultsForExit) logDebug("finished creation of properties") if (s.areDependeesEmpty) { - Result(entity, property) + logDebug("creating final results") + Results( + Iterable( + Result(callable, propertyForExit) + ) ++ propertiesByStatement.map { case (stmt, property) => + Result((callable, stmt), property) + } + ) } else { - InterimResult.forUB( - entity, - property, - s.getDependees.toSet, - onDependeeUpdateContinuation(entity, callable, stmt) + logDebug("creating interim results") + Results( + Iterable( + InterimPartialResult( + s.getDependees.toSet, + onDependeeUpdateContinuation(callable) + ), + InterimResult.forUB( + callable, + propertyForExit, + Set.empty, + _ => { throw new IllegalStateException() } + ) + ) ++ propertiesByStatement.map { case (stmt, property) => + InterimResult.forUB( + (callable, stmt), + property, + Set.empty, + _ => { throw new IllegalStateException() } + ) + } ) } } - private def onDependeeUpdateContinuation( - entity: Entity, - callable: Callable, - stmt: Option[Statement] - )(eps: SomeEPS)(implicit s: State): ProperPropertyComputationResult = { - // Call and remove all continuations that are remembered for the EPS + private def onDependeeUpdateContinuation(callable: Callable)(eps: SomeEPS)( + implicit s: State + ): ProperPropertyComputationResult = { + // Get and remove all continuations that are remembered for the EPS val cs = s.getAndRemoveDependeeContinuations(eps) + + // Call continuations cs.foreach(c => c()) // The continuations can have enqueued paths to the path work list continuePhase1() performPhase2(callable) - createResult(entity, callable, stmt) + createResult(callable) } private def seedPhase1(callable: Callable)(implicit s: State): Unit = { diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala new file mode 100644 index 0000000000..88937ed7e4 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala @@ -0,0 +1,90 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.solver + +import scala.collection.immutable + +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.fpcf.Entity +import org.opalj.fpcf.EPK +import org.opalj.fpcf.InterimPartialResult +import org.opalj.fpcf.InterimResult +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.Result +import org.opalj.fpcf.SomeEPS +import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEValue + +/** + * A proxy for IDE analyses that accepts analysis requests for callables as well as statement-callable combinations. + * The [[IDEAnalysis]] solver runs on callables only and additionally produces results for each statement of that + * callable. This proxy analysis reduces all analysis requests to the callable and then forward it to the actual IDE + * solver. + * @param backingPropertyKey the property key used for the backing analysis + */ +private[ide] class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( + val project: SomeProject, + val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value], + val backingPropertyKey: PropertyKey[IDEPropertyMetaInformation[Fact, Value]#Self] +) extends FPCFAnalysis { + /** + * @param entity either only a callable or a pair of callable and statement that should be analyzed (if no statement + * is given, the result for all exit statements is calculated) + */ + def proxyAnalysis(entity: Entity): ProperPropertyComputationResult = { + println(s"Proxying request to ${PropertyKey.name(propertyMetaInformation.key)} for $entity") + + val (callable, stmt) = entity match { + case (c: Entity, s: Entity) => (c.asInstanceOf[Callable], Some(s.asInstanceOf[Statement])) + case c => (c.asInstanceOf[Callable], None) + } + + createCoarseResult(callable, stmt) + } + + private def createCoarseResult(callable: Callable, stmt: Option[Statement]): ProperPropertyComputationResult = { + val eOptionP = propertyStore(callable, backingPropertyKey) + eOptionP match { + case _: EPK[Callable, ?] => + // In this case, the analysis has not been called yet + InterimPartialResult( + immutable.Set(eOptionP), + onDependeeUpdateContinuationCoarse(callable, stmt) + ) + case _ => + // In this case, some kind of result is present (for the callable, as well as for each statement) + createFineResult(callable, stmt) + } + } + + private def onDependeeUpdateContinuationCoarse( + callable: Callable, + stmt: Option[Statement] + )(eps: SomeEPS): ProperPropertyComputationResult = { + createCoarseResult(callable, stmt) + } + + private def createFineResult(callable: Callable, stmt: Option[Statement]): ProperPropertyComputationResult = { + val eOptionP = propertyStore((callable, stmt.get), backingPropertyKey) + if (eOptionP.isFinal) { + Result(eOptionP.asFinal) + } else if (eOptionP.hasUBP) { + InterimResult( + eOptionP.asInterim, + immutable.Set(eOptionP), + onDependeeUpdateContinuationFine(callable, stmt) + ) + } else { + throw new IllegalStateException(s"Expected a final or interim EPS but got $eOptionP!") + } + } + + private def onDependeeUpdateContinuationFine( + callable: Callable, + stmt: Option[Statement] + )(eps: SomeEPS): ProperPropertyComputationResult = { + createFineResult(callable, stmt) + } +} From e479a5a87e7db0d7df915c23802b6dd1c862a639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 31 Jul 2024 15:26:43 +0200 Subject: [PATCH 032/167] Some type fixes --- .../org/opalj/fpcf/ide/IDEPropertiesTest.scala | 2 +- .../scala/org/opalj/ide/problem/EdgeFunction.scala | 6 +++--- .../org/opalj/ide/problem/EdgeFunctionResult.scala | 2 +- .../src/main/scala/org/opalj/ide/solver/ICFG.scala | 6 +++--- .../scala/org/opalj/ide/solver/IDEAnalysis.scala | 14 +++++++------- .../LinearConstantPropagationEdgeFunctions.scala | 2 +- .../tac/fpcf/analyses/ide/solver/JavaICFG.scala | 12 +++++++----- 7 files changed, 23 insertions(+), 21 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala index ea96dcc5e0..c758553a24 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala @@ -35,7 +35,7 @@ class IDEPropertiesTest extends PropertiesTest { p.get(RTACallGraphKey) } - def getEntryPointsByICFG(icfg: JavaICFG, project: Project[URL]): Set[Method] = { + def getEntryPointsByICFG(icfg: JavaICFG, project: Project[URL]): collection.Set[Method] = { icfg.getCallablesCallableFromOutside .filter { method => val packageOfEntryPoint = method.classFile.thisType.packageName diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala index 03235aaecc..5370969165 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala @@ -51,7 +51,7 @@ case class IdentityEdgeFunction[Value <: IDEValue]() extends EdgeFunction[Value] } override def equalTo(otherEdgeFunction: EdgeFunction[Value]): Boolean = - otherEdgeFunction == this || otherEdgeFunction.isInstanceOf[IdentityEdgeFunction[Value]] + (otherEdgeFunction eq this) || otherEdgeFunction.isInstanceOf[IdentityEdgeFunction[Value]] } /** @@ -66,7 +66,7 @@ abstract case class AllTopEdgeFunction[Value <: IDEValue](private val top: Value otherEdgeFunction override def equalTo(otherEdgeFunction: EdgeFunction[Value]): Boolean = - otherEdgeFunction == this || + (otherEdgeFunction eq this) || otherEdgeFunction.isInstanceOf[AllTopEdgeFunction[Value]] && otherEdgeFunction.asInstanceOf[AllTopEdgeFunction[Value]].top == top } @@ -83,7 +83,7 @@ abstract case class AllBottomEdgeFunction[Value <: IDEValue](private val bottom: this override def equalTo(otherEdgeFunction: EdgeFunction[Value]): Boolean = - otherEdgeFunction == this || + (otherEdgeFunction eq this) || otherEdgeFunction.isInstanceOf[AllBottomEdgeFunction[Value]] && otherEdgeFunction.asInstanceOf[AllBottomEdgeFunction[Value]].bottom == bottom } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala index 083d1a0b88..927082f989 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala @@ -20,5 +20,5 @@ case class FinalEdgeFunction[Value <: IDEValue](edgeFunction: EdgeFunction[Value */ case class InterimEdgeFunction[Value <: IDEValue]( interimEdgeFunction: EdgeFunction[Value], - dependees: Set[SomeEOptionP] + dependees: collection.Set[SomeEOptionP] ) extends EdgeFunctionResult[Value] diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala index 790cc45cef..3dd76a88cc 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala @@ -10,12 +10,12 @@ trait ICFG[Statement, Callable <: Entity] { /** * Get all statements a callable can be entered at */ - def getStartStatements(callable: Callable): Set[Statement] + def getStartStatements(callable: Callable): collection.Set[Statement] /** * Get all statements that can directly follow the given one */ - def getNextStatements(stmt: Statement): Set[Statement] + def getNextStatements(stmt: Statement): collection.Set[Statement] /** * Check whether a statement exits a callable in a normal way (e.g. with a return) @@ -35,7 +35,7 @@ trait ICFG[Statement, Callable <: Entity] { /** * Get all possible callees a call statement could call */ - def getCalleesIfCallStatement(stmt: Statement): Option[Set[? <: Callable]] + def getCalleesIfCallStatement(stmt: Statement): Option[collection.Set[? <: Callable]] /** * Get the callable a statement belongs to diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index af3234ccf4..24e71f1387 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -173,7 +173,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } def getEndSummaries(start: Node): collection.Set[(Node, JumpFunction)] = { - endSummaries.getOrElse(start, Set.empty) + endSummaries.getOrElse(start, immutable.Set.empty) } def rememberCallable(callable: Callable): Unit = { @@ -192,7 +192,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } def lookupCallSourcesForTarget(target: Statement, targetFact: Fact): collection.Set[Node] = { - callTargetsToSources.getOrElse((target, targetFact), Set.empty) + callTargetsToSources.getOrElse((target, targetFact), immutable.Set.empty) } def enqueueNode(node: Node): Unit = { @@ -276,8 +276,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent dependees.values.map(_._1).toSet } - def getAndRemoveDependeeContinuations(eOptionP: SomeEOptionP): Set[() => Unit] = { - dependees.remove(eOptionP.toEPK).map(_._2).getOrElse(Set.empty).toSet + def getAndRemoveDependeeContinuations(eOptionP: SomeEOptionP): collection.Set[() => Unit] = { + dependees.remove(eOptionP.toEPK).map(_._2).getOrElse(immutable.Set.empty).toSet } } @@ -414,14 +414,14 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent InterimResult.forUB( callable, propertyForExit, - Set.empty, + immutable.Set.empty, _ => { throw new IllegalStateException() } ) ) ++ propertiesByStatement.map { case (stmt, property) => InterimResult.forUB( (callable, stmt), property, - Set.empty, + immutable.Set.empty, _ => { throw new IllegalStateException() } ) } @@ -771,7 +771,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val (n, d) = node // IDE P2 line 8 - val cs = collectReachableStmts(collection.Set(n), stmt => icfg.isCallStatement(stmt)) + val cs = collectReachableStmts(immutable.Set(n), stmt => icfg.isCallStatement(stmt)) // IDE P2 lines 9 - 10 cs.foreach { c => diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala index 9189d07486..36e32aca87 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala @@ -82,7 +82,7 @@ case class LinearCombinationEdgeFunction( } override def equalTo(otherEdgeFunction: EdgeFunction[LinearConstantPropagationValue]): Boolean = { - otherEdgeFunction == this || + (otherEdgeFunction eq this) || (otherEdgeFunction match { case LinearCombinationEdgeFunction(a2, b2, c2) => a == a2 && b == b2 && c == c2 case _ => false diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala index ad301cd72d..9c9cd25b81 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala @@ -1,6 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ide.solver +import scala.collection.immutable + import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.ide.solver.ICFG @@ -13,15 +15,15 @@ class JavaICFG(project: SomeProject) extends ICFG[JavaStatement, Method] { // TODO (IDE) CURRENTLY DEPENDS ON IMPLEMENTATION FROM IFDS private val baseICFG = new JavaForwardICFG(project) - override def getStartStatements(callable: Method): Set[JavaStatement] = + override def getStartStatements(callable: Method): collection.Set[JavaStatement] = baseICFG.startStatements(callable).map { case org.opalj.tac.fpcf.analyses.ifds.JavaStatement(method, index, code, cfg) => JavaStatement(method, index, isReturnNode = false, code, cfg) } - override def getNextStatements(stmt: JavaStatement): Set[JavaStatement] = { + override def getNextStatements(stmt: JavaStatement): collection.Set[JavaStatement] = { if (isCallStatement(stmt)) { - Set(JavaStatement(stmt.method, stmt.index, isReturnNode = true, stmt.code, stmt.cfg)) + immutable.Set(JavaStatement(stmt.method, stmt.index, isReturnNode = true, stmt.code, stmt.cfg)) } else { baseICFG.nextStatements( org.opalj.tac.fpcf.analyses.ifds.JavaStatement(stmt.method, stmt.index, stmt.code, stmt.cfg) @@ -47,7 +49,7 @@ class JavaICFG(project: SomeProject) extends ICFG[JavaStatement, Method] { } // TODO (IDE) REFACTOR AS 'getCallees(...): Set[Method]' - override def getCalleesIfCallStatement(stmt: JavaStatement): Option[Set[Method]] = { + override def getCalleesIfCallStatement(stmt: JavaStatement): Option[collection.Set[Method]] = { if (stmt.isReturnNode) { None } else { @@ -70,7 +72,7 @@ class JavaICFG(project: SomeProject) extends ICFG[JavaStatement, Method] { override def getCallable(stmt: JavaStatement): Method = stmt.method - def getCallablesCallableFromOutside: Set[Method] = { + def getCallablesCallableFromOutside: collection.Set[Method] = { baseICFG.methodsCallableFromOutside.map { declaredMethod => declaredMethod.asDefinedMethod.definedMethod } } From 8ba62dc30a4f89265cab0054aba1de392ae1a821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 31 Jul 2024 17:29:10 +0200 Subject: [PATCH 033/167] Fix some typos --- .../opalj/ide/problem/FlowRecordingIDEProblem.scala | 13 +++++++------ .../scala/org/opalj/ide/solver/IDEAnalysis.scala | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index 582131b2ef..54b92447f4 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -38,7 +38,7 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal /** * Wrapper class for flow functions doing the actual recording */ - private class RecodingFlowFunction( + private class RecordingFlowFunction( baseFlowFunction: FlowFunction[Fact], val source: Statement, val target: Statement, @@ -66,7 +66,7 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal override def getNormalFlowFunction(source: Statement, target: Statement)( implicit propertyStore: PropertyStore ): FlowFunction[Fact] = { - new RecodingFlowFunction(baseProblem.getNormalFlowFunction(source, target), source, target, "normal flow") + new RecordingFlowFunction(baseProblem.getNormalFlowFunction(source, target), source, target, "normal flow") } override def getCallFlowFunction( @@ -74,7 +74,7 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal calleeEntry: Statement, callee: Callable )(implicit propertyStore: PropertyStore): FlowFunction[Fact] = { - new RecodingFlowFunction( + new RecordingFlowFunction( baseProblem.getCallFlowFunction(callSite, calleeEntry, callee), callSite, calleeEntry, @@ -87,7 +87,7 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal callee: Callable, returnSite: Statement )(implicit propertyStore: PropertyStore): FlowFunction[Fact] = { - new RecodingFlowFunction( + new RecordingFlowFunction( baseProblem.getReturnFlowFunction(calleeExit, callee, returnSite), calleeExit, returnSite, @@ -98,7 +98,7 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal override def getCallToReturnFlowFunction(callSite: Statement, callee: Callable, returnSite: Statement)( implicit propertyStore: PropertyStore ): FlowFunction[Fact] = { - new RecodingFlowFunction( + new RecordingFlowFunction( baseProblem.getCallToReturnFlowFunction(callSite, callee, returnSite), callSite, returnSite, @@ -127,7 +127,8 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal calleeEntryFact: Fact, callee: Callable )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] = { - val edgeFunctionResult = baseProblem.getCallEdgeFunction(callSite, callSiteFact, calleeEntry, calleeEntryFact, callee) + val edgeFunctionResult = + baseProblem.getCallEdgeFunction(callSite, callSiteFact, calleeEntry, calleeEntryFact, callee) collectedEdgeFunctions.put( createDotEdge(callSite, callSiteFact, calleeEntry, calleeEntryFact, "call flow"), getEdgeFunctionFromEdgeFunctionResult(edgeFunctionResult) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 24e71f1387..753d8bd9c2 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -338,7 +338,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logDebug("finished phase 1") true } else { - logDebug(s"there are ${s.getDependeesSize} outstanding dependees") + logDebug(s"there is/are ${s.getDependeesSize} outstanding dependee(s)") logDebug("pausing phase 1") false } @@ -357,7 +357,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logDebug("finished phase 1") true } else { - logDebug(s"there are ${s.getDependeesSize} outstanding dependees left") + logDebug(s"there is/are ${s.getDependeesSize} outstanding dependee(s) left") logDebug("pausing phase 1 again") false } From 9e2829447fa70cbc4e38aecfd598fe41356cf44d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 31 Jul 2024 17:41:41 +0200 Subject: [PATCH 034/167] Add concept of additional seeds --- .../PropagationAcrossMethodsExample.java | 2 ++ .../ide/problem/FlowRecordingIDEProblem.scala | 6 ++++++ .../scala/org/opalj/ide/problem/IDEProblem.scala | 14 ++++++++++++++ .../scala/org/opalj/ide/solver/IDEAnalysis.scala | 6 ++++++ .../problem/LinearConstantPropagationProblem.scala | 10 ++++++++++ 5 files changed, 38 insertions(+) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java index e8ef1d907b..a98c07c245 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java @@ -7,6 +7,8 @@ import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; public class PropagationAcrossMethodsExample { + @VariableValue(variable = "param2") + @VariableValue(variable = "param3") public int linearCalculation1(String msg, int a, int b) { System.out.println(msg + ": " + a); return 42 - 5 * b; diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index 54b92447f4..e3147922aa 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -63,6 +63,12 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal override val lattice: MeetLattice[Value] = baseProblem.lattice + override def getAdditionalSeeds(stmt: Statement, callee: Callable)( + implicit propertyStore: PropertyStore + ): collection.Set[Fact] = { + baseProblem.getAdditionalSeeds(stmt, callee) + } + override def getNormalFlowFunction(source: Statement, target: Statement)( implicit propertyStore: PropertyStore ): FlowFunction[Fact] = { diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index 8cf8d4ef2b..7f4cd77881 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -1,8 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ide.problem +import scala.annotation.unused import scala.language.implicitConversions +import scala.collection.immutable + import org.opalj.fpcf.Entity import org.opalj.fpcf.PropertyStore import org.opalj.ide.solver.ICFG @@ -27,6 +30,17 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl */ val lattice: MeetLattice[Value] + /** + * Add additional facts that the analysis should be seeded with. Traditionally, IDE starts with the null fact at the + * start statements of the callable. E.g. additional seeds can be used for adding facts about the parameters of the + * analyzed callable. + * @param stmt the start statement + * @param callee the analyzed callable + */ + def getAdditionalSeeds(stmt: Statement, callee: Callable)( + implicit @unused propertyStore: PropertyStore + ): collection.Set[Fact] = immutable.Set.empty + /** * Generate a flow function for a normal flow * @param source where the normal flow starts diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 753d8bd9c2..ad6c030a74 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -453,6 +453,12 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val path = ((stmt, problem.nullFact), (stmt, problem.nullFact)) s.enqueuePath(path) s.setJumpFunction(path, identityEdgeFunction) + + problem.getAdditionalSeeds(stmt, callable).foreach { fact => + val path = ((stmt, problem.nullFact), (stmt, fact)) + s.enqueuePath(path) + s.setJumpFunction(path, identityEdgeFunction) + } } logDebug(s"seeded with ${s.getPathWorkListSize} path(s)") diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index abdb163b50..d43dd3977a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -41,6 +41,16 @@ class LinearConstantPropagationProblem(project: SomeProject) override val lattice: MeetLattice[LinearConstantPropagationValue] = LinearConstantPropagationLattice + override def getAdditionalSeeds(stmt: JavaStatement, callee: Method)( + implicit propertyStore: PropertyStore + ): collection.Set[LinearConstantPropagationFact] = { + callee.parameterTypes + .zipWithIndex + .filter { case (paramType, _) => paramType.isIntegerType } + .map { case (_, index) => VariableFact(s"param${index + 1}", -(index + 2)) } + .toSet + } + override def getNormalFlowFunction( source: JavaStatement, target: JavaStatement From 2e87f98f1a17524e5f1e92733331687bdc2a82f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 31 Jul 2024 17:42:52 +0200 Subject: [PATCH 035/167] Add problem-global identity edge function --- .../src/main/scala/org/opalj/ide/problem/IDEProblem.scala | 2 ++ .../problem/LinearConstantPropagationProblem.scala | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index 7f4cd77881..905a6f5216 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -20,6 +20,8 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl FinalEdgeFunction(edgeFunction) } + protected val identityEdgeFunction = new IdentityEdgeFunction[Value] + /** * The null fact to use. Also used to bootstrap the analysis at the entry points. */ diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index d43dd3977a..d16431feb9 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -11,7 +11,6 @@ import org.opalj.fpcf.PropertyStore import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.FlowFunction -import org.opalj.ide.problem.IdentityEdgeFunction import org.opalj.ide.problem.MeetLattice import org.opalj.tac.ArrayLength import org.opalj.tac.Assignment @@ -33,8 +32,6 @@ class LinearConstantPropagationProblem(project: SomeProject) extends JavaIDEProblem[LinearConstantPropagationFact, LinearConstantPropagationValue]( new JavaICFG(project) ) { - private val identityEdgeFunction = new IdentityEdgeFunction[LinearConstantPropagationValue] - override val nullFact: LinearConstantPropagationFact = NullFact @@ -158,7 +155,7 @@ class LinearConstantPropagationProblem(project: SomeProject) /* Parameters and their types (excluding the implicit 'this' reference) */ val params = callStmt.params - val paramTypes = callee.descriptor.parameterTypes + val paramTypes = callee.parameterTypes params .zipWithIndex From 26c05f227f65241043477f4a3903b132df1eca08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 2 Aug 2024 08:52:01 +0200 Subject: [PATCH 036/167] Add predefined flow functions --- .../org/opalj/ide/problem/FlowFunction.scala | 18 ++++++++++++++++++ .../org/opalj/ide/problem/IDEProblem.scala | 12 ++++++++++++ 2 files changed, 30 insertions(+) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala index 49dd8ac7f4..8a13f01e97 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala @@ -1,6 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ide.problem +import scala.collection.immutable + /** * Interface representing IDE flow functions */ @@ -11,3 +13,19 @@ trait FlowFunction[Fact <: IDEFact] { */ def compute(sourceFact: Fact): collection.Set[Fact] } + +/** + * Special flow function that always returns the input fact + */ +case class IdentityFlowFunction[Fact <: IDEFact]() extends FlowFunction[Fact] { + override def compute(sourceFact: Fact): collection.Set[Fact] = + immutable.Set(sourceFact) +} + +/** + * Special flow function that always returns an empty set + */ +case class EmptyFlowFunction[Fact <: IDEFact]() extends FlowFunction[Fact] { + override def compute(sourceFact: Fact): collection.Set[Fact] = + immutable.Set.empty +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index 905a6f5216..4ef978f8f7 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -20,6 +20,18 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl FinalEdgeFunction(edgeFunction) } + /** + * Identity flow function that can be used when implementing problems + */ + protected val identityFlowFunction = new IdentityFlowFunction[Fact] + /** + * Empty flow function that can be used when implementing problems + */ + protected val emptyFlowFunction = new EmptyFlowFunction[Fact] + + /** + * Identity edge function that can be used when implementing problems + */ protected val identityEdgeFunction = new IdentityEdgeFunction[Value] /** From 00b598f4b6d1cf07210d9fc1132920c0831a9816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 2 Aug 2024 11:41:42 +0200 Subject: [PATCH 037/167] Prepare linear constant propagation for 'on fields' extension --- .../LinearConstantPropagationProblem.scala | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index d16431feb9..1f17bac485 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -1,6 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem +import scala.annotation.unused + import scala.collection.immutable import org.opalj.BinaryArithmeticOperators @@ -8,7 +10,6 @@ import org.opalj.ai.domain.l1.DefaultIntegerRangeValues import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.PropertyStore -import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.FlowFunction import org.opalj.ide.problem.MeetLattice @@ -70,7 +71,7 @@ class LinearConstantPropagationProblem(project: SomeProject) } } - private def isExpressionInfluencedByFact( + protected def isExpressionInfluencedByFact( expr: Expr[JavaStatement.V], sourceFact: LinearConstantPropagationFact ): Boolean = { @@ -215,10 +216,7 @@ class LinearConstantPropagationProblem(project: SomeProject) callee: Method, returnSite: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { - (sourceFact: LinearConstantPropagationFact) => - { - immutable.Set(sourceFact) - } + identityFlowFunction } override def getNormalEdgeFunction( @@ -235,14 +233,15 @@ class LinearConstantPropagationProblem(project: SomeProject) source.stmt.astID match { case Assignment.ASTID => val assignment = source.stmt.asAssignment - getEdgeFunctionForExpression(assignment.expr) + getEdgeFunctionForExpression(assignment.expr, source) case _ => identityEdgeFunction } } - private def getEdgeFunctionForExpression( - ): EdgeFunction[LinearConstantPropagationValue] = { - expr: Expr[JavaStatement.V] + protected def getEdgeFunctionForExpression( + expr: Expr[JavaStatement.V], + source: JavaStatement + )(implicit @unused propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { expr.astID match { case IntConst.ASTID => LinearCombinationEdgeFunction(0, expr.asIntConst.value) From 324677a7a615875a03da8de50b83d428a60cc918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 2 Aug 2024 12:12:24 +0200 Subject: [PATCH 038/167] Prepare linear constant propagation for 'on fields' extension --- ...ntPropagationPropertyMetaInformation.scala | 2 +- ...nearConstantPropagationEdgeFunctions.scala | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala index 9aaf5fcd01..49e9973700 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala @@ -11,5 +11,5 @@ import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.pro */ object LinearConstantPropagationPropertyMetaInformation extends IDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] { - final val key: PropertyKey[Self] = PropertyKey.create("IDELinearConstantPropagation") + final val key: PropertyKey[Self] = PropertyKey.create("opalj.ide.LinearConstantPropagation") } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala index 36e32aca87..ce519b815f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala @@ -43,7 +43,9 @@ case class LinearCombinationEdgeFunction( c2 ) ) + case VariableValueEdgeFunction => secondEdgeFunction + case UnknownValueEdgeFunction => secondEdgeFunction case IdentityEdgeFunction() => this case AllTopEdgeFunction(_) => secondEdgeFunction @@ -72,6 +74,7 @@ case class LinearCombinationEdgeFunction( VariableValueEdgeFunction case VariableValueEdgeFunction => otherEdgeFunction + case UnknownValueEdgeFunction => this case IdentityEdgeFunction() => this case AllTopEdgeFunction(_) => this @@ -104,3 +107,25 @@ object VariableValueEdgeFunction extends AllBottomEdgeFunction[LinearConstantPro } } } + +/** + * Edge function for variables whose value is unknown + */ +object UnknownValueEdgeFunction extends AllTopEdgeFunction[LinearConstantPropagationValue](UnknownValue) { + override def composeWith( + secondEdgeFunction: EdgeFunction[LinearConstantPropagationValue] + ): EdgeFunction[LinearConstantPropagationValue] = { + secondEdgeFunction match { + case LinearCombinationEdgeFunction(_, _, _) => secondEdgeFunction + + case VariableValueEdgeFunction => secondEdgeFunction + case UnknownValueEdgeFunction => secondEdgeFunction + + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => secondEdgeFunction + + case _ => + throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") + } + } +} From d3f8265826e6a87c332622938ecac6eb88feea2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 2 Aug 2024 14:45:40 +0200 Subject: [PATCH 039/167] Extend linear constant propagation with array access expressions --- .../FieldAccessExample.java | 9 +++++++-- .../LinearConstantPropagationMatcher.scala | 2 ++ .../problem/LinearConstantPropagationProblem.scala | 11 +++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java index e20bba027b..4cf4622445 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java @@ -7,6 +7,7 @@ public class FieldAccessExample { private final int a; int b; static int c = 42; + int[] d = new int[]{23}; public FieldAccessExample(int a, int b) { this.a = a; @@ -20,7 +21,9 @@ public int getA() { @VariableValues({ @VariableValue(variable = "lv4"), @VariableValue(variable = "lv5"), - @VariableValue(variable = "lv6") + @VariableValue(variable = "lv6"), + @VariableValue(variable = "lv8"), + @VariableValue(variable = "lvb") }) public static void main(String[] args) { FieldAccessExample example = new FieldAccessExample(11, 22); @@ -28,7 +31,9 @@ public static void main(String[] args) { int i = example.getA(); int j = example.b; int k = c; + int l = example.d.length; + int m = example.d[0]; - System.out.println("i: " + i + ", j: " + j + ", k: " + k); + System.out.println("i: " + i + ", j: " + j + ", k: " + k + ", l: " + l + ", m: " + m); } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala index 6a6f380563..9c05ba032c 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala @@ -76,6 +76,8 @@ class VariableValueMatcher extends AbstractRepeatablePropertyMatcher { case _ => false } + + case _ => false } ) { None diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 1f17bac485..17ef4a2c26 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -14,6 +14,7 @@ import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.FlowFunction import org.opalj.ide.problem.MeetLattice import org.opalj.tac.ArrayLength +import org.opalj.tac.ArrayLoad import org.opalj.tac.Assignment import org.opalj.tac.BinaryExpr import org.opalj.tac.Expr @@ -121,6 +122,13 @@ class LinearConstantPropagationProblem(project: SomeProject) /* Generate for array length expressions only by null fact */ sourceFact == nullFact + case ArrayLoad.ASTID => + val arrayLoadExpr = expr.asArrayLoad + val arrayVar = arrayLoadExpr.arrayRef.asVar + /* Generate for array access expressions only by null fact and if array stores integers */ + sourceFact == nullFact && + arrayVar.value.asReferenceValue.asReferenceType.asArrayType.componentType.isIntegerType + case GetField.ASTID => val getFieldExpr = expr.asGetField /* Generate for field access expressions only by null fact and if field is of type integer */ @@ -317,6 +325,9 @@ class LinearConstantPropagationProblem(project: SomeProject) case ArrayLength.ASTID => VariableValueEdgeFunction + case ArrayLoad.ASTID => + VariableValueEdgeFunction + case GetField.ASTID => VariableValueEdgeFunction From c58da7e89532a27a92b5c17ef547ba971682cbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 2 Aug 2024 15:07:12 +0200 Subject: [PATCH 040/167] Prepare linear constant propagation for 'on fields' extension --- .../LinearConstantPropagationMatcher.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala index 9c05ba032c..318509b642 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala @@ -44,7 +44,9 @@ class ConstantValueMatcher extends AbstractRepeatablePropertyMatcher { ) { None } else { - Some(s"Result should contain (VariableFact(${expectedVariableName}),ConstantValue(${expectedVariableValue}))!") + Some( + s"Result should contain (${LCPProblem.VariableFact(expectedVariableName, 0)}, ${LCPProblem.ConstantValue(expectedVariableValue)})!" + ) } } } @@ -82,7 +84,9 @@ class VariableValueMatcher extends AbstractRepeatablePropertyMatcher { ) { None } else { - Some(s"Result should contain (VariableFact(${expectedVariableName}),VariableValue)!") + Some( + s"Result should contain (${LCPProblem.VariableFact(expectedVariableName, 0)}, ${LCPProblem.VariableValue})!" + ) } } } From 7215e995dda54c6031e7644ce09edc1a584cc4a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 7 Aug 2024 09:49:21 +0200 Subject: [PATCH 041/167] Refactor test execution --- .../LinearConstantPropagationTests.scala | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala index 5ba0f70378..a76a745c60 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala @@ -3,7 +3,6 @@ package org.opalj.fpcf.ide.linear_constant_propagation import org.opalj.fpcf.ide.IDEPropertiesTest import org.opalj.fpcf.properties.linear_constant_propagation.LinearConstantPropagationProperty -import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationAnalysis class LinearConstantPropagationTests extends IDEPropertiesTest { override def fixtureProjectPackage: List[String] = { @@ -15,11 +14,10 @@ class LinearConstantPropagationTests extends IDEPropertiesTest { LinearConstantPropagationAnalysisScheduler )) - testContext.analyses.foreach { - case analysis: LinearConstantPropagationAnalysis => - getEntryPointsByICFG(analysis.lcpProblem.icfg, testContext.project) - .foreach { method => testContext.propertyStore.force(method, analysis.propertyMetaInformation.key) } - } + methodsWithAnnotations(testContext.project) + .foreach { case (method, _, _) => + testContext.propertyStore.force(method, LinearConstantPropagationAnalysisScheduler.property.key) + } testContext.propertyStore.waitOnPhaseCompletion() testContext.propertyStore.shutdown() From 83b08f9981a3c6c3a2b434b08d52f03aa813a5f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 7 Aug 2024 09:51:24 +0200 Subject: [PATCH 042/167] Fix propagation and equality issues (esp. when applying end summaries) --- .../org/opalj/ide/solver/IDEAnalysis.scala | 7 +++++-- ...LinearConstantPropagationEdgeFunctions.scala | 17 ++++++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index ad6c030a74..5e30f22bec 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -65,6 +65,9 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent * It will never be called on this instance anyway. However, we throw an exception here to be safe. */ throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") } + + override def equalTo(otherEdgeFunction: EdgeFunction[Value]): Boolean = + otherEdgeFunction eq this } /** @@ -531,9 +534,9 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent if (!fPrime.equalTo(oldSummaryFunction)) { s.setSummaryFunction(callToReturnPath, fPrime) - - propagate(((sp, d1), (r, d5)), f.composeWith(fPrime)) } + + propagate(((sp, d1), (r, d5)), f.composeWith(fPrime)) } } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala index ce519b815f..558e3c2c8a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala @@ -45,7 +45,7 @@ case class LinearCombinationEdgeFunction( ) case VariableValueEdgeFunction => secondEdgeFunction - case UnknownValueEdgeFunction => secondEdgeFunction + case UnknownValueEdgeFunction => secondEdgeFunction case IdentityEdgeFunction() => this case AllTopEdgeFunction(_) => secondEdgeFunction @@ -74,7 +74,7 @@ case class LinearCombinationEdgeFunction( VariableValueEdgeFunction case VariableValueEdgeFunction => otherEdgeFunction - case UnknownValueEdgeFunction => this + case UnknownValueEdgeFunction => this case IdentityEdgeFunction() => this case AllTopEdgeFunction(_) => this @@ -106,6 +106,8 @@ object VariableValueEdgeFunction extends AllBottomEdgeFunction[LinearConstantPro case _ => this } } + + override def toString: String = "VariableValueEdgeFunction()" } /** @@ -119,7 +121,7 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LinearConstantPropaga case LinearCombinationEdgeFunction(_, _, _) => secondEdgeFunction case VariableValueEdgeFunction => secondEdgeFunction - case UnknownValueEdgeFunction => secondEdgeFunction + case UnknownValueEdgeFunction => secondEdgeFunction case IdentityEdgeFunction() => this case AllTopEdgeFunction(_) => secondEdgeFunction @@ -128,4 +130,13 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LinearConstantPropaga throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") } } + + override def meetWith( + otherEdgeFunction: EdgeFunction[LinearConstantPropagationValue] + ): EdgeFunction[LinearConstantPropagationValue] = this + + override def equalTo(otherEdgeFunction: EdgeFunction[LinearConstantPropagationValue]): Boolean = + otherEdgeFunction eq this + + override def toString: String = "UnknownValueEdgeFunction()" } From 31c83adeb5247c787423a98e1fbeb6bbba5024b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 7 Aug 2024 09:53:11 +0200 Subject: [PATCH 043/167] Fix UnknownValueEdgeFunction going to edge function that produces VariableValue too early --- .../problem/LinearConstantPropagationEdgeFunctions.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala index 558e3c2c8a..812cace4c1 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala @@ -118,7 +118,9 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LinearConstantPropaga secondEdgeFunction: EdgeFunction[LinearConstantPropagationValue] ): EdgeFunction[LinearConstantPropagationValue] = { secondEdgeFunction match { - case LinearCombinationEdgeFunction(_, _, _) => secondEdgeFunction + case LinearCombinationEdgeFunction(0, _, _) => secondEdgeFunction + case LinearCombinationEdgeFunction(_, _, VariableValue) => secondEdgeFunction + case LinearCombinationEdgeFunction(_, _, _) => this case VariableValueEdgeFunction => secondEdgeFunction case UnknownValueEdgeFunction => secondEdgeFunction From 9fa18e85ee5208f71bea4bb54e8510c0b8d6a76b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 7 Aug 2024 13:48:34 +0200 Subject: [PATCH 044/167] Pass callee to call-to-return edge function generation --- .../org/opalj/ide/problem/FlowRecordingIDEProblem.scala | 3 ++- .../src/main/scala/org/opalj/ide/problem/IDEProblem.scala | 2 ++ .../src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 5 ++++- .../problem/LinearConstantPropagationProblem.scala | 1 + 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index e3147922aa..a1f162237f 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -161,11 +161,12 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal override def getCallToReturnEdgeFunction( callSite: Statement, callSiteFact: Fact, + callee: Callable, returnSite: Statement, returnSiteFact: Fact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] = { val edgeFunctionResult = - baseProblem.getCallToReturnEdgeFunction(callSite, callSiteFact, returnSite, returnSiteFact) + baseProblem.getCallToReturnEdgeFunction(callSite, callSiteFact, callee, returnSite, returnSiteFact) collectedEdgeFunctions.put( createDotEdge(callSite, callSiteFact, returnSite, returnSiteFact, "call-to-return flow"), getEdgeFunctionFromEdgeFunctionResult(edgeFunctionResult) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index 4ef978f8f7..72d3a38913 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -144,12 +144,14 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl * Generate an edge function for a call-to-return flow * @param callSite where the call-to-return flow starts (always a call statement) * @param callSiteFact the fact the flow starts with + * @param callee the callable this flow is about * @param returnSite where the call-to-return flow ends (e.g. the next statement after the call) * @param returnSiteFact the fact the flow ends with */ def getCallToReturnEdgeFunction( callSite: Statement, callSiteFact: Fact, + callee: Callable, returnSite: Statement, returnSiteFact: Fact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 5e30f22bec..866d1e1879 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -556,7 +556,10 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent d3s.foreach { d3 => propagate( ((sp, d1), (r, d3)), - f.composeWith(handleEdgeFunctionResult(problem.getCallToReturnEdgeFunction(n, d2, r, d3), path)) + f.composeWith(handleEdgeFunctionResult( + problem.getCallToReturnEdgeFunction(n, d2, q, r, d3), + path + )) ) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 17ef4a2c26..cd3703b53a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -358,6 +358,7 @@ class LinearConstantPropagationProblem(project: SomeProject) override def getCallToReturnEdgeFunction( callSite: JavaStatement, callSiteFact: LinearConstantPropagationFact, + callee: Method, returnSite: JavaStatement, returnSiteFact: LinearConstantPropagationFact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = identityEdgeFunction From c49b2d1df87fe9a69b32b24d0fcdbad7f7938b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 7 Aug 2024 13:50:47 +0200 Subject: [PATCH 045/167] Add fallback for missing edge functions while recording flow (due to whatever reasons) --- .../org/opalj/ide/problem/FlowRecordingIDEProblem.scala | 6 ++++-- .../problem/LinearConstantPropagationProblem.scala | 4 +--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index a1f162237f..aa432622b7 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -216,7 +216,8 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal fromNode = s"${icfg.stringifyStatement(source, short = true)}" toNode = s"${icfg.stringifyStatement(target, short = true)}" if (recordEdgeFunctions) { - label = s"$targetFact\\n($flowType),\\n${collectedEdgeFunctions(dotEdge)}" + label = + s"$targetFact\\n($flowType),\\n${collectedEdgeFunctions.get(dotEdge).map(_.toString).getOrElse("edge function missing")}" } else { label = s"$targetFact\\n($flowType)" } @@ -224,7 +225,8 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal fromNode = s"(${icfg.stringifyStatement(source, short = true)}, $sourceFact)" toNode = s"(${icfg.stringifyStatement(target, short = true)}, $targetFact)" if (recordEdgeFunctions) { - label = s"$flowType,\\n${collectedEdgeFunctions(dotEdge)}" + label = + s"$flowType,\\n${collectedEdgeFunctions.get(dotEdge).map(_.toString).getOrElse("edge function missing")}" } else { label = flowType } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index cd3703b53a..f339eb617d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -31,9 +31,7 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall * Definition of the linear constant propagation problem */ class LinearConstantPropagationProblem(project: SomeProject) - extends JavaIDEProblem[LinearConstantPropagationFact, LinearConstantPropagationValue]( - new JavaICFG(project) - ) { + extends JavaIDEProblem[LinearConstantPropagationFact, LinearConstantPropagationValue](new JavaICFG(project)) { override val nullFact: LinearConstantPropagationFact = NullFact From 0386854ec5b36d0e3c8b86cc53d7341515d3b55c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 7 Aug 2024 14:49:17 +0200 Subject: [PATCH 046/167] Hide ICFG construction in JavaIDEProblem --- .../problem/LinearConstantPropagationProblem.scala | 3 +-- .../tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala | 7 ++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index f339eb617d..7139a68246 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -23,7 +23,6 @@ import org.opalj.tac.GetStatic import org.opalj.tac.IntConst import org.opalj.tac.Var import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem -import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall @@ -31,7 +30,7 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall * Definition of the linear constant propagation problem */ class LinearConstantPropagationProblem(project: SomeProject) - extends JavaIDEProblem[LinearConstantPropagationFact, LinearConstantPropagationValue](new JavaICFG(project)) { + extends JavaIDEProblem[LinearConstantPropagationFact, LinearConstantPropagationValue](project) { override val nullFact: LinearConstantPropagationFact = NullFact diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala index e83e8b1268..f17571966b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala @@ -2,6 +2,7 @@ package org.opalj.tac.fpcf.analyses.ide.problem import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEProblem import org.opalj.ide.problem.IDEValue @@ -13,4 +14,8 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement */ abstract class JavaIDEProblem[Fact <: IDEFact, Value <: IDEValue]( override val icfg: JavaICFG -) extends IDEProblem[Fact, Value, JavaStatement, Method](icfg) +) extends IDEProblem[Fact, Value, JavaStatement, Method](icfg) { + def this(project: SomeProject) = { + this(new JavaICFG(project)) + } +} From e678847c7cc29fb45c9892a1769e7c0d6be74621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 7 Aug 2024 16:10:35 +0200 Subject: [PATCH 047/167] Add matcher for UnknownValue --- .../UnknownValue.java | 21 ++++++++++ .../UnknownValues.java | 17 ++++++++ .../LinearConstantPropagationMatcher.scala | 40 +++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValue.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValues.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValue.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValue.java new file mode 100644 index 0000000000..198d6c87c0 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValue.java @@ -0,0 +1,21 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.linear_constant_propagation; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.*; + +/** + * Annotation to state that a variables value is unknown + */ +@PropertyValidator(key = LinearConstantPropagationProperty.KEY, validator = UnknownValueMatcher.class) +@Repeatable(UnknownValues.class) +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface UnknownValue { + /** + * The name of the variable + */ + String variable() default ""; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValues.java new file mode 100644 index 0000000000..15c97bef02 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValues.java @@ -0,0 +1,17 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.linear_constant_propagation; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.*; + +/** + * Container annotation for {@link UnknownValue} annotations + */ +@PropertyValidator(key = LinearConstantPropagationProperty.KEY, validator = UnknownValueMatcher.class) +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface UnknownValues { + UnknownValue[] value(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala index 318509b642..498ea5de6f 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala @@ -90,3 +90,43 @@ class VariableValueMatcher extends AbstractRepeatablePropertyMatcher { } } } + +/** + * Matcher for [[UnknownValue]] and [[UnknownValues]] annotations + */ +class UnknownValueMatcher extends AbstractRepeatablePropertyMatcher { + override val singleAnnotationType: ObjectType = + ObjectType("org/opalj/fpcf/properties/linear_constant_propagation/UnknownValue") + override val containerAnnotationType: ObjectType = + ObjectType("org/opalj/fpcf/properties/linear_constant_propagation/UnknownValues") + + override def validateSingleProperty( + p: Project[?], + as: Set[ObjectType], + entity: Any, + a: AnnotationLike, + properties: Iterable[Property] + ): Option[String] = { + val expectedVariableName = + getValue(p, singleAnnotationType, a.elementValuePairs, "variable").asStringValue.value + + if (properties.exists { + case property: BasicIDEProperty[?, ?] => + property.results.exists { + case (LCPProblem.VariableFact(name, _), LCPProblem.UnknownValue) => + expectedVariableName == name + + case _ => false + } + + case _ => false + } + ) { + None + } else { + Some( + s"Result should contain (${LCPProblem.VariableFact(expectedVariableName, 0)}, ${LCPProblem.UnknownValue})!" + ) + } + } +} From e29b374d4ff660a08a11d95727e13cb2054b016e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 7 Aug 2024 16:26:17 +0200 Subject: [PATCH 048/167] Add IDE version of LCP on fields --- .../lcp_on_fields/LCPOnFieldsAnalysis.scala | 20 ++ .../LCPOnFieldsPropertyMetaInformation.scala | 15 + ...rConstantPropagationAnalysisExtended.scala | 24 ++ .../problem/LCPOnFieldsEdgeFunctions.scala | 135 ++++++++ .../problem/LCPOnFieldsFact.scala | 54 ++++ .../problem/LCPOnFieldsLattice.scala | 30 ++ .../problem/LCPOnFieldsProblem.scala | 292 ++++++++++++++++++ .../problem/LCPOnFieldsValue.scala | 29 ++ ...arConstantPropagationProblemExtended.scala | 108 +++++++ 9 files changed, 707 insertions(+) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisExtended.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysis.scala new file mode 100644 index 0000000000..fb033542ae --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysis.scala @@ -0,0 +1,20 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields + +import org.opalj.br.analyses.SomeProject +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsProblem +import org.opalj.tac.fpcf.analyses.ide.solver.JavaIDEAnalysis + +/** + * Linear constant propagation on fields as IDE analysis. This implementation is mainly intended to be an example of a + * cyclic IDE analysis (see [[LCPOnFieldsProblem]]). + */ +class LCPOnFieldsAnalysis(project: SomeProject) + extends JavaIDEAnalysis( + project, + new LCPOnFieldsProblem(project), + LCPOnFieldsPropertyMetaInformation + ) { + val lcpOnFieldsProblem: LCPOnFieldsProblem = + problem.asInstanceOf[LCPOnFieldsProblem] +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala new file mode 100644 index 0000000000..ca805983cf --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala @@ -0,0 +1,15 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields + +import org.opalj.fpcf.PropertyKey +import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsFact +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsValue + +/** + * Meta information for linear constant propagation on fields + */ +object LCPOnFieldsPropertyMetaInformation + extends IDEPropertyMetaInformation[LCPOnFieldsFact, LCPOnFieldsValue] { + final val key: PropertyKey[Self] = PropertyKey.create("opalj.ide.LinearConstantPropagationOnFields") +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisExtended.scala new file mode 100644 index 0000000000..991218f735 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisExtended.scala @@ -0,0 +1,24 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields + +import org.opalj.br.analyses.SomeProject +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LinearConstantPropagationProblemExtended +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationProblem +import org.opalj.tac.fpcf.analyses.ide.solver.JavaIDEAnalysis + +/** + * Extended linear constant propagation as IDE analysis + */ +class LinearConstantPropagationAnalysisExtended(project: SomeProject) + extends JavaIDEAnalysis( + project, + new LinearConstantPropagationProblemExtended(project), + LinearConstantPropagationPropertyMetaInformation + ) { + val lcpProblemExtended: LinearConstantPropagationProblemExtended = + problem.asInstanceOf[LinearConstantPropagationProblemExtended] + + val lcpProblem: LinearConstantPropagationProblem = + lcpProblemExtended +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala new file mode 100644 index 0000000000..ea3da14cfc --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -0,0 +1,135 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem + +import scala.collection.immutable + +import org.opalj.ide.problem.AllTopEdgeFunction +import org.opalj.ide.problem.EdgeFunction +import org.opalj.ide.problem.IdentityEdgeFunction +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationLattice +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue + +/** + * Edge function holding the current object state (in form of its field-value mapping) + */ +case class ObjectEdgeFunction( + values: immutable.Map[String, LinearConstantPropagationValue] +) extends EdgeFunction[LCPOnFieldsValue] { + override def compute(sourceValue: LCPOnFieldsValue): LCPOnFieldsValue = + sourceValue match { + case UnknownValue => UnknownValue + case ObjectValue(_) => + throw new UnsupportedOperationException(s"Computing $this for $sourceValue is not implemented!") + case VariableValue => ObjectValue(values) + } + + override def composeWith(secondEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = + secondEdgeFunction match { + case ObjectEdgeFunction(values2) => + ObjectEdgeFunction((values -- values2.keys) ++ values2) + + case PutFieldEdgeFunction(fieldName, value) => + ObjectEdgeFunction((values - fieldName) + (fieldName -> value)) + + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => secondEdgeFunction + + case _ => + throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") + } + + override def meetWith(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = + otherEdgeFunction match { + case ObjectEdgeFunction(values2) => + ObjectEdgeFunction( + values.keySet + .intersect(values2.keySet) + .map { fieldName => + fieldName -> LinearConstantPropagationLattice.meet(values(fieldName), values2(fieldName)) + } + .toMap + ) + + case PutFieldEdgeFunction(_, _) => + throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => this + + case _ => + throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + } + + override def equalTo(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): Boolean = + (otherEdgeFunction eq this) || + (otherEdgeFunction match { + case ObjectEdgeFunction(values2) => values == values2 + case _ => false + }) +} + +/** + * Edge function for initializing an object + */ +object NewObjectEdgeFunction extends ObjectEdgeFunction(immutable.Map.empty) { + override def toString: String = "NewObjectEdgeFunction()" +} + +/** + * Edge function modeling the effect of writing the field of an object + */ +case class PutFieldEdgeFunction( + fieldName: String, + value: LinearConstantPropagationValue +) extends EdgeFunction[LCPOnFieldsValue] { + override def compute(sourceValue: LCPOnFieldsValue): LCPOnFieldsValue = { + sourceValue match { + case UnknownValue => UnknownValue + case ObjectValue(values) => ObjectValue((values - fieldName) + (fieldName -> value)) + case VariableValue => VariableValue + } + } + + override def composeWith(secondEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = { + secondEdgeFunction match { + case ObjectEdgeFunction(values) if values.contains(fieldName) => secondEdgeFunction + case ObjectEdgeFunction(values) => + ObjectEdgeFunction(values + (fieldName -> value)) + + case PutFieldEdgeFunction(fieldName2, _) if fieldName == fieldName2 => secondEdgeFunction + case PutFieldEdgeFunction(fieldName2, value2) => + ObjectEdgeFunction(immutable.Map(fieldName -> value, fieldName2 -> value2)) + + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => secondEdgeFunction + + case _ => + throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") + } + } + + override def meetWith(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = { + otherEdgeFunction match { + case ObjectEdgeFunction(_) => + throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + + case PutFieldEdgeFunction(fieldName2, value2) if fieldName == fieldName2 => + PutFieldEdgeFunction(fieldName, LinearConstantPropagationLattice.meet(value, value2)) + case PutFieldEdgeFunction(_, _) => + throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => this + + case _ => + throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + } + } + + override def equalTo(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): Boolean = + (otherEdgeFunction eq this) || + (otherEdgeFunction match { + case PutFieldEdgeFunction(fieldName2, value2) => fieldName == fieldName2 && value == value2 + case _ => false + }) +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala new file mode 100644 index 0000000000..356b6463b1 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala @@ -0,0 +1,54 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem + +import org.opalj.ide.problem.IDEFact + +/** + * Type for modeling facts for linear constant propagation on fields + */ +trait LCPOnFieldsFact extends IDEFact + +/** + * Fact to use as null fact + */ +case object NullFact extends LCPOnFieldsFact + +/** + * Type for + */ +trait AbstractObjectFact extends LCPOnFieldsFact { + /** + * The name of the variable (e.g. `lv0`) + */ + val name: String + /** + * Where the variable is defined (used to uniquely identify a variable/variable fact) + */ + val definedAtIndex: Int + + def toObjectFact: ObjectFact = ObjectFact(name, definedAtIndex) +} + +/** + * Fact representing a seen object variable + */ +case class ObjectFact(name: String, definedAtIndex: Int) extends AbstractObjectFact { + override def toObjectFact: ObjectFact = this + + override def toString: String = s"ObjectFact($name)" +} + +/** + * Fact representing a seen object variable and modeling that it gets initialized + */ +case class NewObjectFact(name: String, definedAtIndex: Int) extends AbstractObjectFact { + override def toString: String = s"NewObjectFact($name)" +} + +/** + * Fact representing a seen object variable and modeling that one of its fields gets written + * @param fieldName the name of the field that gets written + */ +case class PutFieldFact(name: String, definedAtIndex: Int, fieldName: String) extends AbstractObjectFact { + override def toString: String = s"PutFieldFact($name, $fieldName)" +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala new file mode 100644 index 0000000000..77fcc307b4 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala @@ -0,0 +1,30 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem + +import org.opalj.ide.problem.MeetLattice +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationLattice + +/** + * Lattice used for linear constant propagation on fields + */ +object LCPOnFieldsLattice extends MeetLattice[LCPOnFieldsValue] { + override def top: LCPOnFieldsValue = UnknownValue + + override def bottom: LCPOnFieldsValue = VariableValue + + override def meet(x: LCPOnFieldsValue, y: LCPOnFieldsValue): LCPOnFieldsValue = (x, y) match { + case (UnknownValue, y) => y + case (x, UnknownValue) => x + case (VariableValue, _) => VariableValue + case (_, VariableValue) => VariableValue + case (ObjectValue(xValues), ObjectValue(yValues)) => + val values = xValues.keySet + .intersect(yValues.keySet) + .map { fieldName => + fieldName -> LinearConstantPropagationLattice.meet(xValues(fieldName), yValues(fieldName)) + } + .toMap + ObjectValue(values) + case _ => VariableValue + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala new file mode 100644 index 0000000000..e69c86df04 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -0,0 +1,292 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem + +import scala.collection.immutable + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.InterimUBP +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyStore +import org.opalj.ide.problem.EdgeFunctionResult +import org.opalj.ide.problem.FinalEdgeFunction +import org.opalj.ide.problem.FlowFunction +import org.opalj.ide.problem.InterimEdgeFunction +import org.opalj.ide.problem.MeetLattice +import org.opalj.tac.Assignment +import org.opalj.tac.New +import org.opalj.tac.PutField +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationLattice +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue +import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall + +/** + * Definition of linear constant propagation on fields problem. This implementation detects basic cases of linear + * constant propagation involving fields. It detects direct field assignments but fails to detect assignments done in a + * method where the value is passed as an argument (e.g. a classical setter). + * This implementation is mainly intended to be an example of a cyclic IDE analysis. + */ +class LCPOnFieldsProblem(project: SomeProject) + extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValue](project) { + override val nullFact: LCPOnFieldsFact = + NullFact + + override val lattice: MeetLattice[LCPOnFieldsValue] = + LCPOnFieldsLattice + + override def getNormalFlowFunction(source: JavaStatement, target: JavaStatement)( + implicit propertyStore: PropertyStore + ): FlowFunction[LCPOnFieldsFact] = + (sourceFact: LCPOnFieldsFact) => { + (source.stmt.astID, sourceFact) match { + case (Assignment.ASTID, NullFact) => + val assignment = source.stmt.asAssignment + assignment.expr.astID match { + case New.ASTID => + /* Generate new object fact from null fact if assignment is a 'new' expression */ + immutable.Set(sourceFact, NewObjectFact(assignment.targetVar.name, source.index)) + + case _ => immutable.Set(sourceFact) + } + + case (PutField.ASTID, f: AbstractObjectFact) => + val putField = source.stmt.asPutField + /* Only consider field assignments for integers */ + if (putField.declaredFieldType.isIntegerType) { + val targetObject = putField.objRef.asVar + if (targetObject.definedBy.contains(f.definedAtIndex)) { + /* Generate new (short-lived) fact to handle field assignment */ + immutable.Set(PutFieldFact(f.name, f.definedAtIndex, putField.name)) + } else { + immutable.Set(f.toObjectFact) + } + } else { + immutable.Set(f.toObjectFact) + } + + case (_, f: AbstractObjectFact) => + /* Specialized facts only live for one step and are turned back into object facts afterwards */ + immutable.Set(f.toObjectFact) + + case _ => immutable.Set(sourceFact) + } + } + + override def getCallFlowFunction( + callSite: JavaStatement, + calleeEntry: JavaStatement, + callee: Method + )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { + (sourceFact: LCPOnFieldsFact) => + { + // TODO (IDE) REMOVE ONCE PRECOMPUTED SUMMARIES ARE IMPLEMENTED + if (callee.classFile.thisType.fqn.startsWith("java/") && callee.name != "") { + immutable.Set.empty + } else { + sourceFact match { + case NullFact => + /* Only propagate null fact if function returns an object */ + if (callee.returnType.isObjectType) { + immutable.Set(sourceFact) + } else { + immutable.Set.empty + } + + case f: AbstractObjectFact => + val callStmt = callSite.stmt.asCall() + + /* All parameters (including the implicit 'this' reference) */ + val allParams = callStmt.allParams + + allParams + .zipWithIndex + .filter { case (param, _) => + /* Only parameters where the variable represented by the source fact is one possible + * initializer */ + param.asVar.definedBy.contains(f.definedAtIndex) + } + .map { case (_, index) => + ObjectFact(s"param$index", -(index + 1)) + } + .toSet + } + } + } + } + + override def getReturnFlowFunction( + calleeExit: JavaStatement, + callee: Method, + returnSite: JavaStatement + )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { + (sourceFact: LCPOnFieldsFact) => + { + sourceFact match { + case NullFact => + /* Always propagate null fact */ + immutable.Set(sourceFact) + + case f: AbstractObjectFact => + val definedAtIndex = f.definedAtIndex + + val callStmt = returnSite.stmt.asCall() + + val allParams = callStmt.allParams + + /* Distinguish parameters and local variables */ + if (definedAtIndex < 0) { + val paramIndex = -(definedAtIndex + 1) + val param = allParams(paramIndex) + val paramName = param.asVar.name.substring(1, param.asVar.name.length - 1) + param.asVar.definedBy.map { dAI => ObjectFact(paramName, dAI) }.toSet + } else { + returnSite.stmt.astID match { + case Assignment.ASTID => + val assignment = returnSite.stmt.asAssignment + + val returnExpr = calleeExit.stmt.asReturnValue.expr + /* Only propagate if the variable represented by the source fact is one possible + * initializer of the variable at the return site */ + if (returnExpr.asVar.definedBy.contains(f.definedAtIndex)) { + immutable.Set(ObjectFact(assignment.targetVar.name, returnSite.index)) + } else { + immutable.Set.empty + } + + case _ => immutable.Set.empty + } + } + } + } + } + + override def getCallToReturnFlowFunction( + callSite: JavaStatement, + callee: Method, + returnSite: JavaStatement + )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { + (sourceFact: LCPOnFieldsFact) => + { + val callStmt = returnSite.stmt.asCall() + + sourceFact match { + case NullFact => + /* Always propagate null fact */ + immutable.Set(sourceFact) + + case f: AbstractObjectFact => + /* Only propagate if the variable represented by the source fact is no initializer of one of the + * parameters */ + if (callStmt.allParams.exists { param => param.asVar.definedBy.contains(f.definedAtIndex) }) { + immutable.Set.empty + } else { + immutable.Set(f.toObjectFact) + } + } + } + } + + override def getNormalEdgeFunction( + source: JavaStatement, + sourceFact: LCPOnFieldsFact, + target: JavaStatement, + targetFact: LCPOnFieldsFact + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LCPOnFieldsValue] = { + if (sourceFact == targetFact) { + return FinalEdgeFunction(identityEdgeFunction) + } + + targetFact match { + case NewObjectFact(_, _) => + FinalEdgeFunction(NewObjectEdgeFunction) + + case PutFieldFact(_, _, fieldName) => + val valueVar = source.stmt.asPutField.value.asVar + + val lcpEOptionP = + propertyStore((source.method, source), LinearConstantPropagationPropertyMetaInformation.key) + + /* Decide based on the current result of the linear constant propagation analysis */ + lcpEOptionP match { + case FinalP(property) => + val value = getVariableFromProperty(valueVar)(property) + FinalEdgeFunction(PutFieldEdgeFunction(fieldName, value)) + + case InterimUBP(property) => + val value = getVariableFromProperty(valueVar)(property) + value match { + case linear_constant_propagation.problem.UnknownValue => + InterimEdgeFunction( + PutFieldEdgeFunction(fieldName, value), + immutable.Set(lcpEOptionP) + ) + case linear_constant_propagation.problem.ConstantValue(_) => + InterimEdgeFunction( + PutFieldEdgeFunction(fieldName, value), + immutable.Set(lcpEOptionP) + ) + case linear_constant_propagation.problem.VariableValue => + FinalEdgeFunction(PutFieldEdgeFunction(fieldName, value)) + } + + case _ => + InterimEdgeFunction( + PutFieldEdgeFunction( + fieldName, + linear_constant_propagation.problem.UnknownValue + ), + immutable.Set(lcpEOptionP) + ) + } + + case _ => FinalEdgeFunction(identityEdgeFunction) + } + } + + private def getVariableFromProperty(var0: JavaStatement.V)(property: Property): LinearConstantPropagationValue = { + property + .asInstanceOf[LinearConstantPropagationPropertyMetaInformation.Self] + .results + .filter { + case (linear_constant_propagation.problem.VariableFact(_, definedAtIndex), _) => + var0.definedBy.contains(definedAtIndex) + case _ => false + } + .map(_._2) + .foldLeft( + linear_constant_propagation.problem.UnknownValue: LinearConstantPropagationValue + ) { + case (value1, value2) => + LinearConstantPropagationLattice.meet(value1, value2) + } + } + + override def getCallEdgeFunction( + callSite: JavaStatement, + callSiteFact: LCPOnFieldsFact, + calleeEntry: JavaStatement, + calleeEntryFact: LCPOnFieldsFact, + callee: Method + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LCPOnFieldsValue] = identityEdgeFunction + + override def getReturnEdgeFunction( + calleeExit: JavaStatement, + calleeExitFact: LCPOnFieldsFact, + callee: Method, + returnSite: JavaStatement, + returnSiteFact: LCPOnFieldsFact + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LCPOnFieldsValue] = identityEdgeFunction + + override def getCallToReturnEdgeFunction( + callSite: JavaStatement, + callSiteFact: LCPOnFieldsFact, + callee: Method, + returnSite: JavaStatement, + returnSiteFact: LCPOnFieldsFact + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LCPOnFieldsValue] = identityEdgeFunction +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala new file mode 100644 index 0000000000..94b4bfc542 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem + +import scala.collection.immutable + +import org.opalj.ide.problem.IDEValue +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue + +/** + * Type for modeling values for linear constant propagation on fields + */ +trait LCPOnFieldsValue extends IDEValue + +/** + * Value not known (yet) + */ +case object UnknownValue extends LCPOnFieldsValue + +/** + * Value representing the state of an object + */ +case class ObjectValue(values: immutable.Map[String, LinearConstantPropagationValue]) extends LCPOnFieldsValue { + override def toString: String = s"ObjectValue(${values.mkString(", ")})" +} + +/** + * Value is variable (not really used currently, mainly for completeness) + */ +case object VariableValue extends LCPOnFieldsValue diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala new file mode 100644 index 0000000000..c36ed56aa0 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala @@ -0,0 +1,108 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem + +import scala.collection.immutable + +import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.InterimUBP +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyStore +import org.opalj.ide.problem.EdgeFunctionResult +import org.opalj.ide.problem.FinalEdgeFunction +import org.opalj.ide.problem.InterimEdgeFunction +import org.opalj.tac.ArrayLength +import org.opalj.tac.ArrayLoad +import org.opalj.tac.Expr +import org.opalj.tac.GetField +import org.opalj.tac.GetStatic +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.ConstantValue +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearCombinationEdgeFunction +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationProblem +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.UnknownValue +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.UnknownValueEdgeFunction +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.VariableValue +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.VariableValueEdgeFunction +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement + +/** + * Extended definition of the linear constant propagation problem, trying to resolve field accesses with the LCP on + * fields analysis. + */ +class LinearConstantPropagationProblemExtended(project: SomeProject) extends LinearConstantPropagationProblem(project) { + override def getEdgeFunctionForExpression( + expr: Expr[JavaStatement.V], + source: JavaStatement + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { + expr.astID match { + case ArrayLength.ASTID => + UnknownValueEdgeFunction + + case ArrayLoad.ASTID => + UnknownValueEdgeFunction + + case GetField.ASTID => + val getFieldExpr = expr.asGetField + val objectVar = getFieldExpr.objRef.asVar + val fieldName = getFieldExpr.name + + val lcpOnFieldsEOptionP = + propertyStore((source.method, source), LCPOnFieldsPropertyMetaInformation.key) + + /* Decide based on the current result of the LCP on fields analysis */ + lcpOnFieldsEOptionP match { + case FinalP(property) => + val value = getObjectFieldFromProperty(objectVar, fieldName)(property) + FinalEdgeFunction(value match { + case UnknownValue => UnknownValueEdgeFunction + case ConstantValue(c) => LinearCombinationEdgeFunction(0, c, lattice.top) + case VariableValue => VariableValueEdgeFunction + }) + + case InterimUBP(property) => + val value = getObjectFieldFromProperty(objectVar, fieldName)(property) + value match { + case UnknownValue => + InterimEdgeFunction(UnknownValueEdgeFunction, immutable.Set(lcpOnFieldsEOptionP)) + case ConstantValue(c) => + InterimEdgeFunction( + LinearCombinationEdgeFunction(0, c, lattice.top), + immutable.Set(lcpOnFieldsEOptionP) + ) + case VariableValue => + FinalEdgeFunction(VariableValueEdgeFunction) + } + + case _ => + InterimEdgeFunction(UnknownValueEdgeFunction, immutable.Set(lcpOnFieldsEOptionP)) + } + + case GetStatic.ASTID => + UnknownValueEdgeFunction + + /* Unchanged behavior for all other expressions */ + case _ => super.getEdgeFunctionForExpression(expr, source) + } + } + + private def getObjectFieldFromProperty( + objectVar: JavaStatement.V, + fieldName: String + )(property: Property): LinearConstantPropagationValue = { + property + .asInstanceOf[LCPOnFieldsPropertyMetaInformation.Self] + .results + .filter { + case (f: AbstractObjectFact, ObjectValue(values)) => + objectVar.definedBy.contains(f.definedAtIndex) && values.contains(fieldName) + case _ => false + } + .map(_._2) + .foldLeft(UnknownValue: LinearConstantPropagationValue) { + case (value, ObjectValue(values)) => + lattice.meet(value, values(fieldName)) + } + } +} From 3473777fcf0d79717b44dbd01cf621466d254da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 7 Aug 2024 16:29:36 +0200 Subject: [PATCH 049/167] Prepare test environment for LCP on fields --- .../lcp_on_fields/LCPOnFieldsProperty.java | 6 + .../properties/lcp_on_fields/ObjectValue.java | 39 ++++++ .../lcp_on_fields/ObjectValues.java | 17 +++ .../lcp_on_fields/LCPOnFieldsMatcher.scala | 112 ++++++++++++++++++ 4 files changed, 174 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsProperty.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValue.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValues.java create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsProperty.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsProperty.java new file mode 100644 index 0000000000..2af58486e4 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsProperty.java @@ -0,0 +1,6 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.lcp_on_fields; + +public class LCPOnFieldsProperty { + public static final String KEY = "LCPOnFields"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValue.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValue.java new file mode 100644 index 0000000000..83090e48ea --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValue.java @@ -0,0 +1,39 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.lcp_on_fields; + +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +import org.opalj.fpcf.properties.linear_constant_propagation.UnknownValue; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; + +import java.lang.annotation.*; + +/** + * Annotation to state that an object has been identified and has certain constant and non-constant values + */ +@PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = ObjectValueMatcher.class) +@Repeatable(ObjectValues.class) +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface ObjectValue { + /** + * The name of the variable the object is stored in + */ + String variable(); + + /** + * The constant fields of the object + */ + ConstantValue[] constantValues() default {}; + + /** + * The non-constant fields of the object + */ + VariableValue[] variableValues() default {}; + + /** + * The fields of the object with unknown value + */ + UnknownValue[] unknownValues() default {}; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValues.java new file mode 100644 index 0000000000..52f0b61aaf --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValues.java @@ -0,0 +1,17 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.lcp_on_fields; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.*; + +/** + * Container annotation for {@link ObjectValue} annotations + */ +@PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = ObjectValueMatcher.class) +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface ObjectValues { + ObjectValue[] value(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala new file mode 100644 index 0000000000..861a74c2cc --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala @@ -0,0 +1,112 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.lcp_on_fields + +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.Project +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractRepeatablePropertyMatcher +import org.opalj.ide.integration.BasicIDEProperty +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.{problem => LCPOnFieldsProblem} +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.{problem => LCPProblem} + +/** + * Matcher for [[ObjectValue]] and [[ObjectValues]] annotations + */ +class ObjectValueMatcher extends AbstractRepeatablePropertyMatcher { + override val singleAnnotationType: ObjectType = + ObjectType("org/opalj/fpcf/properties/lcp_on_fields/ObjectValue") + override val containerAnnotationType: ObjectType = + ObjectType("org/opalj/fpcf/properties/lcp_on_fields/ObjectValues") + + private val constantValueType = ObjectType("org/opalj/fpcf/properties/linear_constant_propagation/ConstantValue") + private val variableValueType = ObjectType("org/opalj/fpcf/properties/linear_constant_propagation/VariableValue") + private val unknownValueType = ObjectType("org/opalj/fpcf/properties/linear_constant_propagation/UnknownValue") + + override def validateSingleProperty( + p: Project[?], + as: Set[ObjectType], + entity: Any, + a: AnnotationLike, + properties: Iterable[Property] + ): Option[String] = { + val expectedVariableName = + getValue(p, singleAnnotationType, a.elementValuePairs, "variable").asStringValue.value + + val expectedConstantValues = + getValue(p, singleAnnotationType, a.elementValuePairs, "constantValues").asArrayValue.values + .map { a => + val annotation = a.asAnnotationValue.annotation + val expectedFieldName = + getValue(p, constantValueType, annotation.elementValuePairs, "variable").asStringValue.value + val expectedValue = + getValue(p, constantValueType, annotation.elementValuePairs, "value").asIntValue.value + + (expectedFieldName, expectedValue) + } + + val expectedVariableValues = + getValue(p, singleAnnotationType, a.elementValuePairs, "variableValues").asArrayValue.values + .map { a => + val annotation = a.asAnnotationValue.annotation + val expectedFieldName = + getValue(p, variableValueType, annotation.elementValuePairs, "variable").asStringValue.value + + expectedFieldName + } + + val expectedUnknownValues = + getValue(p, singleAnnotationType, a.elementValuePairs, "unknownValues").asArrayValue.values + .map { a => + val annotation = a.asAnnotationValue.annotation + val expectedFieldName = + getValue(p, unknownValueType, annotation.elementValuePairs, "variable").asStringValue.value + + expectedFieldName + } + + if (properties.exists { + case property: BasicIDEProperty[?, ?] => + property.results.exists { + case (f: LCPOnFieldsProblem.AbstractObjectFact, LCPOnFieldsProblem.ObjectValue(values)) => + expectedVariableName == f.name && + expectedConstantValues.forall { + case (fieldName, value) => + values.get(fieldName) match { + case Some(LCPProblem.ConstantValue(c)) => value == c + case _ => false + } + } && + expectedVariableValues.forall { fieldName => + values.get(fieldName) match { + case Some(LCPProblem.VariableValue) => true + case _ => false + } + } && + expectedUnknownValues.forall { fieldName => + values.get(fieldName) match { + case Some(LCPProblem.UnknownValue) => true + case _ => false + } + } + + case _ => false + } + + case _ => false + } + ) { + None + } else { + val expectedValues = + expectedConstantValues + .map { case (fieldName, c) => fieldName -> LCPProblem.ConstantValue(c) } + .concat(expectedVariableValues.map { fieldName => fieldName -> LCPProblem.VariableValue }) + .concat(expectedUnknownValues.map { fieldName => fieldName -> LCPProblem.UnknownValue }) + .toMap + Some( + s"Result should contain (${LCPOnFieldsProblem.ObjectFact(expectedVariableName, 0)}, ${LCPOnFieldsProblem.ObjectValue(expectedValues)})" + ) + } + } +} From 35822884de344f7fd497066b5aa3b77aab1ec4f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 7 Aug 2024 16:47:44 +0200 Subject: [PATCH 050/167] Add tests for LCP on fields --- .../CreateObjectInMethodExample.java | 36 +++++++++++++ .../FieldReadWriteAcrossMethodsExample.java | 43 ++++++++++++++++ .../FieldReadWriteConstantExample.java | 29 +++++++++++ .../FieldReadWriteWithBranchingExample.java | 33 ++++++++++++ .../LCPOnFieldsAnalysisScheduler.scala | 51 +++++++++++++++++++ .../ide/lcp_on_fields/LCPOnFieldsTests.scala | 34 +++++++++++++ ...PropagationAnalysisSchedulerExtended.scala | 46 +++++++++++++++++ 7 files changed, 272 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteConstantExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java new file mode 100644 index 0000000000..9e6e9192be --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java @@ -0,0 +1,36 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.lcp_on_fields; + +import org.opalj.fpcf.properties.lcp_on_fields.ObjectValue; +import org.opalj.fpcf.properties.lcp_on_fields.ObjectValues; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +import org.opalj.fpcf.properties.linear_constant_propagation.UnknownValue; + +public class CreateObjectInMethodExample { + private int a = 42; + + private CreateObjectInMethodExample createNew1() { + CreateObjectInMethodExample example = new CreateObjectInMethodExample(); + example.a -= 11; + return example; + } + + private CreateObjectInMethodExample createNew2() { + CreateObjectInMethodExample example = new CreateObjectInMethodExample(); + example.a = a + 2; + return example; + } + + @ObjectValues({ + @ObjectValue(variable = "lv0", constantValues = {@ConstantValue(variable = "a", value = 42)}), + @ObjectValue(variable = "lv2", constantValues = {@ConstantValue(variable = "a", value = 31)}), + @ObjectValue(variable = "lv3", unknownValues = {@UnknownValue(variable = "a")}) + }) + public static void main(String[] args) { + CreateObjectInMethodExample example1 = new CreateObjectInMethodExample(); + CreateObjectInMethodExample example2 = example1.createNew1(); + CreateObjectInMethodExample example3 = example2.createNew2(); + + System.out.println("e1: " + example1.a + ", e2: " + example2.a + ", e3: " + example3.a); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java new file mode 100644 index 0000000000..55edca5c78 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java @@ -0,0 +1,43 @@ +package org.opalj.fpcf.fixtures.lcp_on_fields; + +import org.opalj.fpcf.properties.lcp_on_fields.ObjectValue; +import org.opalj.fpcf.properties.lcp_on_fields.ObjectValues; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; + +public class FieldReadWriteAcrossMethodsExample { + private int a = -2; + + private void setA(int a) { + this.a = a; + } + + private void setATo42() { + this.a = 42; + } + + private int getA() { + return a; + } + + @ObjectValues({ + @ObjectValue(variable = "lv0", variableValues = {@VariableValue(variable = "a")}), + @ObjectValue(variable = "lv2", constantValues = {@ConstantValue(variable = "a", value = 42)}), + @ObjectValue(variable = "lv4", constantValues = {@ConstantValue(variable = "a", value = -2)}) + }) + public static void main(String[] args) { + FieldReadWriteAcrossMethodsExample example1 = new FieldReadWriteAcrossMethodsExample(); + FieldReadWriteAcrossMethodsExample example2 = new FieldReadWriteAcrossMethodsExample(); + FieldReadWriteAcrossMethodsExample example3 = new FieldReadWriteAcrossMethodsExample(); + + example1.setA(23); + int e1a = example1.getA(); + + example2.setATo42(); + int e2a = example2.getA(); + + int e3a = example3.getA(); + + System.out.println("e1a: " + e1a + ", e2a: " + e2a + ", e3a: " + e3a); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteConstantExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteConstantExample.java new file mode 100644 index 0000000000..6f3e6f8046 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteConstantExample.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.lcp_on_fields; + +import org.opalj.fpcf.properties.lcp_on_fields.ObjectValue; +import org.opalj.fpcf.properties.lcp_on_fields.ObjectValues; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; + +public class FieldReadWriteConstantExample { + private int a = -1; + + @ObjectValues({ + @ObjectValue(variable = "lv0", constantValues = {@ConstantValue(variable = "a", value = -1)}), + @ObjectValue(variable = "lv2", constantValues = {@ConstantValue(variable = "a", value = 42)}), + @ObjectValue(variable = "lv4", constantValues = {@ConstantValue(variable = "a", value = 41)}) + }) + public static void main(String[] args) { + FieldReadWriteConstantExample example1 = new FieldReadWriteConstantExample(); + FieldReadWriteConstantExample example2 = new FieldReadWriteConstantExample(); + FieldReadWriteConstantExample example3 = new FieldReadWriteConstantExample(); + + example2.a = 23; + example2.a = 42; + + example3.a = example2.a; + example3.a--; + + System.out.println("e1: " + example1.a + ", e2: " + example2.a + ", e3: " + example3.a); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java new file mode 100644 index 0000000000..2f827ea04b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java @@ -0,0 +1,33 @@ +package org.opalj.fpcf.fixtures.lcp_on_fields; + +import org.opalj.fpcf.properties.lcp_on_fields.ObjectValue; +import org.opalj.fpcf.properties.lcp_on_fields.ObjectValues; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; + +public class FieldReadWriteWithBranchingExample { + private int a = -1; + + @ObjectValues({ + @ObjectValue(variable = "lv0", constantValues = {@ConstantValue(variable = "a", value = 42)}), + @ObjectValue(variable = "lv2", variableValues = {@VariableValue(variable = "a")}), + @ObjectValue(variable = "lv4", variableValues = {@VariableValue(variable = "a")}) + }) + public static void main(String[] args) { + FieldReadWriteWithBranchingExample example1 = new FieldReadWriteWithBranchingExample(); + FieldReadWriteWithBranchingExample example2 = new FieldReadWriteWithBranchingExample(); + FieldReadWriteWithBranchingExample example3 = new FieldReadWriteWithBranchingExample(); + + if (args.length == 0) { + example1.a = 42; + example2.a = 23; + example3.a = example2.a; + } else { + example1.a = 40; + example1.a += 2; + example3.a = example1.a; + } + + System.out.println("e1: " + example1.a + ", e2: " + example2.a + ", e3: " + example3.a); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala new file mode 100644 index 0000000000..750aa4b71d --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -0,0 +1,51 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ide.lcp_on_fields + +import scala.collection.immutable + +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyStore +import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.cg.TypeIteratorKey +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsAnalysis +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsFact +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsValue +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler +import org.opalj.tac.fpcf.analyses.ide.solver.JavaIDEAnalysis +import org.opalj.tac.fpcf.properties.TACAI + +object LCPOnFieldsAnalysisScheduler + extends JavaIDEAnalysisScheduler[LCPOnFieldsFact, LCPOnFieldsValue] { + override def property: IDEPropertyMetaInformation[LCPOnFieldsFact, LCPOnFieldsValue] = + LCPOnFieldsPropertyMetaInformation + + override def requiredProjectInformation: ProjectInformationKeys = + Seq(DeclaredMethodsKey, TypeIteratorKey, PropertyStoreKey, RTACallGraphKey) + + override def init( + project: SomeProject, + ps: PropertyStore + ): JavaIDEAnalysis[LCPOnFieldsFact, LCPOnFieldsValue] = { + new LCPOnFieldsAnalysis(project) + } + + override def uses: immutable.Set[PropertyBounds] = + immutable.Set( + PropertyBounds.finalP(TACAI), + PropertyBounds.finalP(Callers), + PropertyBounds.ub(LinearConstantPropagationPropertyMetaInformation) + ) + + override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = { + // TODO (IDE) TO PREVENT EXCEPTION ABOUT InterimResult WITH EMPTY DEPENDEES TO BE THROWN + PropertyStore.updateDebug(false) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala new file mode 100644 index 0000000000..5a926dbd0b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala @@ -0,0 +1,34 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ide.lcp_on_fields + +import org.opalj.fpcf.ide.IDEPropertiesTest +import org.opalj.fpcf.properties.lcp_on_fields.LCPOnFieldsProperty +import org.opalj.fpcf.properties.linear_constant_propagation.LinearConstantPropagationProperty + +class LCPOnFieldsTests extends IDEPropertiesTest { + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures/lcp_on_fields") + } + + describe("Execute the o.o.t.f.a.i.i.l.LCPOnFieldsAnalysis") { + val testContext = executeAnalyses(Set( + LinearConstantPropagationAnalysisSchedulerExtended, + LCPOnFieldsAnalysisScheduler + )) + + val entryPoints = methodsWithAnnotations(testContext.project) + entryPoints.foreach { case (method, _, _) => + testContext.propertyStore.force(method, LCPOnFieldsAnalysisScheduler.property.key) + } + + testContext.propertyStore.waitOnPhaseCompletion() + testContext.propertyStore.shutdown() + + validateProperties( + testContext, + methodsWithAnnotations(testContext.project), + Set(LCPOnFieldsProperty.KEY, LinearConstantPropagationProperty.KEY), + failOnInterimResults = false + ) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala new file mode 100644 index 0000000000..e8a9c66d65 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala @@ -0,0 +1,46 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ide.lcp_on_fields + +import scala.collection.immutable + +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyStore +import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.cg.TypeIteratorKey +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LinearConstantPropagationAnalysisExtended +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler +import org.opalj.tac.fpcf.analyses.ide.solver.JavaIDEAnalysis +import org.opalj.tac.fpcf.properties.TACAI + +object LinearConstantPropagationAnalysisSchedulerExtended + extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] { + override def property: IDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] = + LinearConstantPropagationPropertyMetaInformation + + override def requiredProjectInformation: ProjectInformationKeys = + Seq(DeclaredMethodsKey, TypeIteratorKey, PropertyStoreKey, RTACallGraphKey) + + override def init( + project: SomeProject, + ps: PropertyStore + ): JavaIDEAnalysis[LinearConstantPropagationFact, LinearConstantPropagationValue] = { + new LinearConstantPropagationAnalysisExtended(project) + } + + override def uses: immutable.Set[PropertyBounds] = + immutable.Set( + PropertyBounds.finalP(TACAI), + PropertyBounds.finalP(Callers), + PropertyBounds.ub(LCPOnFieldsPropertyMetaInformation) + ) +} From 7aa00ba3b2e5b21db915e84e88425c43738680a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 7 Aug 2024 16:48:53 +0200 Subject: [PATCH 051/167] Add parameter to general PropertiesTest to allow non-final results at the end --- .../scala/org/opalj/fpcf/PropertiesTest.scala | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala index f7cf2a2693..e0e960510b 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala @@ -180,6 +180,7 @@ abstract class PropertiesTest extends AnyFunSpec with Matchers { * @param eas An iterator over the relevant entities along with the found annotations. * @param propertyKinds The kinds of properties (as specified by the annotations) that are * to be tested. + * @param failOnInterimResults Whether to fail when seeing a non-final result or to accept (and validate) it */ def validateProperties( context: TestContext, @@ -187,7 +188,8 @@ abstract class PropertiesTest extends AnyFunSpec with Matchers { Entity, /*the processed annotation*/ String => String /* a String identifying the entity */, Iterable[AnnotationLike] )], - propertyKinds: Set[String] + propertyKinds: Set[String], + failOnInterimResults: Boolean = true ): Unit = { val TestContext(p: Project[URL], ps: PropertyStore, as: List[FPCFAnalysis]) = context val ats = @@ -207,12 +209,20 @@ abstract class PropertiesTest extends AnyFunSpec with Matchers { it(entityIdentifier(s"$annotationTypeName")) { info(s"validator: " + matcherClass.toString.substring(32)) val epss = ps.properties(e).toIndexedSeq - val nonFinalPSs = epss.filter(_.isRefinable) - assert( - nonFinalPSs.isEmpty, - nonFinalPSs.mkString("some epss are not final:\n\t", "\n\t", "\n") - ) - val properties = epss.map(_.toFinalEP.p) + if (failOnInterimResults) { + val nonFinalPSs = epss.filter(_.isRefinable) + assert( + nonFinalPSs.isEmpty, + nonFinalPSs.mkString("some epss are not final:\n\t", "\n\t", "\n") + ) + } + val properties = epss.map { + case FinalP(p) => p + case eps @ InterimUBP(ub) if eps.hasUBP => ub + case eps @ InterimLBP(lb) if eps.hasLBP => lb + case eps => + throw new IllegalStateException(s"Unable to extract property from $eps!") + } matcher.validateProperty(p, ats, e, annotation, properties) match { case Some(error: String) => val propertiesAsStrings = properties.map(_.toString) From 42d067bd59b5879cc249974daa1cb02d51cb8eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 12 Aug 2024 09:24:58 +0200 Subject: [PATCH 052/167] Exchange InterimResults with empty dependees with PartialResults --- .../LCPOnFieldsAnalysisScheduler.scala | 5 --- .../org/opalj/ide/solver/IDEAnalysis.scala | 33 +++++++++++-------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala index 750aa4b71d..e41b2e2b21 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -43,9 +43,4 @@ object LCPOnFieldsAnalysisScheduler PropertyBounds.finalP(Callers), PropertyBounds.ub(LinearConstantPropagationPropertyMetaInformation) ) - - override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = { - // TODO (IDE) TO PREVENT EXCEPTION ABOUT InterimResult WITH EMPTY DEPENDEES TO BE THROWN - PropertyStore.updateDebug(false) - } } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 866d1e1879..7b062b821e 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -9,17 +9,20 @@ import scala.collection.mutable import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.fpcf.Entity +import org.opalj.fpcf.InterimEUBP import org.opalj.fpcf.InterimPartialResult -import org.opalj.fpcf.InterimResult +import org.opalj.fpcf.PartialResult import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.Result import org.opalj.fpcf.Results import org.opalj.fpcf.SomeEOptionP import org.opalj.fpcf.SomeEPK import org.opalj.fpcf.SomeEPS +import org.opalj.fpcf.SomePartialResult import org.opalj.ide.ConfigKeyDebugLog import org.opalj.ide.ConfigKeyTraceLog import org.opalj.ide.FrameworkName +import org.opalj.ide.integration.IDEProperty import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.ide.problem.AllTopEdgeFunction import org.opalj.ide.problem.EdgeFunction @@ -414,19 +417,9 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent s.getDependees.toSet, onDependeeUpdateContinuation(callable) ), - InterimResult.forUB( - callable, - propertyForExit, - immutable.Set.empty, - _ => { throw new IllegalStateException() } - ) + createPartialResult(callable, propertyForExit) ) ++ propertiesByStatement.map { case (stmt, property) => - InterimResult.forUB( - (callable, stmt), - property, - immutable.Set.empty, - _ => { throw new IllegalStateException() } - ) + createPartialResult((callable, stmt), property) } ) } @@ -448,6 +441,20 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent createResult(callable) } + private def createPartialResult(entity: Entity, property: IDEProperty[Fact, Value]): SomePartialResult = { + PartialResult( + entity, + propertyMetaInformation.key, + { (eOptionP: SomeEOptionP) => + if (eOptionP.hasUBP && eOptionP.ub == property) { + None + } else { + Some(InterimEUBP(entity, property)) + } + } + ) + } + private def seedPhase1(callable: Callable)(implicit s: State): Unit = { s.rememberCallable(callable) From ce3a4184c0c6e5699a5591b6107adbbab1340798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 12 Aug 2024 10:05:25 +0200 Subject: [PATCH 053/167] Enhance directory handling and logging of FlowRecordingIDEAnalysis --- .../ide/solver/FlowRecordingIDEAnalysis.scala | 27 ++++++++++++++++++- .../org/opalj/ide/solver/IDEAnalysis.scala | 7 ++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala index 108e16ebfd..2d738658bf 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala @@ -4,10 +4,13 @@ package org.opalj.ide.solver import scala.annotation.tailrec import java.io.File +import java.io.FileNotFoundException import java.io.FileWriter +import java.io.Writer import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths +import scala.collection.mutable import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.Entity @@ -48,6 +51,11 @@ class FlowRecordingIDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Ca val flowRecordingProblem: FlowRecordingIDEProblem[Fact, Value, Statement, Callable] = problem.asInstanceOf[FlowRecordingIDEProblem[Fact, Value, Statement, Callable]] + /** + * Associate used writers with the file they write to + */ + private val fileByWriter = mutable.Map.empty[Writer, File] + /** * Get the file to write the graph to. If [[path]] references a file then this method will return [[path]]. If * [[path]] references a directory, a new filename is created (s.t. no file gets overwritten). @@ -90,7 +98,22 @@ class FlowRecordingIDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Ca def startRecording(): Unit = { logDebug("starting recording") - val writer = new FileWriter(getFile) + val file = getFile + val directoryAsFile = file.getParentFile + val directoryAsPath = directoryAsFile.toPath.toAbsolutePath.normalize() + if (!directoryAsFile.exists()) { + if (directoryAsPath.startsWith(Paths.get(".").toAbsolutePath.normalize())) { + logWarn(s"creating directory '$directoryAsPath' as it didn't exist!") + directoryAsFile.mkdirs() + } else { + throw new FileNotFoundException( + s"Directory '$directoryAsPath' does not exist! Directories outside of the project directory are not created automatically!" + ) + } + } + + val writer = new FileWriter(file) + fileByWriter.put(writer, file) flowRecordingProblem.startRecording(writer) } @@ -101,5 +124,7 @@ class FlowRecordingIDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Ca val writer = flowRecordingProblem.stopRecording() writer.close() + + logInfo(s"wrote flow recording to '${fileByWriter(writer)}'") } } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 7b062b821e..4961a2339a 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -1,8 +1,6 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ide.solver -import scala.annotation.unused - import scala.collection.immutable import scala.collection.mutable @@ -300,7 +298,10 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent s"Path(\n$indent\t${nodeToString(path._1, s"$indent\t")} ->\n$indent\t${nodeToString(path._2, s"$indent\t")}\n$indent)" } - @unused + protected def logInfo(message: => String): Unit = { + OPALLogger.info(FrameworkName, message) + } + protected def logWarn(message: => String): Unit = { OPALLogger.warn(FrameworkName, message) } From ab90eef911b2d2cd4803a2a43ced474981cb514e Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Fri, 9 Aug 2024 12:03:00 +0200 Subject: [PATCH 054/167] Fix lines continued by operator --- .../problem/LinearConstantPropagationProblem.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 7139a68246..9ff6b8a998 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -86,13 +86,13 @@ class LinearConstantPropagationProblem(project: SomeProject) if (sourceFact == nullFact) { /* Only generate fact by null fact for binary expressions if both subexpressions are influenced. * This is needed for binary expressions with one constant and one variable. */ - isExpressionInfluencedByFact(leftExpr, sourceFact) - && isExpressionInfluencedByFact (rightExpr, sourceFact) + isExpressionInfluencedByFact(leftExpr, sourceFact) && + isExpressionInfluencedByFact(rightExpr, sourceFact) } else { /* If source fact is not null fact, generate new fact if one subexpression is influenced by the * source fact */ - isExpressionInfluencedByFact(leftExpr, sourceFact) - || isExpressionInfluencedByFact (rightExpr, sourceFact) + isExpressionInfluencedByFact(leftExpr, sourceFact) || + isExpressionInfluencedByFact(rightExpr, sourceFact) } case Var.ASTID => From 3865ca7f9faf295290e73ea8369acac4eef651b8 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Fri, 9 Aug 2024 12:03:24 +0200 Subject: [PATCH 055/167] Add header to test files --- .../lcp_on_fields/FieldReadWriteAcrossMethodsExample.java | 1 + .../lcp_on_fields/FieldReadWriteWithBranchingExample.java | 1 + .../fixtures/linear_constant_propagation/FieldAccessExample.java | 1 + .../fpcf/fixtures/linear_constant_propagation/LoopExample.java | 1 + .../fixtures/linear_constant_propagation/RecursionExample.java | 1 + 5 files changed, 5 insertions(+) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java index 55edca5c78..88c0d0d3f6 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java @@ -1,3 +1,4 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.lcp_on_fields; import org.opalj.fpcf.properties.lcp_on_fields.ObjectValue; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java index 2f827ea04b..813ca6e792 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java @@ -1,3 +1,4 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.lcp_on_fields; import org.opalj.fpcf.properties.lcp_on_fields.ObjectValue; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java index 4cf4622445..29fde447c2 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java @@ -1,3 +1,4 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.linear_constant_propagation; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/LoopExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/LoopExample.java index 2ab470e946..41c8de95bf 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/LoopExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/LoopExample.java @@ -1,3 +1,4 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.linear_constant_propagation; import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/RecursionExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/RecursionExample.java index 25b279a798..e724b4d6bc 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/RecursionExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/RecursionExample.java @@ -1,3 +1,4 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.linear_constant_propagation; import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; From ab125dc46c316470ffece12e4b7b5ad550c483e0 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Fri, 9 Aug 2024 12:03:49 +0200 Subject: [PATCH 056/167] Import formatting --- .../src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala | 2 ++ .../LinearConstantPropagationAnalysisScheduler.scala | 1 + 2 files changed, 3 insertions(+) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala index c758553a24..3ee1336b17 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala @@ -2,8 +2,10 @@ package org.opalj.fpcf.ide import java.net.URL + import com.typesafe.config.Config import com.typesafe.config.ConfigValueFactory + import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.br.Method diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala index 254f1e041e..c28af8fa7b 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala @@ -2,6 +2,7 @@ package org.opalj.fpcf.ide.linear_constant_propagation import scala.collection.immutable + import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.analyses.SomeProject From 2acf8121ba15090106ada1f03722ab0743d21090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 12 Aug 2024 12:02:34 +0200 Subject: [PATCH 057/167] Apply formatter --- .../problem/LCPOnFieldsEdgeFunctions.scala | 6 +++--- .../LinearConstantPropagationEdgeFunctions.scala | 6 +++--- .../tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala | 2 +- .../tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala | 6 +++--- .../tac/fpcf/analyses/ide/solver/JavaStatement.scala | 10 +++++----- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala index ea3da14cfc..5df68c3f22 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -13,7 +13,7 @@ import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.pro * Edge function holding the current object state (in form of its field-value mapping) */ case class ObjectEdgeFunction( - values: immutable.Map[String, LinearConstantPropagationValue] + values: immutable.Map[String, LinearConstantPropagationValue] ) extends EdgeFunction[LCPOnFieldsValue] { override def compute(sourceValue: LCPOnFieldsValue): LCPOnFieldsValue = sourceValue match { @@ -79,8 +79,8 @@ object NewObjectEdgeFunction extends ObjectEdgeFunction(immutable.Map.empty) { * Edge function modeling the effect of writing the field of an object */ case class PutFieldEdgeFunction( - fieldName: String, - value: LinearConstantPropagationValue + fieldName: String, + value: LinearConstantPropagationValue ) extends EdgeFunction[LCPOnFieldsValue] { override def compute(sourceValue: LCPOnFieldsValue): LCPOnFieldsValue = { sourceValue match { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala index 812cace4c1..7b76a6147a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala @@ -10,9 +10,9 @@ import org.opalj.ide.problem.IdentityEdgeFunction * Edge function to calculate the value of a variable `i` for a statement `val i = a * x + b` */ case class LinearCombinationEdgeFunction( - a: Int, - b: Int, - c: LinearConstantPropagationValue = LinearConstantPropagationLattice.top + a: Int, + b: Int, + c: LinearConstantPropagationValue = LinearConstantPropagationLattice.top ) extends EdgeFunction[LinearConstantPropagationValue] { override def compute(sourceValue: LinearConstantPropagationValue): LinearConstantPropagationValue = { LinearConstantPropagationLattice.meet( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala index f17571966b..0fc92f4ebc 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala @@ -13,7 +13,7 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement * Specialized IDE problem for Java programs */ abstract class JavaIDEProblem[Fact <: IDEFact, Value <: IDEValue]( - override val icfg: JavaICFG + override val icfg: JavaICFG ) extends IDEProblem[Fact, Value, JavaStatement, Method](icfg) { def this(project: SomeProject) = { this(new JavaICFG(project)) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala index ebdcb1c2a0..d13c40a2a4 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala @@ -13,7 +13,7 @@ import org.opalj.ide.solver.IDEAnalysis * Solver for IDE problems specialized for Java programs */ class JavaIDEAnalysis[Fact <: IDEFact, Value <: IDEValue]( - project: SomeProject, - problem: IDEProblem[Fact, Value, JavaStatement, Method], - propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] + project: SomeProject, + problem: IDEProblem[Fact, Value, JavaStatement, Method], + propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] ) extends IDEAnalysis[Fact, Value, JavaStatement, Method](project, problem, propertyMetaInformation) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala index 7b1daabac5..30288a735b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala @@ -18,11 +18,11 @@ import org.opalj.value.ValueInformation * @param isReturnNode whether the statement models the return node of a call */ case class JavaStatement( - method: Method, - index: Int, - isReturnNode: Boolean = false, - code: Array[Stmt[JavaStatement.V]], - cfg: CFG[Stmt[JavaStatement.V], TACStmts[JavaStatement.V]] + method: Method, + index: Int, + isReturnNode: Boolean = false, + code: Array[Stmt[JavaStatement.V]], + cfg: CFG[Stmt[JavaStatement.V], TACStmts[JavaStatement.V]] ) { // TODO (IDE) DOES THIS GET REEVALUATED EACH CALL? DO WE ALWAYS CALL IT AT LEAST ONCE? MAYBE MAKE IT A (LAZY) // PROPERTY From 610d108e5fc9081ac9c04f1a149309485e705a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 6 Sep 2024 09:44:58 +0200 Subject: [PATCH 058/167] Apply formatter --- .../org/opalj/ide/integration/IDEProperty.scala | 4 ++-- .../ide/solver/FlowRecordingIDEAnalysis.scala | 14 +++++++------- .../scala/org/opalj/ide/solver/IDEAnalysis.scala | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala index 26eb0dce34..73f01ddca7 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala @@ -18,8 +18,8 @@ abstract class IDEProperty[Fact <: IDEFact, Value <: IDEValue] extends Property * @param propertyMetaInformation corresponding to the produced property */ class BasicIDEProperty[Fact <: IDEFact, Value <: IDEValue]( - val results: collection.Set[(Fact, Value)], - val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] + val results: collection.Set[(Fact, Value)], + val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] ) extends IDEProperty[Fact, Value] { override type Self = propertyMetaInformation.Self diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala index 2d738658bf..be42dc2882 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala @@ -33,13 +33,13 @@ import org.opalj.ide.problem.IDEValue * @param recordEdgeFunctions whether to record edge functions too or just stick with the flow */ class FlowRecordingIDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( - project: SomeProject, - baseProblem: IDEProblem[Fact, Value, Statement, Callable], - propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value], - path: Option[Path] = None, - recorderMode: FlowRecorderMode = FlowRecorderModes.NODE_AS_STMT_AND_FACT, - uniqueFlowsOnly: Boolean = true, - recordEdgeFunctions: Boolean = true + project: SomeProject, + baseProblem: IDEProblem[Fact, Value, Statement, Callable], + propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value], + path: Option[Path] = None, + recorderMode: FlowRecorderMode = FlowRecorderModes.NODE_AS_STMT_AND_FACT, + uniqueFlowsOnly: Boolean = true, + recordEdgeFunctions: Boolean = true ) extends IDEAnalysis( project, new FlowRecordingIDEProblem(baseProblem, recorderMode, uniqueFlowsOnly, recordEdgeFunctions), diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 4961a2339a..704d70f3f7 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -38,9 +38,9 @@ import org.opalj.log.OPALLogger * from 1996 as base. */ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( - val project: SomeProject, - val problem: IDEProblem[Fact, Value, Statement, Callable], - val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] + val project: SomeProject, + val problem: IDEProblem[Fact, Value, Statement, Callable], + val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] ) extends FPCFAnalysis { private type Node = (Statement, Fact) /** From e2d6138004b4d14882f62244e782f6ac9794cd7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 6 Sep 2024 15:24:54 +0200 Subject: [PATCH 059/167] Fix result creation and introduce strict type and scheduler separation between IDEAnalysis and IDEAnalysisProxy Previously, results with the proxies key have been created by IDEAnalysis (and the proxy did not change the key at all), which resulted in strange behavior (only parts of IDEAnalysisProxy were actually used). Additionally, the IDEAnalysisProxy did miss result creation for some cases. Finally, the IDEAnalysisProxy received its own scheduler s.t. its results and dependencies can be specified correctly (currently is scheduled manually, but can potentially be scheduled automatically later). This also triggered a refactoring of the relation between properties and their meta information. --- .../LCPOnFieldsAnalysisScheduler.scala | 2 +- .../ide/lcp_on_fields/LCPOnFieldsTests.scala | 10 ++-- ...PropagationAnalysisSchedulerExtended.scala | 2 +- ...ConstantPropagationAnalysisScheduler.scala | 2 +- .../LinearConstantPropagationTests.scala | 9 ++- .../IDEAnalysisProxyScheduler.scala | 55 +++++++++++++++++++ .../integration/IDEAnalysisScheduler.scala | 18 ++---- .../opalj/ide/integration/IDEProperty.scala | 28 ++++------ .../IDEPropertyMetaInformation.scala | 12 +++- .../ide/integration/IDERawProperty.scala | 37 +++++++++++++ .../IDERawPropertyMetaInformation.scala | 27 +++++++++ .../org/opalj/ide/solver/IDEAnalysis.scala | 27 +++++---- .../opalj/ide/solver/IDEAnalysisProxy.scala | 32 +++++++---- 13 files changed, 197 insertions(+), 64 deletions(-) create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisProxyScheduler.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala index e41b2e2b21..66aac70405 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -24,7 +24,7 @@ import org.opalj.tac.fpcf.properties.TACAI object LCPOnFieldsAnalysisScheduler extends JavaIDEAnalysisScheduler[LCPOnFieldsFact, LCPOnFieldsValue] { - override def property: IDEPropertyMetaInformation[LCPOnFieldsFact, LCPOnFieldsValue] = + override def propertyMetaInformation: IDEPropertyMetaInformation[LCPOnFieldsFact, LCPOnFieldsValue] = LCPOnFieldsPropertyMetaInformation override def requiredProjectInformation: ProjectInformationKeys = diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala index 5a926dbd0b..f2b3a3df49 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala @@ -3,7 +3,7 @@ package org.opalj.fpcf.ide.lcp_on_fields import org.opalj.fpcf.ide.IDEPropertiesTest import org.opalj.fpcf.properties.lcp_on_fields.LCPOnFieldsProperty -import org.opalj.fpcf.properties.linear_constant_propagation.LinearConstantPropagationProperty +import org.opalj.ide.integration.IDEAnalysisProxyScheduler class LCPOnFieldsTests extends IDEPropertiesTest { override def fixtureProjectPackage: List[String] = { @@ -13,12 +13,14 @@ class LCPOnFieldsTests extends IDEPropertiesTest { describe("Execute the o.o.t.f.a.i.i.l.LCPOnFieldsAnalysis") { val testContext = executeAnalyses(Set( LinearConstantPropagationAnalysisSchedulerExtended, - LCPOnFieldsAnalysisScheduler + LCPOnFieldsAnalysisScheduler, + new IDEAnalysisProxyScheduler(LinearConstantPropagationAnalysisSchedulerExtended), + new IDEAnalysisProxyScheduler(LCPOnFieldsAnalysisScheduler) )) val entryPoints = methodsWithAnnotations(testContext.project) entryPoints.foreach { case (method, _, _) => - testContext.propertyStore.force(method, LCPOnFieldsAnalysisScheduler.property.key) + testContext.propertyStore.force(method, LCPOnFieldsAnalysisScheduler.propertyMetaInformation.key) } testContext.propertyStore.waitOnPhaseCompletion() @@ -27,7 +29,7 @@ class LCPOnFieldsTests extends IDEPropertiesTest { validateProperties( testContext, methodsWithAnnotations(testContext.project), - Set(LCPOnFieldsProperty.KEY, LinearConstantPropagationProperty.KEY), + Set(LCPOnFieldsProperty.KEY), failOnInterimResults = false ) } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala index e8a9c66d65..e8d922de5f 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala @@ -24,7 +24,7 @@ import org.opalj.tac.fpcf.properties.TACAI object LinearConstantPropagationAnalysisSchedulerExtended extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] { - override def property: IDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] = + override def propertyMetaInformation: IDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] = LinearConstantPropagationPropertyMetaInformation override def requiredProjectInformation: ProjectInformationKeys = diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala index c28af8fa7b..20f22e6c83 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala @@ -23,7 +23,7 @@ import org.opalj.tac.fpcf.properties.TACAI object LinearConstantPropagationAnalysisScheduler extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] { - override def property: IDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] = + override def propertyMetaInformation: IDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] = LinearConstantPropagationPropertyMetaInformation override def requiredProjectInformation: ProjectInformationKeys = diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala index a76a745c60..ad0659eda2 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala @@ -3,6 +3,7 @@ package org.opalj.fpcf.ide.linear_constant_propagation import org.opalj.fpcf.ide.IDEPropertiesTest import org.opalj.fpcf.properties.linear_constant_propagation.LinearConstantPropagationProperty +import org.opalj.ide.integration.IDEAnalysisProxyScheduler class LinearConstantPropagationTests extends IDEPropertiesTest { override def fixtureProjectPackage: List[String] = { @@ -11,12 +12,16 @@ class LinearConstantPropagationTests extends IDEPropertiesTest { describe("Execute the o.o.t.f.a.i.i.l.LinearConstantPropagationAnalysis") { val testContext = executeAnalyses(Set( - LinearConstantPropagationAnalysisScheduler + LinearConstantPropagationAnalysisScheduler, + new IDEAnalysisProxyScheduler(LinearConstantPropagationAnalysisScheduler) )) methodsWithAnnotations(testContext.project) .foreach { case (method, _, _) => - testContext.propertyStore.force(method, LinearConstantPropagationAnalysisScheduler.property.key) + testContext.propertyStore.force( + method, + LinearConstantPropagationAnalysisScheduler.propertyMetaInformation.key + ) } testContext.propertyStore.waitOnPhaseCompletion() diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisProxyScheduler.scala new file mode 100644 index 0000000000..85fb39402e --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisProxyScheduler.scala @@ -0,0 +1,55 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.integration + +import scala.collection.immutable + +import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.fpcf.Entity +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyStore +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEValue +import org.opalj.ide.solver.IDEAnalysisProxy + +/** + * A scheduler to schedule the proxy analysis that is used to access the IDE analysis results + */ +class IDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( + propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] +) extends FPCFLazyAnalysisScheduler { + override final type InitializationData = IDEAnalysisProxy[Fact, Value, Statement, Callable] + + def this(ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable]) = { + this(ideAnalysisScheduler.propertyMetaInformation) + } + + override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(propertyMetaInformation)) + + override def requiredProjectInformation: ProjectInformationKeys = Seq(PropertyStoreKey) + + override def init(project: SomeProject, ps: PropertyStore): IDEAnalysisProxy[Fact, Value, Statement, Callable] = { + new IDEAnalysisProxy[Fact, Value, Statement, Callable](project, propertyMetaInformation) + } + + override def uses: Set[PropertyBounds] = + immutable.Set(PropertyBounds.ub(propertyMetaInformation.backingPropertyMetaInformation)) + + override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + + override def register( + project: SomeProject, + propertyStore: PropertyStore, + analysis: IDEAnalysisProxy[Fact, Value, Statement, Callable] + ): FPCFAnalysis = { + propertyStore.registerLazyPropertyComputation(propertyMetaInformation.key, analysis.proxyAnalysis) + analysis + } + + override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} + + override def afterPhaseCompletion(p: SomeProject, ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala index cca571b045..5379af226a 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala @@ -6,12 +6,10 @@ import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler import org.opalj.fpcf.Entity import org.opalj.fpcf.PropertyBounds -import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyStore import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEValue import org.opalj.ide.solver.IDEAnalysis -import org.opalj.ide.solver.IDEAnalysisProxy /** * A base scheduler for IDE analyses adding common default behavior @@ -20,28 +18,22 @@ abstract class IDEAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue, Statemen extends FPCFLazyAnalysisScheduler { override final type InitializationData = IDEAnalysis[Fact, Value, Statement, Callable] - def property: IDEPropertyMetaInformation[Fact, Value] + def propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] - override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) + override def derivesLazily: Some[PropertyBounds] = + Some(PropertyBounds.ub(propertyMetaInformation.backingPropertyMetaInformation)) override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} - private lazy val backingPropertyKey: PropertyKey[IDEPropertyMetaInformation[Fact, Value]#Self] = { - PropertyKey.create(s"${PropertyKey.name(property.key)}_Backing") - } - override def register( project: SomeProject, propertyStore: PropertyStore, analysis: IDEAnalysis[Fact, Value, Statement, Callable] ): FPCFAnalysis = { propertyStore.registerLazyPropertyComputation( - property.key, - new IDEAnalysisProxy[Fact, Value, Statement, Callable](project, property, backingPropertyKey).proxyAnalysis + propertyMetaInformation.backingPropertyMetaInformation.key, + analysis.performAnalysis ) - - propertyStore.registerLazyPropertyComputation(backingPropertyKey, analysis.performAnalysis) - analysis } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala index 73f01ddca7..af339811e7 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala @@ -7,40 +7,36 @@ import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEValue /** - * Base class of properties that are produced by an IDE analysis + * Base interface of properties that are produced by an IDE analysis */ -abstract class IDEProperty[Fact <: IDEFact, Value <: IDEValue] extends Property - with IDEPropertyMetaInformation[Fact, Value] +trait IDEProperty[Fact <: IDEFact, Value <: IDEValue] extends Property /** * Basic implementation of [[IDEProperty]] that simply wraps the fact-value results of an IDE analysis + * @param key the property key * @param results the results produced by the analysis - * @param propertyMetaInformation corresponding to the produced property */ class BasicIDEProperty[Fact <: IDEFact, Value <: IDEValue]( - val results: collection.Set[(Fact, Value)], - val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] + val key: PropertyKey[BasicIDEProperty[Fact, Value]], + val results: collection.Set[(Fact, Value)] ) extends IDEProperty[Fact, Value] { - - override type Self = propertyMetaInformation.Self - - override def key: PropertyKey[Self] = propertyMetaInformation.key + override type Self = BasicIDEProperty[Fact, Value] override def toString: String = { - s"${PropertyKey.name(propertyMetaInformation.key)}:\n${ + s"BasicIDEProperty(${PropertyKey.name(key)}, {\n${ results.map { case (fact, value) => s"\t($fact,$value)" }.mkString("\n") - }" + }\n})" } - override def equals(obj: Any): Boolean = { - obj match { + override def equals(other: Any): Boolean = { + other match { case basicIDEProperty: BasicIDEProperty[?, ?] => - results == basicIDEProperty.results && propertyMetaInformation == basicIDEProperty.propertyMetaInformation + key == basicIDEProperty.key && results == basicIDEProperty.results case _ => false } } override def hashCode(): Int = { - results.hashCode() * 31 + propertyMetaInformation.hashCode() + key.hashCode() * 31 + results.hashCode() } } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala index 420dd325cf..05686b29de 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala @@ -11,9 +11,19 @@ import org.opalj.ide.problem.IDEValue trait IDEPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue] extends PropertyMetaInformation { override type Self = BasicIDEProperty[Fact, Value] + /** + * A property meta information corresponding to this one but used for the actual/underlying IDE analysis + */ + private[ide] val backingPropertyMetaInformation: IDERawPropertyMetaInformation[Fact, Value] = + new IDERawPropertyMetaInformation[Fact, Value](this) + + /** + * Create a property + * @param results the results the property should represent + */ def createProperty( results: collection.Set[(Fact, Value)] ): IDEProperty[Fact, Value] = { - new BasicIDEProperty(results, this) + new BasicIDEProperty(key, results) } } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala new file mode 100644 index 0000000000..e1f07a508c --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala @@ -0,0 +1,37 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.integration + +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyKey +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEValue + +/** + * Class representing a property that is directly created by an IDE analysis. + * @param key the property key (very likely taken from an [[IDERawPropertyMetaInformation]] instance) + * @param results the raw results produced by the analysis + */ +class IDERawProperty[Fact <: IDEFact, Value <: IDEValue]( + val key: PropertyKey[IDERawProperty[Fact, Value]], + val results: collection.Set[(Fact, Value)] +) extends Property { + override type Self = IDERawProperty[Fact, Value] + + override def toString: String = { + s"IDERawProperty(${PropertyKey.name(key)}, {\n${ + results.map { case (fact, value) => s"\t($fact,$value)" }.mkString("\n") + }\n})" + } + + override def equals(other: Any): Boolean = { + other match { + case ideRawProperty: IDERawProperty[?, ?] => + key == ideRawProperty.key && results == ideRawProperty.results + case _ => false + } + } + + override def hashCode(): Int = { + key.hashCode() * 31 + results.hashCode() + } +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala new file mode 100644 index 0000000000..e535847488 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala @@ -0,0 +1,27 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.integration + +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.PropertyMetaInformation +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEValue + +/** + * Class for property meta information for properties that are created by IDE analyses directly (also called 'raw'). + * The property type is fixed to [[IDERawProperty]]. + * @param propertyMetaInformation the property meta information this object should be backing + */ +final class IDERawPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue]( + propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] +) extends PropertyMetaInformation { + override type Self = IDERawProperty[Fact, Value] + + /** + * The used property key, based on [[propertyMetaInformation]] + */ + private val backingPropertyKey: PropertyKey[IDERawProperty[Fact, Value]] = { + PropertyKey.create(s"${PropertyKey.name(propertyMetaInformation.key)}_Raw") + } + + override def key: PropertyKey[IDERawProperty[Fact, Value]] = backingPropertyKey +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 704d70f3f7..0acd481754 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -20,8 +20,8 @@ import org.opalj.fpcf.SomePartialResult import org.opalj.ide.ConfigKeyDebugLog import org.opalj.ide.ConfigKeyTraceLog import org.opalj.ide.FrameworkName -import org.opalj.ide.integration.IDEProperty import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.ide.integration.IDERawProperty import org.opalj.ide.problem.AllTopEdgeFunction import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.EdgeFunctionResult @@ -395,9 +395,10 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val (resultsByStatement, resultsForExit) = s.collectResults(callable) val propertiesByStatement = resultsByStatement.map { case (stmt, results) => - (stmt, propertyMetaInformation.createProperty(results)) + (stmt, new IDERawProperty(propertyMetaInformation.backingPropertyMetaInformation.key, results)) } - val propertyForExit = propertyMetaInformation.createProperty(resultsForExit) + val propertyForExit = + new IDERawProperty(propertyMetaInformation.backingPropertyMetaInformation.key, resultsForExit) logDebug("finished creation of properties") @@ -412,16 +413,14 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent ) } else { logDebug("creating interim results") - Results( - Iterable( - InterimPartialResult( - s.getDependees.toSet, - onDependeeUpdateContinuation(callable) - ), + InterimPartialResult( + propertiesByStatement.map { + case (stmt, property) => createPartialResult((callable, stmt), property) + } ++ Iterable( createPartialResult(callable, propertyForExit) - ) ++ propertiesByStatement.map { case (stmt, property) => - createPartialResult((callable, stmt), property) - } + ), + s.getDependees.toSet, + onDependeeUpdateContinuation(callable) ) } } @@ -442,10 +441,10 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent createResult(callable) } - private def createPartialResult(entity: Entity, property: IDEProperty[Fact, Value]): SomePartialResult = { + private def createPartialResult(entity: Entity, property: IDERawProperty[Fact, Value]): SomePartialResult = { PartialResult( entity, - propertyMetaInformation.key, + propertyMetaInformation.backingPropertyMetaInformation.key, { (eOptionP: SomeEOptionP) => if (eOptionP.hasUBP && eOptionP.ub == property) { None diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala index 88937ed7e4..61986d9adb 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala @@ -22,12 +22,10 @@ import org.opalj.ide.problem.IDEValue * The [[IDEAnalysis]] solver runs on callables only and additionally produces results for each statement of that * callable. This proxy analysis reduces all analysis requests to the callable and then forward it to the actual IDE * solver. - * @param backingPropertyKey the property key used for the backing analysis */ -private[ide] class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( - val project: SomeProject, - val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value], - val backingPropertyKey: PropertyKey[IDEPropertyMetaInformation[Fact, Value]#Self] +class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( + val project: SomeProject, + val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] ) extends FPCFAnalysis { /** * @param entity either only a callable or a pair of callable and statement that should be analyzed (if no statement @@ -45,7 +43,7 @@ private[ide] class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statemen } private def createCoarseResult(callable: Callable, stmt: Option[Statement]): ProperPropertyComputationResult = { - val eOptionP = propertyStore(callable, backingPropertyKey) + val eOptionP = propertyStore(callable, propertyMetaInformation.backingPropertyMetaInformation.key) eOptionP match { case _: EPK[Callable, ?] => // In this case, the analysis has not been called yet @@ -67,12 +65,24 @@ private[ide] class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statemen } private def createFineResult(callable: Callable, stmt: Option[Statement]): ProperPropertyComputationResult = { - val eOptionP = propertyStore((callable, stmt.get), backingPropertyKey) - if (eOptionP.isFinal) { - Result(eOptionP.asFinal) + val eOptionP = propertyStore( + stmt match { + case Some(statement) => (callable, statement) + case None => callable + }, + propertyMetaInformation.backingPropertyMetaInformation.key + ) + if (eOptionP.isEPK) { + InterimPartialResult( + immutable.Set(eOptionP), + onDependeeUpdateContinuationFine(callable, stmt) + ) + } else if (eOptionP.isFinal) { + Result(eOptionP.e, propertyMetaInformation.createProperty(eOptionP.ub.results)) } else if (eOptionP.hasUBP) { - InterimResult( - eOptionP.asInterim, + InterimResult.forUB( + eOptionP.e, + propertyMetaInformation.createProperty(eOptionP.ub.results), immutable.Set(eOptionP), onDependeeUpdateContinuationFine(callable, stmt) ) From 6d6d8afcbd6cc266403de155115518fe93c65e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 6 Sep 2024 18:44:44 +0200 Subject: [PATCH 060/167] Add eager scheduler variant of IDEAnalysisProxy --- .../ide/lcp_on_fields/LCPOnFieldsTests.scala | 6 +-- .../LinearConstantPropagationTests.scala | 17 +++---- ...la => BaseIDEAnalysisProxyScheduler.scala} | 26 +++-------- .../EagerIDEAnalysisProxyScheduler.scala | 44 +++++++++++++++++++ .../IDERawPropertyMetaInformation.scala | 2 +- .../LazyIDEAnalysisProxyScheduler.scala | 34 ++++++++++++++ 6 files changed, 95 insertions(+), 34 deletions(-) rename OPAL/ide/src/main/scala/org/opalj/ide/integration/{IDEAnalysisProxyScheduler.scala => BaseIDEAnalysisProxyScheduler.scala} (55%) create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala index f2b3a3df49..3b1f01ba70 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala @@ -3,7 +3,7 @@ package org.opalj.fpcf.ide.lcp_on_fields import org.opalj.fpcf.ide.IDEPropertiesTest import org.opalj.fpcf.properties.lcp_on_fields.LCPOnFieldsProperty -import org.opalj.ide.integration.IDEAnalysisProxyScheduler +import org.opalj.ide.integration.LazyIDEAnalysisProxyScheduler class LCPOnFieldsTests extends IDEPropertiesTest { override def fixtureProjectPackage: List[String] = { @@ -14,8 +14,8 @@ class LCPOnFieldsTests extends IDEPropertiesTest { val testContext = executeAnalyses(Set( LinearConstantPropagationAnalysisSchedulerExtended, LCPOnFieldsAnalysisScheduler, - new IDEAnalysisProxyScheduler(LinearConstantPropagationAnalysisSchedulerExtended), - new IDEAnalysisProxyScheduler(LCPOnFieldsAnalysisScheduler) + new LazyIDEAnalysisProxyScheduler(LinearConstantPropagationAnalysisSchedulerExtended), + new LazyIDEAnalysisProxyScheduler(LCPOnFieldsAnalysisScheduler) )) val entryPoints = methodsWithAnnotations(testContext.project) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala index ad0659eda2..fa800ed90e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala @@ -1,9 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.ide.linear_constant_propagation +import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.ide.IDEPropertiesTest import org.opalj.fpcf.properties.linear_constant_propagation.LinearConstantPropagationProperty -import org.opalj.ide.integration.IDEAnalysisProxyScheduler +import org.opalj.ide.integration.EagerIDEAnalysisProxyScheduler class LinearConstantPropagationTests extends IDEPropertiesTest { override def fixtureProjectPackage: List[String] = { @@ -13,18 +14,12 @@ class LinearConstantPropagationTests extends IDEPropertiesTest { describe("Execute the o.o.t.f.a.i.i.l.LinearConstantPropagationAnalysis") { val testContext = executeAnalyses(Set( LinearConstantPropagationAnalysisScheduler, - new IDEAnalysisProxyScheduler(LinearConstantPropagationAnalysisScheduler) + new EagerIDEAnalysisProxyScheduler( + LinearConstantPropagationAnalysisScheduler, + { (project: SomeProject) => methodsWithAnnotations(project).map(_._1) } + ) )) - methodsWithAnnotations(testContext.project) - .foreach { case (method, _, _) => - testContext.propertyStore.force( - method, - LinearConstantPropagationAnalysisScheduler.propertyMetaInformation.key - ) - } - - testContext.propertyStore.waitOnPhaseCompletion() testContext.propertyStore.shutdown() validateProperties( diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala similarity index 55% rename from OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisProxyScheduler.scala rename to OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala index 85fb39402e..5c02035a6a 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala @@ -6,7 +6,7 @@ import scala.collection.immutable import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler +import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.fpcf.Entity import org.opalj.fpcf.PropertyBounds @@ -16,18 +16,15 @@ import org.opalj.ide.problem.IDEValue import org.opalj.ide.solver.IDEAnalysisProxy /** - * A scheduler to schedule the proxy analysis that is used to access the IDE analysis results + * Base scheduler to schedule the proxy analysis that is used to access the IDE analysis results */ -class IDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( - propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] -) extends FPCFLazyAnalysisScheduler { - override final type InitializationData = IDEAnalysisProxy[Fact, Value, Statement, Callable] +trait BaseIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity] + extends FPCFAnalysisScheduler { + val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] - def this(ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable]) = { - this(ideAnalysisScheduler.propertyMetaInformation) - } + override type InitializationData = IDEAnalysisProxy[Fact, Value, Statement, Callable] - override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(propertyMetaInformation)) + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty override def requiredProjectInformation: ProjectInformationKeys = Seq(PropertyStoreKey) @@ -40,15 +37,6 @@ class IDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, C override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} - override def register( - project: SomeProject, - propertyStore: PropertyStore, - analysis: IDEAnalysisProxy[Fact, Value, Statement, Callable] - ): FPCFAnalysis = { - propertyStore.registerLazyPropertyComputation(propertyMetaInformation.key, analysis.proxyAnalysis) - analysis - } - override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} override def afterPhaseCompletion(p: SomeProject, ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala new file mode 100644 index 0000000000..1c101e6604 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala @@ -0,0 +1,44 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.integration + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.FPCFEagerAnalysisScheduler +import org.opalj.fpcf.Entity +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyStore +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEValue +import org.opalj.ide.solver.IDEAnalysisProxy + +/** + * A scheduler to (eagerly) schedule the proxy analysis that is used to access the IDE analysis results + * @param methodProvider for which methods the results should be computed eagerly + */ +class EagerIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( + val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value], + methodProvider: SomeProject => Iterable[Method] = { project => project.allMethodsWithBody } +) extends BaseIDEAnalysisProxyScheduler[Fact, Value, Statement, Callable] with FPCFEagerAnalysisScheduler { + def this(ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable]) = { + this(ideAnalysisScheduler.propertyMetaInformation) + } + + def this( + ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable], + methodProvider: SomeProject => Iterable[Method] + ) = { + this(ideAnalysisScheduler.propertyMetaInformation, methodProvider) + } + + override def derivesEagerly: Set[PropertyBounds] = Set(PropertyBounds.ub(propertyMetaInformation)) + + override def start( + project: SomeProject, + propertyStore: PropertyStore, + analysis: IDEAnalysisProxy[Fact, Value, Statement, Callable] + ): FPCFAnalysis = { + propertyStore.scheduleEagerComputationsForEntities(methodProvider(project))(analysis.proxyAnalysis) + analysis + } +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala index e535847488..0715693fad 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala @@ -19,7 +19,7 @@ final class IDERawPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue]( /** * The used property key, based on [[propertyMetaInformation]] */ - private val backingPropertyKey: PropertyKey[IDERawProperty[Fact, Value]] = { + private lazy val backingPropertyKey: PropertyKey[IDERawProperty[Fact, Value]] = { PropertyKey.create(s"${PropertyKey.name(propertyMetaInformation.key)}_Raw") } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala new file mode 100644 index 0000000000..2f718934b2 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala @@ -0,0 +1,34 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.integration + +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler +import org.opalj.fpcf.Entity +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyStore +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEValue +import org.opalj.ide.solver.IDEAnalysisProxy + +/** + * A scheduler to (lazily) schedule the proxy analysis that is used to access the IDE analysis results + */ +class LazyIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( + val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] +) extends BaseIDEAnalysisProxyScheduler[Fact, Value, Statement, Callable] with FPCFLazyAnalysisScheduler { + def this(ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable]) = { + this(ideAnalysisScheduler.propertyMetaInformation) + } + + override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(propertyMetaInformation)) + + override def register( + project: SomeProject, + propertyStore: PropertyStore, + analysis: IDEAnalysisProxy[Fact, Value, Statement, Callable] + ): FPCFAnalysis = { + propertyStore.registerLazyPropertyComputation(propertyMetaInformation.key, analysis.proxyAnalysis) + analysis + } +} From c0910f904773a2d44676351b2c69b3b797bdecce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 9 Sep 2024 10:28:05 +0200 Subject: [PATCH 061/167] Fix LCPOnFieldsTests to trigger property computation in phase When triggering the computation after executeAnalyses() the properties will be computed correctly but won't get finalized as the scheduler expects all property computation to happen inside the phase. --- .../ide/lcp_on_fields/LCPOnFieldsTests.scala | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala index 3b1f01ba70..565e358412 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala @@ -1,9 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.ide.lcp_on_fields +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.ide.IDEPropertiesTest import org.opalj.fpcf.properties.lcp_on_fields.LCPOnFieldsProperty import org.opalj.ide.integration.LazyIDEAnalysisProxyScheduler +import org.opalj.tac.fpcf.analyses.ide.linear_constant_propagation.LCPOnFieldsAnalysisScheduler class LCPOnFieldsTests extends IDEPropertiesTest { override def fixtureProjectPackage: List[String] = { @@ -15,22 +18,22 @@ class LCPOnFieldsTests extends IDEPropertiesTest { LinearConstantPropagationAnalysisSchedulerExtended, LCPOnFieldsAnalysisScheduler, new LazyIDEAnalysisProxyScheduler(LinearConstantPropagationAnalysisSchedulerExtended), - new LazyIDEAnalysisProxyScheduler(LCPOnFieldsAnalysisScheduler) + new LazyIDEAnalysisProxyScheduler(LCPOnFieldsAnalysisScheduler) { + override def afterPhaseScheduling(propertyStore: PropertyStore, analysis: FPCFAnalysis): Unit = { + val entryPoints = methodsWithAnnotations(analysis.project) + entryPoints.foreach { case (method, _, _) => + propertyStore.force(method, LCPOnFieldsAnalysisScheduler.propertyMetaInformation.key) + } + } + } )) - val entryPoints = methodsWithAnnotations(testContext.project) - entryPoints.foreach { case (method, _, _) => - testContext.propertyStore.force(method, LCPOnFieldsAnalysisScheduler.propertyMetaInformation.key) - } - - testContext.propertyStore.waitOnPhaseCompletion() testContext.propertyStore.shutdown() validateProperties( testContext, methodsWithAnnotations(testContext.project), - Set(LCPOnFieldsProperty.KEY), - failOnInterimResults = false + Set(LCPOnFieldsProperty.KEY) ) } } From e7e718a7c5e33482b732bf3bab29563e783bfc3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 9 Sep 2024 10:38:55 +0200 Subject: [PATCH 062/167] Revert "Add parameter to general PropertiesTest to allow non-final results at the end" This reverts commit 7aa00ba3b2e5b21db915e84e88425c43738680a1. --- .../scala/org/opalj/fpcf/PropertiesTest.scala | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala index ca5dbf6f76..fef3b9b5d3 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala @@ -180,7 +180,6 @@ abstract class PropertiesTest extends AnyFunSpec with Matchers { * @param eas An iterator over the relevant entities along with the found annotations. * @param propertyKinds The kinds of properties (as specified by the annotations) that are * to be tested. - * @param failOnInterimResults Whether to fail when seeing a non-final result or to accept (and validate) it */ def validateProperties( context: TestContext, @@ -188,8 +187,7 @@ abstract class PropertiesTest extends AnyFunSpec with Matchers { Entity, /*the processed annotation*/ String => String /* a String identifying the entity */, Iterable[AnnotationLike] )], - propertyKinds: Set[String], - failOnInterimResults: Boolean = true + propertyKinds: Set[String] ): Unit = { val TestContext(p: Project[URL], ps: PropertyStore, as: List[FPCFAnalysis]) = context val ats = @@ -209,20 +207,12 @@ abstract class PropertiesTest extends AnyFunSpec with Matchers { it(entityIdentifier(s"$annotationTypeName")) { info(s"validator: " + matcherClass.toString.substring(32)) val epss = ps.properties(e).toIndexedSeq - if (failOnInterimResults) { - val nonFinalPSs = epss.filter(_.isRefinable) - assert( - nonFinalPSs.isEmpty, - nonFinalPSs.mkString("some epss are not final:\n\t", "\n\t", "\n") - ) - } - val properties = epss.map { - case FinalP(p) => p - case eps @ InterimUBP(ub) if eps.hasUBP => ub - case eps @ InterimLBP(lb) if eps.hasLBP => lb - case eps => - throw new IllegalStateException(s"Unable to extract property from $eps!") - } + val nonFinalPSs = epss.filter(_.isRefinable) + assert( + nonFinalPSs.isEmpty, + nonFinalPSs.mkString("some epss are not final:\n\t", "\n\t", "\n") + ) + val properties = epss.map(_.toFinalEP.p) matcher.validateProperty(p, ats, e, annotation, properties) match { case Some(error: String) => val propertiesAsStrings = properties.map(_.toString) From b4e7ae77f145e53834c4ac87681d83d7537ed96f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 9 Sep 2024 11:23:12 +0200 Subject: [PATCH 063/167] Apply formatter --- .../LinearConstantPropagationAnalysisSchedulerExtended.scala | 5 ++++- .../LinearConstantPropagationAnalysisScheduler.scala | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala index e8d922de5f..34b99b529e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala @@ -24,7 +24,10 @@ import org.opalj.tac.fpcf.properties.TACAI object LinearConstantPropagationAnalysisSchedulerExtended extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] { - override def propertyMetaInformation: IDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] = + override def propertyMetaInformation: IDEPropertyMetaInformation[ + LinearConstantPropagationFact, + LinearConstantPropagationValue + ] = LinearConstantPropagationPropertyMetaInformation override def requiredProjectInformation: ProjectInformationKeys = diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala index 20f22e6c83..0bea970779 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala @@ -23,7 +23,10 @@ import org.opalj.tac.fpcf.properties.TACAI object LinearConstantPropagationAnalysisScheduler extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] { - override def propertyMetaInformation: IDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] = + override def propertyMetaInformation: IDEPropertyMetaInformation[ + LinearConstantPropagationFact, + LinearConstantPropagationValue + ] = LinearConstantPropagationPropertyMetaInformation override def requiredProjectInformation: ProjectInformationKeys = From 5e6470b72283bc582ed9c5faeb75d97f3029281c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 9 Sep 2024 13:22:42 +0200 Subject: [PATCH 064/167] Refactor callable passing in IDEAnalysis --- .../org/opalj/ide/solver/IDEAnalysis.scala | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 0acd481754..e384f99e5b 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -74,7 +74,9 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent /** * Container class for simpler interaction and passing of the 'shared' data */ - private class State { + private class State( + val targetCallable: Callable + ) { /** * The work list for paths used in P1 */ @@ -324,21 +326,21 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent def performAnalysis(callable: Callable): ProperPropertyComputationResult = { logDebug(s"performing ${getClass.getSimpleName} for $callable") - implicit val state: State = new State + implicit val state: State = new State(callable) - performPhase1(callable) - performPhase2(callable) + performPhase1() + performPhase2() - createResult(callable) + createResult() } /** * @return whether the phase is finished or has to be continued once the dependees are resolved */ - private def performPhase1(callable: Callable)(implicit s: State): Boolean = { + private def performPhase1()(implicit s: State): Boolean = { logDebug("starting phase 1") - seedPhase1(callable) + seedPhase1() processPathWorkList() if (s.areDependeesEmpty) { @@ -373,7 +375,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent /** * Perform phase 2 from scratch */ - private def performPhase2(callable: Callable)(implicit s: State): Unit = { + private def performPhase2()(implicit s: State): Unit = { logDebug("starting phase 2") // TODO (IDE) PHASE 2 IS PERFORMED ALSO FOR INTERIM RESULTS TO MAKE CYCLIC ANALYSES POSSIBLE @@ -382,17 +384,19 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // - DO WE NEED TO RERUN IT FROM SCRATCH EACH TIME OR IS THERE AN INCREMENTAL SOLUTION? s.clearValues() - seedPhase2(callable) + seedPhase2() computeValues() logDebug("finished phase 2") } - private def createResult(callable: Callable)( + private def createResult()( implicit s: State ): ProperPropertyComputationResult = { logDebug("starting creation of properties") + val callable = s.targetCallable + val (resultsByStatement, resultsForExit) = s.collectResults(callable) val propertiesByStatement = resultsByStatement.map { case (stmt, results) => (stmt, new IDERawProperty(propertyMetaInformation.backingPropertyMetaInformation.key, results)) @@ -420,12 +424,12 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent createPartialResult(callable, propertyForExit) ), s.getDependees.toSet, - onDependeeUpdateContinuation(callable) + onDependeeUpdateContinuation ) } } - private def onDependeeUpdateContinuation(callable: Callable)(eps: SomeEPS)( + private def onDependeeUpdateContinuation(eps: SomeEPS)( implicit s: State ): ProperPropertyComputationResult = { // Get and remove all continuations that are remembered for the EPS @@ -436,9 +440,9 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // The continuations can have enqueued paths to the path work list continuePhase1() - performPhase2(callable) + performPhase2() - createResult(callable) + createResult() } private def createPartialResult(entity: Entity, property: IDERawProperty[Fact, Value]): SomePartialResult = { @@ -455,7 +459,9 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent ) } - private def seedPhase1(callable: Callable)(implicit s: State): Unit = { + private def seedPhase1()(implicit s: State): Unit = { + val callable = s.targetCallable + s.rememberCallable(callable) // IDE P1 lines 5 - 6 @@ -693,7 +699,9 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } } - private def seedPhase2(callable: Callable)(implicit s: State): Unit = { + private def seedPhase2()(implicit s: State): Unit = { + val callable = s.targetCallable + // IDE P2 lines 2 - 3 icfg.getStartStatements(callable).foreach { stmt => val node = (stmt, problem.nullFact) From f1ab93815e0560bad8d2a640c6afab754a0ede8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 13 Sep 2024 13:40:13 +0200 Subject: [PATCH 065/167] Extend linear constant propagation to deal with boolean operators --- .../problem/LinearConstantPropagationProblem.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 9ff6b8a998..a3194f0936 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -315,6 +315,13 @@ class LinearConstantPropagationProblem(project: SomeProject) case (None, None, _) => VariableValueEdgeFunction + case (_, _, BinaryArithmeticOperators.And) => + VariableValueEdgeFunction + case (_, _, BinaryArithmeticOperators.Or) => + VariableValueEdgeFunction + case (_, _, BinaryArithmeticOperators.XOr) => + VariableValueEdgeFunction + case (_, _, op) => throw new UnsupportedOperationException(s"Operator $op is not implemented!") } From d69696907dcb3e9fcc6215ac3d9e5996fa8566db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 13 Sep 2024 13:43:39 +0200 Subject: [PATCH 066/167] Fix LCP on fields edge function computation --- .../lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala index 5df68c3f22..3f8efe37b8 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -17,10 +17,12 @@ case class ObjectEdgeFunction( ) extends EdgeFunction[LCPOnFieldsValue] { override def compute(sourceValue: LCPOnFieldsValue): LCPOnFieldsValue = sourceValue match { - case UnknownValue => UnknownValue - case ObjectValue(_) => + case UnknownValue => UnknownValue + case ObjectValue(values2) => ObjectValue((values -- values2.keys) ++ values2) + case VariableValue => ObjectValue(values) + + case _ => throw new UnsupportedOperationException(s"Computing $this for $sourceValue is not implemented!") - case VariableValue => ObjectValue(values) } override def composeWith(secondEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = From b2240114e2c1c3560cf4a96159ea91c38530870e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 13 Sep 2024 13:46:25 +0200 Subject: [PATCH 067/167] Refactor property matchers --- .../lcp_on_fields/LCPOnFieldsMatcher.scala | 31 +++++++++++-------- .../LinearConstantPropagationMatcher.scala | 23 +++++++++----- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala index 861a74c2cc..38e48c5592 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala @@ -7,8 +7,8 @@ import org.opalj.br.analyses.Project import org.opalj.fpcf.Property import org.opalj.fpcf.properties.AbstractRepeatablePropertyMatcher import org.opalj.ide.integration.BasicIDEProperty -import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.{problem => LCPOnFieldsProblem} -import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.{problem => LCPProblem} +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation /** * Matcher for [[ObjectValue]] and [[ObjectValues]] annotations @@ -68,25 +68,26 @@ class ObjectValueMatcher extends AbstractRepeatablePropertyMatcher { if (properties.exists { case property: BasicIDEProperty[?, ?] => property.results.exists { - case (f: LCPOnFieldsProblem.AbstractObjectFact, LCPOnFieldsProblem.ObjectValue(values)) => + case (f: lcp_on_fields.problem.AbstractObjectFact, lcp_on_fields.problem.ObjectValue(values)) => expectedVariableName == f.name && expectedConstantValues.forall { case (fieldName, value) => values.get(fieldName) match { - case Some(LCPProblem.ConstantValue(c)) => value == c - case _ => false + case Some(linear_constant_propagation.problem.ConstantValue(c)) => + value == c + case _ => false } } && expectedVariableValues.forall { fieldName => values.get(fieldName) match { - case Some(LCPProblem.VariableValue) => true - case _ => false + case Some(linear_constant_propagation.problem.VariableValue) => true + case _ => false } } && expectedUnknownValues.forall { fieldName => values.get(fieldName) match { - case Some(LCPProblem.UnknownValue) => true - case _ => false + case Some(linear_constant_propagation.problem.UnknownValue) => true + case _ => false } } @@ -100,12 +101,16 @@ class ObjectValueMatcher extends AbstractRepeatablePropertyMatcher { } else { val expectedValues = expectedConstantValues - .map { case (fieldName, c) => fieldName -> LCPProblem.ConstantValue(c) } - .concat(expectedVariableValues.map { fieldName => fieldName -> LCPProblem.VariableValue }) - .concat(expectedUnknownValues.map { fieldName => fieldName -> LCPProblem.UnknownValue }) + .map { case (fieldName, c) => fieldName -> linear_constant_propagation.problem.ConstantValue(c) } + .concat(expectedVariableValues.map { fieldName => + fieldName -> linear_constant_propagation.problem.VariableValue + }) + .concat(expectedUnknownValues.map { fieldName => + fieldName -> linear_constant_propagation.problem.UnknownValue + }) .toMap Some( - s"Result should contain (${LCPOnFieldsProblem.ObjectFact(expectedVariableName, 0)}, ${LCPOnFieldsProblem.ObjectValue(expectedValues)})" + s"Result should contain (${lcp_on_fields.problem.ObjectFact(expectedVariableName, 0)}, ${lcp_on_fields.problem.ObjectValue(expectedValues)})" ) } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala index 498ea5de6f..ec682fb430 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala @@ -7,7 +7,7 @@ import org.opalj.br.analyses.Project import org.opalj.fpcf.Property import org.opalj.fpcf.properties.AbstractRepeatablePropertyMatcher import org.opalj.ide.integration.BasicIDEProperty -import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.{problem => LCPProblem} +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation /** * Matcher for [[ConstantValue]] and [[ConstantValues]] annotations @@ -33,7 +33,10 @@ class ConstantValueMatcher extends AbstractRepeatablePropertyMatcher { if (properties.exists { case property: BasicIDEProperty[?, ?] => property.results.exists { - case (LCPProblem.VariableFact(name, _), LCPProblem.ConstantValue(value)) => + case ( + linear_constant_propagation.problem.VariableFact(name, _), + linear_constant_propagation.problem.ConstantValue(value) + ) => expectedVariableName == name && expectedVariableValue == value case _ => false @@ -45,7 +48,7 @@ class ConstantValueMatcher extends AbstractRepeatablePropertyMatcher { None } else { Some( - s"Result should contain (${LCPProblem.VariableFact(expectedVariableName, 0)}, ${LCPProblem.ConstantValue(expectedVariableValue)})!" + s"Result should contain (${linear_constant_propagation.problem.VariableFact(expectedVariableName, 0)}, ${linear_constant_propagation.problem.ConstantValue(expectedVariableValue)})!" ) } } @@ -73,7 +76,10 @@ class VariableValueMatcher extends AbstractRepeatablePropertyMatcher { if (properties.exists { case property: BasicIDEProperty[?, ?] => property.results.exists { - case (LCPProblem.VariableFact(name, _), LCPProblem.VariableValue) => + case ( + linear_constant_propagation.problem.VariableFact(name, _), + linear_constant_propagation.problem.VariableValue + ) => expectedVariableName == name case _ => false @@ -85,7 +91,7 @@ class VariableValueMatcher extends AbstractRepeatablePropertyMatcher { None } else { Some( - s"Result should contain (${LCPProblem.VariableFact(expectedVariableName, 0)}, ${LCPProblem.VariableValue})!" + s"Result should contain (${linear_constant_propagation.problem.VariableFact(expectedVariableName, 0)}, ${linear_constant_propagation.problem.VariableValue})!" ) } } @@ -113,7 +119,10 @@ class UnknownValueMatcher extends AbstractRepeatablePropertyMatcher { if (properties.exists { case property: BasicIDEProperty[?, ?] => property.results.exists { - case (LCPProblem.VariableFact(name, _), LCPProblem.UnknownValue) => + case ( + linear_constant_propagation.problem.VariableFact(name, _), + linear_constant_propagation.problem.UnknownValue + ) => expectedVariableName == name case _ => false @@ -125,7 +134,7 @@ class UnknownValueMatcher extends AbstractRepeatablePropertyMatcher { None } else { Some( - s"Result should contain (${LCPProblem.VariableFact(expectedVariableName, 0)}, ${LCPProblem.UnknownValue})!" + s"Result should contain (${linear_constant_propagation.problem.VariableFact(expectedVariableName, 0)}, ${linear_constant_propagation.problem.UnknownValue})!" ) } } From 45193f77dfb1f5115d5fa8b8b0c0caae17dccf34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 13 Sep 2024 14:18:30 +0200 Subject: [PATCH 068/167] Fix IDEAnalysis final result creation --- .../src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index e384f99e5b..cad8e91436 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -409,11 +409,11 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent if (s.areDependeesEmpty) { logDebug("creating final results") Results( - Iterable( - Result(callable, propertyForExit) - ) ++ propertiesByStatement.map { case (stmt, property) => + propertiesByStatement.map { case (stmt, property) => Result((callable, stmt), property) - } + } ++ Iterable( + Result(callable, propertyForExit) + ) ) } else { logDebug("creating interim results") From e05fcf2adb94da8b441b10c50bcef703dd46b6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 13 Sep 2024 14:24:36 +0200 Subject: [PATCH 069/167] Add facts and values to LCP on fields for array detection --- .../problem/LCPOnFieldsFact.scala | 39 ++++++++++++++++++- .../problem/LCPOnFieldsValue.scala | 10 +++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala index 356b6463b1..7184a5c48e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala @@ -14,9 +14,9 @@ trait LCPOnFieldsFact extends IDEFact case object NullFact extends LCPOnFieldsFact /** - * Type for + * Common type for different types of entities */ -trait AbstractObjectFact extends LCPOnFieldsFact { +trait AbstractEntityFact extends LCPOnFieldsFact { /** * The name of the variable (e.g. `lv0`) */ @@ -25,7 +25,12 @@ trait AbstractObjectFact extends LCPOnFieldsFact { * Where the variable is defined (used to uniquely identify a variable/variable fact) */ val definedAtIndex: Int +} +/** + * Type for object facts + */ +trait AbstractObjectFact extends AbstractEntityFact { def toObjectFact: ObjectFact = ObjectFact(name, definedAtIndex) } @@ -52,3 +57,33 @@ case class NewObjectFact(name: String, definedAtIndex: Int) extends AbstractObje case class PutFieldFact(name: String, definedAtIndex: Int, fieldName: String) extends AbstractObjectFact { override def toString: String = s"PutFieldFact($name, $fieldName)" } + +/** + * Type for array facts + */ +trait AbstractArrayFact extends AbstractEntityFact { + def toArrayFact: ArrayFact = ArrayFact(name, definedAtIndex) +} + +/** + * Fact representing a seen array variable + */ +case class ArrayFact(name: String, definedAtIndex: Int) extends AbstractArrayFact { + override def toArrayFact: ArrayFact = this + + override def toString: String = s"ArrayFact($name)" +} + +/** + * Fact representing a seen array variable and modeling that it gets initialized + */ +case class NewArrayFact(name: String, definedAtIndex: Int) extends AbstractArrayFact { + override def toString: String = s"NewArrayFact($name)" +} + +/** + * Fact representing a seen array variable and modeling that one of its elements gets written + */ +case class PutElementFact(name: String, definedAtIndex: Int) extends AbstractArrayFact { + override def toString: String = s"PutElementFact($name)" +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala index 94b4bfc542..128797531a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala @@ -23,6 +23,16 @@ case class ObjectValue(values: immutable.Map[String, LinearConstantPropagationVa override def toString: String = s"ObjectValue(${values.mkString(", ")})" } +/** + * Value representing the state of an array + */ +case class ArrayValue( + initValue: LinearConstantPropagationValue, + elements: immutable.Map[Int, LinearConstantPropagationValue] +) extends LCPOnFieldsValue { + override def toString: String = s"ArrayValue($initValue, ${elements.mkString(", ")})" +} + /** * Value is variable (not really used currently, mainly for completeness) */ From 3ba7a8bbef1e759e490223625a73bd044621b005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 13 Sep 2024 14:28:39 +0200 Subject: [PATCH 070/167] Extend test annotations and matcher of LCP on fields --- .../properties/lcp_on_fields/ArrayValue.java | 36 ++++++ .../properties/lcp_on_fields/ArrayValues.java | 17 +++ .../lcp_on_fields/ConstantArrayElement.java | 21 ++++ .../lcp_on_fields/UnknownArrayElement.java | 18 +++ .../lcp_on_fields/VariableArrayElement.java | 18 +++ .../lcp_on_fields/LCPOnFieldsMatcher.scala | 115 ++++++++++++++++++ 6 files changed, 225 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValue.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValues.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ConstantArrayElement.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownArrayElement.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableArrayElement.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValue.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValue.java new file mode 100644 index 0000000000..8a8268c3db --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValue.java @@ -0,0 +1,36 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.lcp_on_fields; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.*; + +/** + * Annotation to state that an array has been identified and has certain constant and non-constant elements + */ +@PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = ArrayValueMatcher.class) +@Repeatable(ArrayValues.class) +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface ArrayValue { + /** + * The name of the variable the array is stored in + */ + String variable(); + + /** + * The constant elements of the array + */ + ConstantArrayElement[] constantElements() default {}; + + /** + * The non-constant elements of the array + */ + VariableArrayElement[] variableElements() default {}; + + /** + * The elements of the array with unknown value + */ + UnknownArrayElement[] unknownElements() default {}; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValues.java new file mode 100644 index 0000000000..e1c9280df2 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValues.java @@ -0,0 +1,17 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.lcp_on_fields; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.*; + +/** + * Container annotation for {@link ArrayValue} annotations + */ +@PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = ArrayValueMatcher.class) +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface ArrayValues { + ArrayValue[] value(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ConstantArrayElement.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ConstantArrayElement.java new file mode 100644 index 0000000000..292975d90c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ConstantArrayElement.java @@ -0,0 +1,21 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.lcp_on_fields; + +import java.lang.annotation.*; + +/** + * Annotation to state that an array element has a constant value + */ +@Documented +@Target(ElementType.ANNOTATION_TYPE) +public @interface ConstantArrayElement { + /** + * The index of the element + */ + int index(); + + /** + * The constant value of the element + */ + int value(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownArrayElement.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownArrayElement.java new file mode 100644 index 0000000000..c6d4ee6e98 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownArrayElement.java @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.lcp_on_fields; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Annotation to state that an array elements value is unknown + */ +@Documented +@Target(ElementType.ANNOTATION_TYPE) +public @interface UnknownArrayElement { + /** + * The index of the element + */ + int index(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableArrayElement.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableArrayElement.java new file mode 100644 index 0000000000..333f0af140 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableArrayElement.java @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.lcp_on_fields; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Annotation to state that an array element has a non-constant value + */ +@Documented +@Target(ElementType.ANNOTATION_TYPE) +public @interface VariableArrayElement { + /** + * The index of the element + */ + int index(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala index 38e48c5592..4d5a1835db 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala @@ -115,3 +115,118 @@ class ObjectValueMatcher extends AbstractRepeatablePropertyMatcher { } } } + +/** + * Matcher for [[ArrayValue]] and [[ArrayValues]] annotations + */ +class ArrayValueMatcher extends AbstractRepeatablePropertyMatcher { + override val singleAnnotationType: ObjectType = + ObjectType("org/opalj/fpcf/properties/lcp_on_fields/ArrayValue") + override val containerAnnotationType: ObjectType = + ObjectType("org/opalj/fpcf/properties/lcp_on_fields/ArrayValues") + + private val constantArrayElementType = ObjectType("org/opalj/fpcf/properties/lcp_on_fields/ConstantArrayElement") + private val variableArrayElementType = ObjectType("org/opalj/fpcf/properties/lcp_on_fields/VariableArrayElement") + private val unknownArrayElementType = ObjectType("org/opalj/fpcf/properties/lcp_on_fields/UnknownArrayElement") + + override def validateSingleProperty( + p: Project[?], + as: Set[ObjectType], + entity: Any, + a: AnnotationLike, + properties: Iterable[Property] + ): Option[String] = { + val expectedVariableName = + getValue(p, singleAnnotationType, a.elementValuePairs, "variable").asStringValue.value + + val expectedConstantElements = + getValue(p, singleAnnotationType, a.elementValuePairs, "constantElements").asArrayValue.values + .map { a => + val annotation = a.asAnnotationValue.annotation + val expectedIndex = + getValue(p, constantArrayElementType, annotation.elementValuePairs, "index").asIntValue.value + val expectedValue = + getValue(p, constantArrayElementType, annotation.elementValuePairs, "value").asIntValue.value + + (expectedIndex, expectedValue) + } + + val expectedVariableElements = + getValue(p, singleAnnotationType, a.elementValuePairs, "variableElements").asArrayValue.values + .map { a => + val annotation = a.asAnnotationValue.annotation + val expectedIndex = + getValue(p, variableArrayElementType, annotation.elementValuePairs, "index").asIntValue.value + + expectedIndex + } + + val expectedUnknownElements = + getValue(p, singleAnnotationType, a.elementValuePairs, "unknownElements").asArrayValue.values + .map { a => + val annotation = a.asAnnotationValue.annotation + val expectedIndex = + getValue(p, unknownArrayElementType, annotation.elementValuePairs, "index").asIntValue.value + + expectedIndex + } + + if (properties.exists { + case property: BasicIDEProperty[?, ?] => + property.results.exists { + case ( + f: lcp_on_fields.problem.AbstractArrayFact, + lcp_on_fields.problem.ArrayValue(initValue, elements) + ) => + expectedVariableName == f.name && + expectedConstantElements.forall { + case (index, value) => + elements.get(index) match { + case Some(linear_constant_propagation.problem.ConstantValue(c)) => + value == c + case None => + initValue == linear_constant_propagation.problem.ConstantValue(value) + case _ => false + } + } && + expectedVariableElements.forall { index => + elements.get(index) match { + case Some(linear_constant_propagation.problem.VariableValue) => true + case None => + initValue == linear_constant_propagation.problem.VariableValue + case _ => false + } + } && + expectedUnknownElements.forall { index => + elements.get(index) match { + case Some(linear_constant_propagation.problem.UnknownValue) => true + case None => + initValue == linear_constant_propagation.problem.UnknownValue + case _ => false + } + } + + case _ => false + } + + case _ => false + } + ) { + None + } else { + val expectedElements = + expectedConstantElements + .map { case (index, c) => index -> linear_constant_propagation.problem.ConstantValue(c) } + .concat(expectedVariableElements.map { index => + index -> linear_constant_propagation.problem.VariableValue + }) + .concat(expectedUnknownElements.map { index => + index -> linear_constant_propagation.problem.UnknownValue + }) + .toMap + Some( + s"Result should contain (${lcp_on_fields.problem.ArrayFact(expectedVariableName, 0)}, ArrayValue(?, $expectedElements)" + ) + } + } +} From a71068989d3b909ff7cb42ce17f223dcc92ef674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 13 Sep 2024 16:10:54 +0200 Subject: [PATCH 071/167] Refactor linear constant propagation problem definition --- ...arConstantPropagationProblemExtended.scala | 109 +++-- .../LinearConstantPropagationProblem.scala | 415 ++++++++++++------ 2 files changed, 341 insertions(+), 183 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala index c36ed56aa0..294fb1c78f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala @@ -13,12 +13,12 @@ import org.opalj.ide.problem.FinalEdgeFunction import org.opalj.ide.problem.InterimEdgeFunction import org.opalj.tac.ArrayLength import org.opalj.tac.ArrayLoad -import org.opalj.tac.Expr import org.opalj.tac.GetField import org.opalj.tac.GetStatic import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.ConstantValue import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearCombinationEdgeFunction +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationProblem import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.UnknownValue @@ -32,58 +32,68 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement * fields analysis. */ class LinearConstantPropagationProblemExtended(project: SomeProject) extends LinearConstantPropagationProblem(project) { - override def getEdgeFunctionForExpression( - expr: Expr[JavaStatement.V], - source: JavaStatement + override def getNormalEdgeFunctionForArrayLength( + arrayLengthExpr: ArrayLength[JavaStatement.V] + )( + source: JavaStatement, + sourceFact: LinearConstantPropagationFact, + target: JavaStatement, + targetFact: LinearConstantPropagationFact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { - expr.astID match { - case ArrayLength.ASTID => - UnknownValueEdgeFunction - - case ArrayLoad.ASTID => - UnknownValueEdgeFunction + UnknownValueEdgeFunction + } - case GetField.ASTID => - val getFieldExpr = expr.asGetField - val objectVar = getFieldExpr.objRef.asVar - val fieldName = getFieldExpr.name + override def getNormalEdgeFunctionForArrayLoad( + arrayLoadExpr: ArrayLoad[JavaStatement.V] + )( + source: JavaStatement, + sourceFact: LinearConstantPropagationFact, + target: JavaStatement, + targetFact: LinearConstantPropagationFact + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { + UnknownValueEdgeFunction + } - val lcpOnFieldsEOptionP = - propertyStore((source.method, source), LCPOnFieldsPropertyMetaInformation.key) + override def getNormalEdgeFunctionForGetField( + getFieldExpr: GetField[JavaStatement.V] + )( + source: JavaStatement, + sourceFact: LinearConstantPropagationFact, + target: JavaStatement, + targetFact: LinearConstantPropagationFact + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { + val objectVar = getFieldExpr.objRef.asVar + val fieldName = getFieldExpr.name - /* Decide based on the current result of the LCP on fields analysis */ - lcpOnFieldsEOptionP match { - case FinalP(property) => - val value = getObjectFieldFromProperty(objectVar, fieldName)(property) - FinalEdgeFunction(value match { - case UnknownValue => UnknownValueEdgeFunction - case ConstantValue(c) => LinearCombinationEdgeFunction(0, c, lattice.top) - case VariableValue => VariableValueEdgeFunction - }) + val lcpOnFieldsEOptionP = + propertyStore((source.method, source), LCPOnFieldsPropertyMetaInformation.key) - case InterimUBP(property) => - val value = getObjectFieldFromProperty(objectVar, fieldName)(property) - value match { - case UnknownValue => - InterimEdgeFunction(UnknownValueEdgeFunction, immutable.Set(lcpOnFieldsEOptionP)) - case ConstantValue(c) => - InterimEdgeFunction( - LinearCombinationEdgeFunction(0, c, lattice.top), - immutable.Set(lcpOnFieldsEOptionP) - ) - case VariableValue => - FinalEdgeFunction(VariableValueEdgeFunction) - } + /* Decide based on the current result of the LCP on fields analysis */ + lcpOnFieldsEOptionP match { + case FinalP(property) => + val value = getObjectFieldFromProperty(objectVar, fieldName)(property) + FinalEdgeFunction(value match { + case UnknownValue => UnknownValueEdgeFunction + case ConstantValue(c) => LinearCombinationEdgeFunction(0, c, lattice.top) + case VariableValue => VariableValueEdgeFunction + }) - case _ => + case InterimUBP(property) => + val value = getObjectFieldFromProperty(objectVar, fieldName)(property) + value match { + case UnknownValue => InterimEdgeFunction(UnknownValueEdgeFunction, immutable.Set(lcpOnFieldsEOptionP)) + case ConstantValue(c) => + InterimEdgeFunction( + LinearCombinationEdgeFunction(0, c, lattice.top), + immutable.Set(lcpOnFieldsEOptionP) + ) + case VariableValue => + FinalEdgeFunction(VariableValueEdgeFunction) } - case GetStatic.ASTID => - UnknownValueEdgeFunction - - /* Unchanged behavior for all other expressions */ - case _ => super.getEdgeFunctionForExpression(expr, source) + case _ => + InterimEdgeFunction(UnknownValueEdgeFunction, immutable.Set(lcpOnFieldsEOptionP)) } } @@ -105,4 +115,15 @@ class LinearConstantPropagationProblemExtended(project: SomeProject) extends Lin lattice.meet(value, values(fieldName)) } } + + override def getNormalEdgeFunctionForGetStatic( + getStaticExpr: GetStatic + )( + source: JavaStatement, + sourceFact: LinearConstantPropagationFact, + target: JavaStatement, + targetFact: LinearConstantPropagationFact + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { + UnknownValueEdgeFunction + } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index a3194f0936..09d118c45a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -56,7 +56,7 @@ class LinearConstantPropagationProblem(project: SomeProject) source.stmt.astID match { case Assignment.ASTID => val assignment = source.stmt.asAssignment - if (isExpressionInfluencedByFact(assignment.expr, sourceFact)) { + if (isExpressionGeneratedByFact(assignment.expr)(source, sourceFact, target)) { /* Generate fact for target of assignment if the expression is influenced by the source * fact */ immutable.Set(sourceFact, VariableFact(assignment.targetVar.name, source.index)) @@ -69,77 +69,148 @@ class LinearConstantPropagationProblem(project: SomeProject) } } - protected def isExpressionInfluencedByFact( - expr: Expr[JavaStatement.V], - sourceFact: LinearConstantPropagationFact + private def isExpressionGeneratedByFact( + expr: Expr[JavaStatement.V] + )( + source: JavaStatement, + sourceFact: LinearConstantPropagationFact, + target: JavaStatement ): Boolean = { - expr.astID match { + (expr.astID match { case IntConst.ASTID => - /* Only generate fact for constants from null fact */ - sourceFact == nullFact + isIntConstExpressionGeneratedByFact(expr.asIntConst)(_, _, _) case BinaryExpr.ASTID => - val binaryExpr = expr.asBinaryExpr - val leftExpr = binaryExpr.left - val rightExpr = binaryExpr.right - - if (sourceFact == nullFact) { - /* Only generate fact by null fact for binary expressions if both subexpressions are influenced. - * This is needed for binary expressions with one constant and one variable. */ - isExpressionInfluencedByFact(leftExpr, sourceFact) && - isExpressionInfluencedByFact(rightExpr, sourceFact) - } else { - /* If source fact is not null fact, generate new fact if one subexpression is influenced by the - * source fact */ - isExpressionInfluencedByFact(leftExpr, sourceFact) || - isExpressionInfluencedByFact(rightExpr, sourceFact) - } + isBinaryExpressionGeneratedByFact(expr.asBinaryExpr)(_, _, _) case Var.ASTID => - val varExpr = expr.asVar - - val hasConstantValue = - varExpr.value match { - case intRange: DefaultIntegerRangeValues#IntegerRange => - intRange.lowerBound == intRange.upperBound - case _ => false - } - - sourceFact match { - case NullFact => - /* Generate fact by null fact for variables if it is definitely a constant */ - hasConstantValue - case VariableFact(_, definedAtIndex) => - /* Generate fact only if it is not detected as constant (by the value analysis) and the variable - * represented by the source fact is one possible initializer of the target variable */ - !hasConstantValue && varExpr.definedBy.contains(definedAtIndex) - } + isVarExpressionGeneratedByFact(expr.asVar)(_, _, _) case ArrayLength.ASTID => - /* Generate for array length expressions only by null fact */ - sourceFact == nullFact + isArrayLengthExpressionGeneratedByFact(expr.asArrayLength)(_, _, _) case ArrayLoad.ASTID => - val arrayLoadExpr = expr.asArrayLoad - val arrayVar = arrayLoadExpr.arrayRef.asVar - /* Generate for array access expressions only by null fact and if array stores integers */ - sourceFact == nullFact && - arrayVar.value.asReferenceValue.asReferenceType.asArrayType.componentType.isIntegerType + isArrayLoadExpressionGeneratedByFact(expr.asArrayLoad)(_, _, _) case GetField.ASTID => - val getFieldExpr = expr.asGetField - /* Generate for field access expressions only by null fact and if field is of type integer */ - sourceFact == nullFact && getFieldExpr.declaredFieldType.isIntegerType + isGetFieldExpressionGeneratedByFact(expr.asGetField)(_, _, _) case GetStatic.ASTID => - val getStaticExpr = expr.asGetStatic - /* Generate for field access expressions only by null fact and if field is of type integer */ - sourceFact == nullFact && getStaticExpr.declaredFieldType.isIntegerType + isGetStaticExpressionGeneratedByFact(expr.asGetStatic)(_, _, _) + + case _ => return false + })( + source, + sourceFact, + target + ) + } - case _ => false + protected def isIntConstExpressionGeneratedByFact( + @unused intConstExpr: IntConst + )( + @unused source: JavaStatement, + sourceFact: LinearConstantPropagationFact, + @unused target: JavaStatement + ): Boolean = { + /* Only generate fact for constants from null fact */ + sourceFact == nullFact + } + + protected def isBinaryExpressionGeneratedByFact( + binaryExpr: BinaryExpr[JavaStatement.V] + )( + @unused source: JavaStatement, + sourceFact: LinearConstantPropagationFact, + @unused target: JavaStatement + ): Boolean = { + val leftExpr = binaryExpr.left + val rightExpr = binaryExpr.right + + if (sourceFact == nullFact) { + /* Only generate fact by null fact for binary expressions if both subexpressions are influenced. + * This is needed for binary expressions with one constant and one variable. */ + (leftExpr.isConst || isVarExpressionGeneratedByFact(leftExpr.asVar)(source, sourceFact, target)) && + (rightExpr.isConst || isVarExpressionGeneratedByFact(rightExpr.asVar)(source, sourceFact, target)) + } else { + /* If source fact is not null fact, generate new fact if one subexpression is influenced by the + * source fact */ + leftExpr.isVar && isVarExpressionGeneratedByFact(leftExpr.asVar)(source, sourceFact, target) || + rightExpr.isVar && isVarExpressionGeneratedByFact(rightExpr.asVar)(source, sourceFact, target) } } + protected def isVarExpressionGeneratedByFact( + varExpr: JavaStatement.V + )( + @unused source: JavaStatement, + sourceFact: LinearConstantPropagationFact, + @unused target: JavaStatement + ): Boolean = { + val hasConstantValue = + varExpr.value match { + case intRange: DefaultIntegerRangeValues#IntegerRange => + intRange.lowerBound == intRange.upperBound + case _ => false + } + + sourceFact match { + case NullFact => + /* Generate fact by null fact for variables if it is definitely a constant */ + hasConstantValue + case VariableFact(_, definedAtIndex) => + /* Generate fact only if it is not detected as constant (by the value analysis) and the variable + * represented by the source fact is one possible initializer of the target variable */ + !hasConstantValue && varExpr.definedBy.contains(definedAtIndex) + } + } + + protected def isArrayLengthExpressionGeneratedByFact( + @unused arrayLengthExpr: ArrayLength[JavaStatement.V] + )( + @unused source: JavaStatement, + sourceFact: LinearConstantPropagationFact, + @unused target: JavaStatement + ): Boolean = { + /* Generate fact for array length expressions only by null fact */ + sourceFact == nullFact + } + + protected def isArrayLoadExpressionGeneratedByFact( + arrayLoadExpr: ArrayLoad[JavaStatement.V] + )( + @unused source: JavaStatement, + sourceFact: LinearConstantPropagationFact, + @unused target: JavaStatement + ): Boolean = { + val arrayVar = arrayLoadExpr.arrayRef.asVar + /* Generate fact for array access expressions only by null fact and if array stores integers */ + sourceFact == nullFact && + arrayVar.value.asReferenceValue.asReferenceType.asArrayType.componentType.isIntegerType + } + + protected def isGetFieldExpressionGeneratedByFact( + getFieldExpr: GetField[JavaStatement.V] + )( + @unused source: JavaStatement, + sourceFact: LinearConstantPropagationFact, + @unused target: JavaStatement + ): Boolean = { + /* Generate fact for field access expressions only by null fact and if field is of type integer */ + sourceFact == nullFact && getFieldExpr.declaredFieldType.isIntegerType + } + + protected def isGetStaticExpressionGeneratedByFact( + getStaticExpr: GetStatic + )( + @unused source: JavaStatement, + sourceFact: LinearConstantPropagationFact, + @unused target: JavaStatement + ): Boolean = { + /* Generate fact for field access expressions only by null fact and if field is of type integer */ + sourceFact == nullFact && getStaticExpr.declaredFieldType.isIntegerType + } + override def getCallFlowFunction( callSite: JavaStatement, calleeEntry: JavaStatement, @@ -238,111 +309,177 @@ class LinearConstantPropagationProblem(project: SomeProject) source.stmt.astID match { case Assignment.ASTID => val assignment = source.stmt.asAssignment - getEdgeFunctionForExpression(assignment.expr, source) + val expr = assignment.expr + + (expr.astID match { + case IntConst.ASTID => + getNormalEdgeFunctionForIntConstExpression(expr.asIntConst)(_, _, _, _) + + case BinaryExpr.ASTID => + getNormalEdgeFunctionForBinaryExpression(expr.asBinaryExpr)(_, _, _, _) + + case ArrayLength.ASTID => + getNormalEdgeFunctionForArrayLength(expr.asArrayLength)(_, _, _, _) + + case ArrayLoad.ASTID => + getNormalEdgeFunctionForArrayLoad(expr.asArrayLoad)(_, _, _, _) + + case GetField.ASTID => + getNormalEdgeFunctionForGetField(expr.asGetField)(_, _, _, _) + + case GetStatic.ASTID => + getNormalEdgeFunctionForGetStatic(expr.asGetStatic)(_, _, _, _) + + case _ => + throw new IllegalArgumentException(s"Expression $expr should not occur here!") + })( + source, + sourceFact, + target, + targetFact + ) + case _ => identityEdgeFunction } } - protected def getEdgeFunctionForExpression( - expr: Expr[JavaStatement.V], - source: JavaStatement + protected def getNormalEdgeFunctionForIntConstExpression( + intConstExpr: IntConst + )( + @unused source: JavaStatement, + @unused sourceFact: LinearConstantPropagationFact, + @unused target: JavaStatement, + @unused targetFact: LinearConstantPropagationFact )(implicit @unused propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { - expr.astID match { - case IntConst.ASTID => - LinearCombinationEdgeFunction(0, expr.asIntConst.value) + LinearCombinationEdgeFunction(0, intConstExpr.value) + } - case BinaryExpr.ASTID => - val binaryExpr = expr.asBinaryExpr - val leftExpr = binaryExpr.left - val rightExpr = binaryExpr.right - - if (leftExpr.astID != Var.ASTID && leftExpr.astID != IntConst.ASTID - || rightExpr.astID != Var.ASTID && rightExpr.astID != IntConst.ASTID - ) { - throw new IllegalArgumentException(s"Combination ($leftExpr, $rightExpr) should not occur here!") - } + protected def getNormalEdgeFunctionForBinaryExpression( + binaryExpr: BinaryExpr[JavaStatement.V] + )( + @unused source: JavaStatement, + @unused sourceFact: LinearConstantPropagationFact, + @unused target: JavaStatement, + @unused targetFact: LinearConstantPropagationFact + )(implicit @unused propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { + val leftExpr = binaryExpr.left + val rightExpr = binaryExpr.right - /* Try to resolve an constant or variable expression to a constant value */ - val getValueForExpr: Expr[JavaStatement.V] => Option[Int] = expr => { - expr.astID match { - case Var.ASTID => - val var0 = expr.asVar - var0.value match { - case intRange: DefaultIntegerRangeValues#IntegerRange => - if (intRange.lowerBound == intRange.upperBound) { - /* If boundaries are equal, the value is constant */ - Some(intRange.lowerBound) - } else if (var0.definedBy.size > 1) { - // TODO (IDE) ADD TESTS FOR THIS (CAN WE REFACTOR THIS/DOES THIS REDUCE THE - // ACCURACY) - return VariableValueEdgeFunction - } else { - None - } - case _ => - None - } + if (leftExpr.astID != Var.ASTID && leftExpr.astID != IntConst.ASTID + || rightExpr.astID != Var.ASTID && rightExpr.astID != IntConst.ASTID + ) { + throw new IllegalArgumentException(s"Combination ($leftExpr, $rightExpr) should not occur here!") + } - case IntConst.ASTID => Some(expr.asIntConst.value) + /* Try to resolve an constant or variable expression to a constant value */ + val getValueForExpr: Expr[JavaStatement.V] => Option[Int] = expr => { + expr.astID match { + case Var.ASTID => + val var0 = expr.asVar + var0.value match { + case intRange: DefaultIntegerRangeValues#IntegerRange => + if (intRange.lowerBound == intRange.upperBound) { + /* If boundaries are equal, the value is constant */ + Some(intRange.lowerBound) + } else if (var0.definedBy.size > 1) { + // TODO (IDE) ADD TESTS FOR THIS (CAN WE REFACTOR THIS/DOES THIS REDUCE THE + // ACCURACY) + return VariableValueEdgeFunction + } else { + None + } + case _ => + None } - } - val leftValue = getValueForExpr(leftExpr) - val rightValue = getValueForExpr(rightExpr) - - (leftValue, rightValue, binaryExpr.op) match { - case (Some(l), Some(r), BinaryArithmeticOperators.Add) => - LinearCombinationEdgeFunction(0, l + r) - case (Some(l), None, BinaryArithmeticOperators.Add) => - LinearCombinationEdgeFunction(1, l) - case (None, Some(r), BinaryArithmeticOperators.Add) => - LinearCombinationEdgeFunction(1, r) - - case (Some(l), Some(r), BinaryArithmeticOperators.Subtract) => - LinearCombinationEdgeFunction(0, l - r) - case (Some(l), None, BinaryArithmeticOperators.Subtract) => - LinearCombinationEdgeFunction(-1, l) - case (None, Some(r), BinaryArithmeticOperators.Subtract) => - LinearCombinationEdgeFunction(1, -r) - - case (Some(l), Some(r), BinaryArithmeticOperators.Multiply) => - LinearCombinationEdgeFunction(0, l * r) - case (Some(l), None, BinaryArithmeticOperators.Multiply) => - LinearCombinationEdgeFunction(l, 0) - case (None, Some(r), BinaryArithmeticOperators.Multiply) => - LinearCombinationEdgeFunction(r, 0) - - case (None, None, _) => - VariableValueEdgeFunction - - case (_, _, BinaryArithmeticOperators.And) => - VariableValueEdgeFunction - case (_, _, BinaryArithmeticOperators.Or) => - VariableValueEdgeFunction - case (_, _, BinaryArithmeticOperators.XOr) => - VariableValueEdgeFunction - - case (_, _, op) => - throw new UnsupportedOperationException(s"Operator $op is not implemented!") - } + case IntConst.ASTID => Some(expr.asIntConst.value) + } + } - case ArrayLength.ASTID => + val leftValue = getValueForExpr(leftExpr) + val rightValue = getValueForExpr(rightExpr) + + (leftValue, rightValue, binaryExpr.op) match { + case (Some(l), Some(r), BinaryArithmeticOperators.Add) => + LinearCombinationEdgeFunction(0, l + r) + case (Some(l), None, BinaryArithmeticOperators.Add) => + LinearCombinationEdgeFunction(1, l) + case (None, Some(r), BinaryArithmeticOperators.Add) => + LinearCombinationEdgeFunction(1, r) + + case (Some(l), Some(r), BinaryArithmeticOperators.Subtract) => + LinearCombinationEdgeFunction(0, l - r) + case (Some(l), None, BinaryArithmeticOperators.Subtract) => + LinearCombinationEdgeFunction(-1, l) + case (None, Some(r), BinaryArithmeticOperators.Subtract) => + LinearCombinationEdgeFunction(1, -r) + + case (Some(l), Some(r), BinaryArithmeticOperators.Multiply) => + LinearCombinationEdgeFunction(0, l * r) + case (Some(l), None, BinaryArithmeticOperators.Multiply) => + LinearCombinationEdgeFunction(l, 0) + case (None, Some(r), BinaryArithmeticOperators.Multiply) => + LinearCombinationEdgeFunction(r, 0) + + case (None, None, _) => VariableValueEdgeFunction - case ArrayLoad.ASTID => + case (_, _, BinaryArithmeticOperators.And) => VariableValueEdgeFunction - - case GetField.ASTID => + case (_, _, BinaryArithmeticOperators.Or) => VariableValueEdgeFunction - - case GetStatic.ASTID => + case (_, _, BinaryArithmeticOperators.XOr) => VariableValueEdgeFunction - case _ => - throw new IllegalArgumentException(s"Expression $expr should not occur here!") + case (_, _, op) => + throw new UnsupportedOperationException(s"Operator $op is not implemented!") } } + protected def getNormalEdgeFunctionForArrayLength( + @unused arrayLengthExpr: ArrayLength[JavaStatement.V] + )( + @unused source: JavaStatement, + @unused sourceFact: LinearConstantPropagationFact, + @unused target: JavaStatement, + @unused targetFact: LinearConstantPropagationFact + )(implicit @unused propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { + VariableValueEdgeFunction + } + + protected def getNormalEdgeFunctionForArrayLoad( + @unused arrayLoadExpr: ArrayLoad[JavaStatement.V] + )( + @unused source: JavaStatement, + @unused sourceFact: LinearConstantPropagationFact, + @unused target: JavaStatement, + @unused targetFact: LinearConstantPropagationFact + )(implicit @unused propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { + VariableValueEdgeFunction + } + + protected def getNormalEdgeFunctionForGetField( + @unused getFieldExpr: GetField[JavaStatement.V] + )( + @unused source: JavaStatement, + @unused sourceFact: LinearConstantPropagationFact, + @unused target: JavaStatement, + @unused targetFact: LinearConstantPropagationFact + )(implicit @unused propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { + VariableValueEdgeFunction + } + + protected def getNormalEdgeFunctionForGetStatic( + @unused getStaticExpr: GetStatic + )( + @unused source: JavaStatement, + @unused sourceFact: LinearConstantPropagationFact, + @unused target: JavaStatement, + @unused targetFact: LinearConstantPropagationFact + )(implicit @unused propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { + VariableValueEdgeFunction + } + override def getCallEdgeFunction( callSite: JavaStatement, callSiteFact: LinearConstantPropagationFact, From 0809bada775566146df11cefee7f7627f21456ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 13 Sep 2024 16:17:44 +0200 Subject: [PATCH 072/167] Fix LCP on fields tests --- .../org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala index 565e358412..d9399a4691 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala @@ -6,7 +6,6 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.ide.IDEPropertiesTest import org.opalj.fpcf.properties.lcp_on_fields.LCPOnFieldsProperty import org.opalj.ide.integration.LazyIDEAnalysisProxyScheduler -import org.opalj.tac.fpcf.analyses.ide.linear_constant_propagation.LCPOnFieldsAnalysisScheduler class LCPOnFieldsTests extends IDEPropertiesTest { override def fixtureProjectPackage: List[String] = { From dd5d4961a8ce6b17948ce4fa9ff7d0f898ba119d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 13 Sep 2024 17:27:47 +0200 Subject: [PATCH 073/167] Make IDEAnalysisProxy more robust --- .../opalj/ide/solver/IDEAnalysisProxy.scala | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala index 61986d9adb..9d3a6070b8 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala @@ -52,8 +52,23 @@ class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statement, Callable < onDependeeUpdateContinuationCoarse(callable, stmt) ) case _ => - // In this case, some kind of result is present (for the callable, as well as for each statement) - createFineResult(callable, stmt) + if (propertyStore.hasProperty( + stmt match { + case Some(statement) => (callable, statement) + case None => callable + }, + propertyMetaInformation.backingPropertyMetaInformation.key + ) + ) { + // In this case, some kind of result is present (for the callable, as well as for each statement) + createFineResult(callable, stmt) + } else { + // Otherwise, the algorithm did not reach the statement (yet) + InterimPartialResult( + immutable.Set(eOptionP), + onDependeeUpdateContinuationCoarse(callable, stmt) + ) + } } } From 84d8e6748c25deedd843ccd804601ff05cab5b6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 16 Sep 2024 11:37:43 +0200 Subject: [PATCH 074/167] Extend LCP on fields problem with array detection --- .../ArrayReadWriteAcrossMethodsExample.java | 38 ++++ .../ArrayReadWriteConstantExample.java | 35 ++++ .../ArrayUnknownIndicesExample.java | 42 +++++ .../problem/LCPOnFieldsEdgeFunctions.scala | 166 ++++++++++++++++++ .../problem/LCPOnFieldsLattice.scala | 17 ++ .../problem/LCPOnFieldsProblem.scala | 68 +++++-- ...arConstantPropagationProblemExtended.scala | 107 ++++++++--- 7 files changed, 441 insertions(+), 32 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteAcrossMethodsExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteConstantExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayUnknownIndicesExample.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteAcrossMethodsExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteAcrossMethodsExample.java new file mode 100644 index 0000000000..03b411c78c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteAcrossMethodsExample.java @@ -0,0 +1,38 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.lcp_on_fields; + +import org.opalj.fpcf.properties.lcp_on_fields.ArrayValue; +import org.opalj.fpcf.properties.lcp_on_fields.ArrayValues; +import org.opalj.fpcf.properties.lcp_on_fields.ConstantArrayElement; +import org.opalj.fpcf.properties.lcp_on_fields.VariableArrayElement; + +public class ArrayReadWriteAcrossMethodsExample { + public void setIndexTo23(int[] arr, int index) { + arr[index] = 23; + } + + public void set11To42(int[] arr) { + arr[11] = 42; + } + + @ArrayValues({ + @ArrayValue(variable = "lv3", variableElements = { + @VariableArrayElement(index = 0), + @VariableArrayElement(index = 1), + @VariableArrayElement(index = 2), + @VariableArrayElement(index = 3) + }), + @ArrayValue(variable = "lv5", constantElements = { + @ConstantArrayElement(index = 11, value = 42) + }) + }) + public static void main(String[] args) { + ArrayReadWriteAcrossMethodsExample example = new ArrayReadWriteAcrossMethodsExample(); + + int[] arr1 = new int[100]; + int[] arr2 = new int[100]; + + example.setIndexTo23(arr1, 2); + example.set11To42(arr2); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteConstantExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteConstantExample.java new file mode 100644 index 0000000000..53695abc86 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteConstantExample.java @@ -0,0 +1,35 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.lcp_on_fields; + +import org.opalj.fpcf.properties.lcp_on_fields.ArrayValue; +import org.opalj.fpcf.properties.lcp_on_fields.ArrayValues; +import org.opalj.fpcf.properties.lcp_on_fields.ConstantArrayElement; + +public class ArrayReadWriteConstantExample { + @ArrayValues({ + @ArrayValue(variable = "lv1", constantElements = { + @ConstantArrayElement(index = 0, value = 0), + @ConstantArrayElement(index = 1, value = 0), + @ConstantArrayElement(index = 2, value = 42), + @ConstantArrayElement(index = 3, value = 4), + @ConstantArrayElement(index = 4, value = 0) + }), + @ArrayValue(variable = "lv3", constantElements = { + @ConstantArrayElement(index = 0, value = 0), + @ConstantArrayElement(index = 1, value = 2), + @ConstantArrayElement(index = 2, value = 3), + @ConstantArrayElement(index = 3, value = 4) + }) + }) + public static void main(String[] args) { + int[] arr1 = new int[5]; + int[] arr2 = new int[]{1, 2, 3, 4}; + + arr1[2] = 42; + arr1[3] = arr2[3]; + arr2[0] = arr1[4]; + + System.out.println("arr1: {" + arr1[0] + ", " + arr1[1] + ", " + arr1[2] + ", " + arr1[3] + ", " + arr1[4] + + "}; arr2: {" + arr2[0] + ", " + arr2[1] + ", " + arr2[2] + ", " + arr2[3] + "}"); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayUnknownIndicesExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayUnknownIndicesExample.java new file mode 100644 index 0000000000..a7b016c603 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayUnknownIndicesExample.java @@ -0,0 +1,42 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.lcp_on_fields; + +import org.opalj.fpcf.properties.lcp_on_fields.ArrayValue; +import org.opalj.fpcf.properties.lcp_on_fields.ConstantArrayElement; +import org.opalj.fpcf.properties.lcp_on_fields.VariableArrayElement; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; + +public class ArrayUnknownIndicesExample { + @ArrayValue(variable = "lv1", variableElements = { + @VariableArrayElement(index = 0), + @VariableArrayElement(index = 1), + @VariableArrayElement(index = 10), + @VariableArrayElement(index = 11), + @VariableArrayElement(index = 12), + @VariableArrayElement(index = 13), + @VariableArrayElement(index = 98), + @VariableArrayElement(index = 99), + }, constantElements = { + @ConstantArrayElement(index = 50, value = 99) + }) + @VariableValue(variable = "lv9") + public static void main(String[] args) { + int[] arr = new int[100]; + + int i; + int j; + if (args.length == 0) { + i = 42; + j = 11; + } else { + i = 23; + j = 12; + } + + int a = arr[i]; + arr[j] = 13; + arr[50] = 99; + + System.out.println("a" + a); + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala index 3f8efe37b8..0e73ed2a15 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -6,6 +6,7 @@ import scala.collection.immutable import org.opalj.ide.problem.AllTopEdgeFunction import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.IdentityEdgeFunction +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationLattice import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue @@ -135,3 +136,168 @@ case class PutFieldEdgeFunction( case _ => false }) } + +/** + * Edge function holding the current array state. An array is represented as an initial value and a collection of + * elements. The array length is not tracked in this problem definition, thus arbitrary indices can be read and written. + * The initial value is used as a fallback/default value for elements that are not in the collection of elements yet + * (will likely be one of `ConstantValue(0)` and `VariableValue`). + */ +case class ArrayEdgeFunction( + initValue: LinearConstantPropagationValue, + elements: immutable.Map[Int, LinearConstantPropagationValue] +) extends EdgeFunction[LCPOnFieldsValue] { + override def compute(sourceValue: LCPOnFieldsValue): LCPOnFieldsValue = + sourceValue match { + case UnknownValue => UnknownValue + case ArrayValue(initValue2, elements2) => ArrayValue(initValue2, (elements -- elements2.keys) ++ elements2) + case VariableValue => ArrayValue(initValue, elements) + + case _ => + throw new UnsupportedOperationException(s"Computing $this for $sourceValue is not implemented!") + } + + override def composeWith(secondEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = + secondEdgeFunction match { + case ArrayEdgeFunction(_, _) => + throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") + + case PutElementEdgeFunction(index, value) => + index match { + case linear_constant_propagation.problem.UnknownValue => + /* In this case it is unknown which indices will be affected */ + UnknownValueEdgeFunction + case linear_constant_propagation.problem.ConstantValue(i) => + ArrayEdgeFunction(initValue, (elements - i) + (i -> value)) + case linear_constant_propagation.problem.VariableValue => + /* In this case any index could be affected */ + ArrayEdgeFunction(linear_constant_propagation.problem.VariableValue, immutable.Map.empty) + } + + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => secondEdgeFunction + + case _ => + throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") + } + + override def meetWith(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = + otherEdgeFunction match { + case ArrayEdgeFunction(initValue2, elements2) => + ArrayEdgeFunction( + LinearConstantPropagationLattice.meet(initValue, initValue2), + elements.keySet.union(elements2.keySet) + .map { index => + index -> LinearConstantPropagationLattice.meet( + elements.getOrElse(index, initValue), + elements2.getOrElse(index, initValue2) + ) + } + .toMap + ) + + case PutElementEdgeFunction(_, _) => + throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => this + + case _ => + throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + } + + override def equalTo(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): Boolean = + (otherEdgeFunction eq this) || + (otherEdgeFunction match { + case ArrayEdgeFunction(initValue2, elements2) => initValue == initValue2 && elements == elements2 + case _ => false + }) +} + +/** + * Edge function for initializing an array + */ +object NewArrayEdgeFunction + extends ArrayEdgeFunction(linear_constant_propagation.problem.ConstantValue(0), immutable.Map.empty) { + override def toString: String = "NewArrayEdgeFunction()" +} + +/** + * Edge function modeling the effect of writing an element of an array + */ +case class PutElementEdgeFunction( + index: LinearConstantPropagationValue, + value: LinearConstantPropagationValue +) extends EdgeFunction[LCPOnFieldsValue] { + override def compute(sourceValue: LCPOnFieldsValue): LCPOnFieldsValue = { + sourceValue match { + case UnknownValue => UnknownValue + case ArrayValue(initValue, elements) => + index match { + case linear_constant_propagation.problem.ConstantValue(i) => + ArrayValue(initValue, (elements - i) + (i -> value)) + case _ => + ArrayValue(linear_constant_propagation.problem.VariableValue, immutable.Map.empty) + } + case VariableValue => VariableValue + + case _ => + throw new UnsupportedOperationException(s"Computing $this for $sourceValue is not implemented!") + } + } + + override def composeWith(secondEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = { + secondEdgeFunction match { + case ArrayEdgeFunction(_, _) => + throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") + + case PutElementEdgeFunction(index2, _) if index == index2 => secondEdgeFunction + case PutElementEdgeFunction(_, _) => + ArrayEdgeFunction(linear_constant_propagation.problem.UnknownValue, immutable.Map.empty) + .composeWith(this).composeWith(secondEdgeFunction) + + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => secondEdgeFunction + + case _ => + throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") + } + } + + override def meetWith(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = { + otherEdgeFunction match { + case ArrayEdgeFunction(_, _) => + throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + + case PutElementEdgeFunction(index2, value2) => + PutElementEdgeFunction( + LinearConstantPropagationLattice.meet(index, index2), + LinearConstantPropagationLattice.meet(value, value2) + ) + + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => this + + case _ => + throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + } + } + + override def equalTo(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): Boolean = + (otherEdgeFunction eq this) || + (otherEdgeFunction match { + case PutElementEdgeFunction(index2, value2) => index == index2 && value == value2 + case _ => false + }) +} + +/** + * Edge function for cases where a value is unknown + */ +object UnknownValueEdgeFunction extends AllTopEdgeFunction[LCPOnFieldsValue](UnknownValue) { + override def composeWith( + secondEdgeFunction: EdgeFunction[LCPOnFieldsValue] + ): EdgeFunction[LCPOnFieldsValue] = { + this + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala index 77fcc307b4..6ebb16e72b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala @@ -17,6 +17,7 @@ object LCPOnFieldsLattice extends MeetLattice[LCPOnFieldsValue] { case (x, UnknownValue) => x case (VariableValue, _) => VariableValue case (_, VariableValue) => VariableValue + case (ObjectValue(xValues), ObjectValue(yValues)) => val values = xValues.keySet .intersect(yValues.keySet) @@ -25,6 +26,22 @@ object LCPOnFieldsLattice extends MeetLattice[LCPOnFieldsValue] { } .toMap ObjectValue(values) + + case (ArrayValue(xInitValue, xElements), ArrayValue(yInitValue, yElements)) => + val elements = xElements.keySet + .union(yElements.keySet) + .map { index => + index -> LinearConstantPropagationLattice.meet( + xElements.getOrElse(index, xInitValue), + yElements.getOrElse(index, yInitValue) + ) + } + .toMap + ArrayValue( + LinearConstantPropagationLattice.meet(xInitValue, yInitValue), + elements + ) + case _ => VariableValue } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index e69c86df04..8b55d56c0d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -14,8 +14,10 @@ import org.opalj.ide.problem.FinalEdgeFunction import org.opalj.ide.problem.FlowFunction import org.opalj.ide.problem.InterimEdgeFunction import org.opalj.ide.problem.MeetLattice +import org.opalj.tac.ArrayStore import org.opalj.tac.Assignment import org.opalj.tac.New +import org.opalj.tac.NewArray import org.opalj.tac.PutField import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation @@ -28,7 +30,8 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall /** * Definition of linear constant propagation on fields problem. This implementation detects basic cases of linear * constant propagation involving fields. It detects direct field assignments but fails to detect assignments done in a - * method where the value is passed as an argument (e.g. a classical setter). + * method where the value is passed as an argument (e.g. a classical setter). Similar, array read accesses can only be + * resolved if the index is a constant literal. * This implementation is mainly intended to be an example of a cyclic IDE analysis. */ class LCPOnFieldsProblem(project: SomeProject) @@ -51,6 +54,15 @@ class LCPOnFieldsProblem(project: SomeProject) /* Generate new object fact from null fact if assignment is a 'new' expression */ immutable.Set(sourceFact, NewObjectFact(assignment.targetVar.name, source.index)) + case NewArray.ASTID => + /* Generate new array fact from null fact if assignment is a 'new array' expression for an + * integer array */ + if (assignment.expr.asNewArray.tpe.componentType.isIntegerType) { + immutable.Set(sourceFact, NewArrayFact(assignment.targetVar.name, source.index)) + } else { + immutable.Set(sourceFact) + } + case _ => immutable.Set(sourceFact) } @@ -69,9 +81,21 @@ class LCPOnFieldsProblem(project: SomeProject) immutable.Set(f.toObjectFact) } - case (_, f: AbstractObjectFact) => - /* Specialized facts only live for one step and are turned back into object facts afterwards */ - immutable.Set(f.toObjectFact) + case (ArrayStore.ASTID, f: AbstractArrayFact) => + val arrayStore = source.stmt.asArrayStore + val arrayVar = arrayStore.arrayRef.asVar + if (arrayVar.definedBy.contains(f.definedAtIndex)) { + immutable.Set(PutElementFact(f.name, f.definedAtIndex)) + } else { + immutable.Set(f.toArrayFact) + } + + case (_, f: AbstractEntityFact) => + /* Specialized facts only live for one step and are turned back into basic ones afterwards */ + immutable.Set(f match { + case f: AbstractObjectFact => f.toObjectFact + case f: AbstractArrayFact => f.toArrayFact + }) case _ => immutable.Set(sourceFact) } @@ -90,14 +114,18 @@ class LCPOnFieldsProblem(project: SomeProject) } else { sourceFact match { case NullFact => - /* Only propagate null fact if function returns an object */ + /* Only propagate null fact if function returns an object or an array of integers */ if (callee.returnType.isObjectType) { immutable.Set(sourceFact) + } else if (callee.returnType.isArrayType && + callee.returnType.asArrayType.componentType.isIntegerType + ) { + immutable.Set(sourceFact) } else { immutable.Set.empty } - case f: AbstractObjectFact => + case f: AbstractEntityFact => val callStmt = callSite.stmt.asCall() /* All parameters (including the implicit 'this' reference) */ @@ -111,7 +139,10 @@ class LCPOnFieldsProblem(project: SomeProject) param.asVar.definedBy.contains(f.definedAtIndex) } .map { case (_, index) => - ObjectFact(s"param$index", -(index + 1)) + f match { + case _: AbstractObjectFact => ObjectFact(s"param$index", -(index + 1)) + case _: AbstractArrayFact => ArrayFact(s"param$index", -(index + 1)) + } } .toSet } @@ -131,7 +162,7 @@ class LCPOnFieldsProblem(project: SomeProject) /* Always propagate null fact */ immutable.Set(sourceFact) - case f: AbstractObjectFact => + case f: AbstractEntityFact => val definedAtIndex = f.definedAtIndex val callStmt = returnSite.stmt.asCall() @@ -143,7 +174,12 @@ class LCPOnFieldsProblem(project: SomeProject) val paramIndex = -(definedAtIndex + 1) val param = allParams(paramIndex) val paramName = param.asVar.name.substring(1, param.asVar.name.length - 1) - param.asVar.definedBy.map { dAI => ObjectFact(paramName, dAI) }.toSet + param.asVar.definedBy.map { dAI => + f match { + case _: AbstractObjectFact => ObjectFact(paramName, dAI) + case _: AbstractArrayFact => ArrayFact(paramName, dAI) + } + }.toSet } else { returnSite.stmt.astID match { case Assignment.ASTID => @@ -153,7 +189,12 @@ class LCPOnFieldsProblem(project: SomeProject) /* Only propagate if the variable represented by the source fact is one possible * initializer of the variable at the return site */ if (returnExpr.asVar.definedBy.contains(f.definedAtIndex)) { - immutable.Set(ObjectFact(assignment.targetVar.name, returnSite.index)) + immutable.Set(f match { + case _: AbstractObjectFact => + ObjectFact(assignment.targetVar.name, returnSite.index) + case _: AbstractArrayFact => + ArrayFact(assignment.targetVar.name, returnSite.index) + }) } else { immutable.Set.empty } @@ -179,13 +220,16 @@ class LCPOnFieldsProblem(project: SomeProject) /* Always propagate null fact */ immutable.Set(sourceFact) - case f: AbstractObjectFact => + case f: AbstractEntityFact => /* Only propagate if the variable represented by the source fact is no initializer of one of the * parameters */ if (callStmt.allParams.exists { param => param.asVar.definedBy.contains(f.definedAtIndex) }) { immutable.Set.empty } else { - immutable.Set(f.toObjectFact) + immutable.Set(f match { + case f: AbstractObjectFact => f.toObjectFact + case f: AbstractArrayFact => f.toArrayFact + }) } } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala index 294fb1c78f..077679e2f9 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala @@ -3,6 +3,7 @@ package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem import scala.collection.immutable +import org.opalj.ai.domain.l1.DefaultIntegerRangeValues import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.FinalP import org.opalj.fpcf.InterimUBP @@ -11,36 +12,46 @@ import org.opalj.fpcf.PropertyStore import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.FinalEdgeFunction import org.opalj.ide.problem.InterimEdgeFunction -import org.opalj.tac.ArrayLength import org.opalj.tac.ArrayLoad import org.opalj.tac.GetField -import org.opalj.tac.GetStatic import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.ConstantValue import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearCombinationEdgeFunction import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationProblem import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.NullFact import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.UnknownValue import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.UnknownValueEdgeFunction +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.VariableFact import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.VariableValue import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.VariableValueEdgeFunction import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.V /** * Extended definition of the linear constant propagation problem, trying to resolve field accesses with the LCP on * fields analysis. */ class LinearConstantPropagationProblemExtended(project: SomeProject) extends LinearConstantPropagationProblem(project) { - override def getNormalEdgeFunctionForArrayLength( - arrayLengthExpr: ArrayLength[JavaStatement.V] + override def isArrayLoadExpressionGeneratedByFact( + arrayLoadExpr: ArrayLoad[V] )( source: JavaStatement, sourceFact: LinearConstantPropagationFact, - target: JavaStatement, - targetFact: LinearConstantPropagationFact - )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { - UnknownValueEdgeFunction + target: JavaStatement + ): Boolean = { + /* Generate fact only if the variable represented by the source fact is one possible initializer of the index + * variable */ + sourceFact match { + case NullFact => false + case VariableFact(_, definedAtIndex) => + val arrayVar = arrayLoadExpr.arrayRef.asVar + val indexVar = arrayLoadExpr.index.asVar + + indexVar.definedBy.contains(definedAtIndex) && + arrayVar.value.asReferenceValue.asReferenceType.asArrayType.componentType.isIntegerType + } } override def getNormalEdgeFunctionForArrayLoad( @@ -51,7 +62,74 @@ class LinearConstantPropagationProblemExtended(project: SomeProject) extends Lin target: JavaStatement, targetFact: LinearConstantPropagationFact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { - UnknownValueEdgeFunction + val arrayVar = arrayLoadExpr.arrayRef.asVar + + val index = arrayLoadExpr.index.asVar.value match { + case intRange: DefaultIntegerRangeValues#IntegerRange => + if (intRange.lowerBound == intRange.upperBound) { + Some(intRange.lowerBound) + } else { + None + } + } + + val lcpOnFieldsEOptionP = + propertyStore((source.method, source), LCPOnFieldsPropertyMetaInformation.key) + + lcpOnFieldsEOptionP match { + case FinalP(property) => + val value = getArrayElementFromProperty(arrayVar, index)(property) + FinalEdgeFunction(value match { + case UnknownValue => UnknownValueEdgeFunction + case ConstantValue(c) => LinearCombinationEdgeFunction(0, c, lattice.top) + case VariableValue => VariableValueEdgeFunction + }) + + case InterimUBP(property) => + val value = getArrayElementFromProperty(arrayVar, index)(property) + value match { + case UnknownValue => + InterimEdgeFunction(UnknownValueEdgeFunction, immutable.Set(lcpOnFieldsEOptionP)) + case ConstantValue(c) => + InterimEdgeFunction( + LinearCombinationEdgeFunction(0, c, lattice.top), + immutable.Set(lcpOnFieldsEOptionP) + ) + case VariableValue => + FinalEdgeFunction(VariableValueEdgeFunction) + } + + case _ => + InterimEdgeFunction(UnknownValueEdgeFunction, immutable.Set(lcpOnFieldsEOptionP)) + } + } + + private def getArrayElementFromProperty( + arrayVar: JavaStatement.V, + index: Option[Int] + )(property: Property): LinearConstantPropagationValue = { + property + .asInstanceOf[LCPOnFieldsPropertyMetaInformation.Self] + .results + .filter { + case (f: AbstractArrayFact, ArrayValue(_, _)) => + arrayVar.definedBy.contains(f.definedAtIndex) + case _ => false + } + .map(_._2) + .foldLeft(UnknownValue: LinearConstantPropagationValue) { + case (value, ArrayValue(initValue, values)) => + val arrayValue = index match { + case Some(i) => values.getOrElse(i, initValue) + case None => + if (values.values.forall { v => v == initValue }) { + initValue + } else { + VariableValue + } + } + lattice.meet(value, arrayValue) + } } override def getNormalEdgeFunctionForGetField( @@ -115,15 +193,4 @@ class LinearConstantPropagationProblemExtended(project: SomeProject) extends Lin lattice.meet(value, values(fieldName)) } } - - override def getNormalEdgeFunctionForGetStatic( - getStaticExpr: GetStatic - )( - source: JavaStatement, - sourceFact: LinearConstantPropagationFact, - target: JavaStatement, - targetFact: LinearConstantPropagationFact - )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { - UnknownValueEdgeFunction - } } From 2ac5750a719b4474556e3a128cda87e0e7a14b77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 16 Sep 2024 11:39:16 +0200 Subject: [PATCH 075/167] Extend LCP on fields problem with array detection --- .../problem/LCPOnFieldsProblem.scala | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 8b55d56c0d..81b5d2f0eb 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -288,6 +288,39 @@ class LCPOnFieldsProblem(project: SomeProject) ) } + case NewArrayFact(_, _) => + FinalEdgeFunction(NewArrayEdgeFunction) + + case PutElementFact(_, _) => + val arrayStore = source.stmt.asArrayStore + val indexVar = arrayStore.index.asVar + val valueVar = arrayStore.value.asVar + + val lcpEOptionP = + propertyStore((source.method, source), LinearConstantPropagationPropertyMetaInformation.key) + + /* Decide based on the current result of the linear constant propagation analysis */ + lcpEOptionP match { + case FinalP(property) => + val index = getVariableFromProperty(indexVar)(property) + val value = getVariableFromProperty(valueVar)(property) + FinalEdgeFunction(PutElementEdgeFunction(index, value)) + + case InterimUBP(property) => + val index = getVariableFromProperty(indexVar)(property) + val value = getVariableFromProperty(valueVar)(property) + InterimEdgeFunction(PutElementEdgeFunction(index, value), immutable.Set(lcpEOptionP)) + + case _ => + InterimEdgeFunction( + PutElementEdgeFunction( + linear_constant_propagation.problem.UnknownValue, + linear_constant_propagation.problem.UnknownValue + ), + immutable.Set(lcpEOptionP) + ) + } + case _ => FinalEdgeFunction(identityEdgeFunction) } } From 1fb83f80c6596d1ea82ac68810723188afeca041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 16 Sep 2024 14:19:51 +0200 Subject: [PATCH 076/167] Use method instead of string comparison --- .../instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 81b5d2f0eb..8b80dd5069 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -109,7 +109,7 @@ class LCPOnFieldsProblem(project: SomeProject) (sourceFact: LCPOnFieldsFact) => { // TODO (IDE) REMOVE ONCE PRECOMPUTED SUMMARIES ARE IMPLEMENTED - if (callee.classFile.thisType.fqn.startsWith("java/") && callee.name != "") { + if (callee.classFile.thisType.fqn.startsWith("java/") && !callee.isConstructor) { immutable.Set.empty } else { sourceFact match { From 039a72aab49a8a6919050c0fdb054d8bdf722918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 18 Sep 2024 09:12:57 +0200 Subject: [PATCH 077/167] Refactor logging --- .../org/opalj/ide/solver/IDEAnalysis.scala | 26 +----- .../scala/org/opalj/ide/util/Logging.scala | 80 +++++++++++++++++++ 2 files changed, 82 insertions(+), 24 deletions(-) create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/util/Logging.scala diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index cad8e91436..d736133b86 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -17,9 +17,6 @@ import org.opalj.fpcf.SomeEOptionP import org.opalj.fpcf.SomeEPK import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.SomePartialResult -import org.opalj.ide.ConfigKeyDebugLog -import org.opalj.ide.ConfigKeyTraceLog -import org.opalj.ide.FrameworkName import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.ide.integration.IDERawProperty import org.opalj.ide.problem.AllTopEdgeFunction @@ -31,7 +28,7 @@ import org.opalj.ide.problem.IdentityEdgeFunction import org.opalj.ide.problem.IDEProblem import org.opalj.ide.problem.IDEValue import org.opalj.ide.problem.InterimEdgeFunction -import org.opalj.log.OPALLogger +import org.opalj.ide.util.Logging /** * Basic solver for IDE problems. Uses the exhaustive/forward algorithm that was presented in the original IDE paper @@ -41,7 +38,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val project: SomeProject, val problem: IDEProblem[Fact, Value, Statement, Callable], val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] -) extends FPCFAnalysis { +) extends FPCFAnalysis with Logging.ByProjectConfig { private type Node = (Statement, Fact) /** * A 'path' in the graph, denoted by it's start and end node as done in the IDE algorithm @@ -289,9 +286,6 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent private val icfg: ICFG[Statement, Callable] = problem.icfg - private val isDebug: Boolean = project.config.getBoolean(ConfigKeyDebugLog) - private val isTrace: Boolean = project.config.getBoolean(ConfigKeyTraceLog) - private def nodeToString(node: Node, indent: String = ""): String = { s"Node(\n$indent\t${icfg.stringifyStatement(node._1, s"$indent\t")},\n$indent\t${node._2}\n$indent)" } @@ -300,22 +294,6 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent s"Path(\n$indent\t${nodeToString(path._1, s"$indent\t")} ->\n$indent\t${nodeToString(path._2, s"$indent\t")}\n$indent)" } - protected def logInfo(message: => String): Unit = { - OPALLogger.info(FrameworkName, message) - } - - protected def logWarn(message: => String): Unit = { - OPALLogger.warn(FrameworkName, message) - } - - protected def logDebug(message: => String): Unit = { - OPALLogger.debug({ isDebug }, s"$FrameworkName - debug", message) - } - - protected def logTrace(message: => String): Unit = { - OPALLogger.debug({ isTrace }, s"$FrameworkName - trace", message) - } - /** * Run the IDE solver and calculate (and return) the result * @param callable the callable that should be analyzed diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/util/Logging.scala b/OPAL/ide/src/main/scala/org/opalj/ide/util/Logging.scala new file mode 100644 index 0000000000..eabab04a2f --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/util/Logging.scala @@ -0,0 +1,80 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.util + +import org.opalj.br.analyses.SomeProject +import org.opalj.ide.ConfigKeyDebugLog +import org.opalj.ide.ConfigKeyTraceLog +import org.opalj.ide.FrameworkName +import org.opalj.log.GlobalLogContext +import org.opalj.log.LogContext +import org.opalj.log.OPALLogger + +/** + * Logging extension for IDE + */ +trait Logging { + /** + * Whether debug messages should be logged + */ + val isDebug: Boolean + /** + * Whether trace messages should be logged + */ + val isTrace: Boolean + + /** + * Log an info message + */ + protected def logInfo(message: => String)(implicit ctx: LogContext): Unit = { + OPALLogger.info(FrameworkName, message) + } + + /** + * Log a warn message + */ + protected def logWarn(message: => String)(implicit ctx: LogContext): Unit = { + OPALLogger.warn(FrameworkName, message) + } + + /** + * Log a debug message + */ + protected def logDebug(message: => String)(implicit ctx: LogContext): Unit = { + OPALLogger.debug({ isDebug }, s"$FrameworkName - debug", message) + } + + /** + * Log a trace message + */ + protected def logTrace(message: => String)(implicit ctx: LogContext): Unit = { + OPALLogger.debug({ isTrace }, s"$FrameworkName - trace", message) + } +} + +object Logging { + /** + * Logging extension for IDE where debug and trace messages are enabled + */ + trait EnableAll extends Logging { + override val isDebug: Boolean = true + override val isTrace: Boolean = true + } + + /** + * Logging extension for IDE where debug and trace messages are en-/disabled dynamically based on the configuration + * of the given project + */ + trait ByProjectConfig extends Logging { + val project: SomeProject + + lazy val isDebug: Boolean = project.config.getBoolean(ConfigKeyDebugLog) + lazy val isTrace: Boolean = project.config.getBoolean(ConfigKeyTraceLog) + } + + /** + * Drop-in of the global log context (when no log context is present) + */ + trait GlobalLogContext extends Logging { + implicit val logContext: LogContext = GlobalLogContext + } +} From 1f6577d44f898c94c03e713de4f43d2ebb5c0c43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 18 Sep 2024 09:47:46 +0200 Subject: [PATCH 078/167] Refactor IDEAnalysisScheduler to adapt flow recording functionality --- .../LCPOnFieldsAnalysisScheduler.scala | 42 +-------- ...PropagationAnalysisSchedulerExtended.scala | 45 +-------- ...ConstantPropagationAnalysisScheduler.scala | 40 +------- .../FlowRecordingAnalysisScheduler.scala} | 92 +++++++++++++------ .../integration/IDEAnalysisScheduler.scala | 21 ++++- .../lcp_on_fields/LCPOnFieldsAnalysis.scala | 20 ---- .../LCPOnFieldsAnalysisScheduler.scala | 39 ++++++++ ...rConstantPropagationAnalysisExtended.scala | 24 ----- ...PropagationAnalysisSchedulerExtended.scala | 41 +++++++++ .../LinearConstantPropagationAnalysis.scala | 19 ---- ...ConstantPropagationAnalysisScheduler.scala | 32 +++++++ .../JavaIDEAnalysisScheduler.scala | 39 +++++++- .../analyses/ide/solver/JavaIDEAnalysis.scala | 19 ---- 13 files changed, 240 insertions(+), 233 deletions(-) rename OPAL/ide/src/main/scala/org/opalj/ide/{solver/FlowRecordingIDEAnalysis.scala => integration/FlowRecordingAnalysisScheduler.scala} (53%) delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisExtended.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala index 66aac70405..990d156f0e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -1,46 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.ide.lcp_on_fields -import scala.collection.immutable - -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.fpcf.properties.cg.Callers -import org.opalj.fpcf.PropertyBounds -import org.opalj.fpcf.PropertyStore -import org.opalj.ide.integration.IDEPropertyMetaInformation -import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.cg.TypeIteratorKey -import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsAnalysis -import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsPropertyMetaInformation -import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsFact -import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsValue -import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsAnalysisScheduler import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler -import org.opalj.tac.fpcf.analyses.ide.solver.JavaIDEAnalysis -import org.opalj.tac.fpcf.properties.TACAI object LCPOnFieldsAnalysisScheduler - extends JavaIDEAnalysisScheduler[LCPOnFieldsFact, LCPOnFieldsValue] { - override def propertyMetaInformation: IDEPropertyMetaInformation[LCPOnFieldsFact, LCPOnFieldsValue] = - LCPOnFieldsPropertyMetaInformation - - override def requiredProjectInformation: ProjectInformationKeys = - Seq(DeclaredMethodsKey, TypeIteratorKey, PropertyStoreKey, RTACallGraphKey) - - override def init( - project: SomeProject, - ps: PropertyStore - ): JavaIDEAnalysis[LCPOnFieldsFact, LCPOnFieldsValue] = { - new LCPOnFieldsAnalysis(project) - } - - override def uses: immutable.Set[PropertyBounds] = - immutable.Set( - PropertyBounds.finalP(TACAI), - PropertyBounds.finalP(Callers), - PropertyBounds.ub(LinearConstantPropagationPropertyMetaInformation) - ) -} + extends LCPOnFieldsAnalysisScheduler with JavaIDEAnalysisScheduler.RTACallGraph diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala index 34b99b529e..eb51a67b48 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala @@ -1,49 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.ide.lcp_on_fields -import scala.collection.immutable - -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.fpcf.properties.cg.Callers -import org.opalj.fpcf.PropertyBounds -import org.opalj.fpcf.PropertyStore -import org.opalj.ide.integration.IDEPropertyMetaInformation -import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.cg.TypeIteratorKey -import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsPropertyMetaInformation -import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LinearConstantPropagationAnalysisExtended -import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation -import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact -import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LinearConstantPropagationAnalysisSchedulerExtended import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler -import org.opalj.tac.fpcf.analyses.ide.solver.JavaIDEAnalysis -import org.opalj.tac.fpcf.properties.TACAI object LinearConstantPropagationAnalysisSchedulerExtended - extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] { - override def propertyMetaInformation: IDEPropertyMetaInformation[ - LinearConstantPropagationFact, - LinearConstantPropagationValue - ] = - LinearConstantPropagationPropertyMetaInformation - - override def requiredProjectInformation: ProjectInformationKeys = - Seq(DeclaredMethodsKey, TypeIteratorKey, PropertyStoreKey, RTACallGraphKey) - - override def init( - project: SomeProject, - ps: PropertyStore - ): JavaIDEAnalysis[LinearConstantPropagationFact, LinearConstantPropagationValue] = { - new LinearConstantPropagationAnalysisExtended(project) - } - - override def uses: immutable.Set[PropertyBounds] = - immutable.Set( - PropertyBounds.finalP(TACAI), - PropertyBounds.finalP(Callers), - PropertyBounds.ub(LCPOnFieldsPropertyMetaInformation) - ) -} + extends LinearConstantPropagationAnalysisSchedulerExtended with JavaIDEAnalysisScheduler.RTACallGraph diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala index 0bea970779..67293af126 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala @@ -1,44 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.ide.linear_constant_propagation -import scala.collection.immutable - -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.fpcf.properties.cg.Callers -import org.opalj.fpcf.PropertyBounds -import org.opalj.fpcf.PropertyStore -import org.opalj.ide.integration.IDEPropertyMetaInformation -import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.cg.TypeIteratorKey -import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationAnalysis -import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation -import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact -import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationAnalysisScheduler import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler -import org.opalj.tac.fpcf.analyses.ide.solver.JavaIDEAnalysis -import org.opalj.tac.fpcf.properties.TACAI object LinearConstantPropagationAnalysisScheduler - extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] { - override def propertyMetaInformation: IDEPropertyMetaInformation[ - LinearConstantPropagationFact, - LinearConstantPropagationValue - ] = - LinearConstantPropagationPropertyMetaInformation - - override def requiredProjectInformation: ProjectInformationKeys = - Seq(DeclaredMethodsKey, TypeIteratorKey, PropertyStoreKey, RTACallGraphKey) - - override def init( - project: SomeProject, - ps: PropertyStore - ): JavaIDEAnalysis[LinearConstantPropagationFact, LinearConstantPropagationValue] = { - new LinearConstantPropagationAnalysis(project) - } - - override def uses: immutable.Set[PropertyBounds] = - immutable.Set(PropertyBounds.finalP(TACAI), PropertyBounds.finalP(Callers)) -} + extends LinearConstantPropagationAnalysisScheduler with JavaIDEAnalysisScheduler.RTACallGraph diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala similarity index 53% rename from OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala rename to OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala index be42dc2882..3c6b0bc041 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/FlowRecordingIDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.solver +package org.opalj.ide.integration import scala.annotation.tailrec @@ -12,44 +12,79 @@ import java.nio.file.Path import java.nio.file.Paths import scala.collection.mutable +import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.fpcf.Entity -import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyStore import org.opalj.ide.problem.FlowRecorderModes import org.opalj.ide.problem.FlowRecorderModes.FlowRecorderMode import org.opalj.ide.problem.FlowRecordingIDEProblem import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEProblem import org.opalj.ide.problem.IDEValue +import org.opalj.ide.solver.IDEAnalysis +import org.opalj.ide.util.Logging /** - * Wrapper class for a normal IDE analysis for debugging purposes. Records the flow paths the IDE solver takes for a - * given base problem as graph and writes it to a file in DOT format. + * Wrapper class for a normal IDE analysis scheduler for debugging purposes. Records the flow paths the IDE solver takes + * for a given base problem as graph and writes it to a file in DOT format. * DOT files can either be viewed with a suitable local program or online e.g. at * [[https://dreampuf.github.io/GraphvizOnline]]. - * @param baseProblem the base problem that defines the flows and edge functions that should be analyzed * @param path the location to write the resulting DOT file (either a file ending with `.dot` or a directory) * @param uniqueFlowsOnly whether to drop or to keep duplicated flows * @param recordEdgeFunctions whether to record edge functions too or just stick with the flow */ -class FlowRecordingIDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( - project: SomeProject, - baseProblem: IDEProblem[Fact, Value, Statement, Callable], - propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value], - path: Option[Path] = None, - recorderMode: FlowRecorderMode = FlowRecorderModes.NODE_AS_STMT_AND_FACT, - uniqueFlowsOnly: Boolean = true, - recordEdgeFunctions: Boolean = true -) extends IDEAnalysis( - project, - new FlowRecordingIDEProblem(baseProblem, recorderMode, uniqueFlowsOnly, recordEdgeFunctions), - propertyMetaInformation - ) { - /** - * The passed IDE problem - */ - val flowRecordingProblem: FlowRecordingIDEProblem[Fact, Value, Statement, Callable] = - problem.asInstanceOf[FlowRecordingIDEProblem[Fact, Value, Statement, Callable]] +class FlowRecordingAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( + ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable], + path: Option[Path] = Some(Paths.get("target/flow-recordings")), + recorderMode: FlowRecorderMode = FlowRecorderModes.NODE_AS_STMT_AND_FACT, + uniqueFlowsOnly: Boolean = true, + recordEdgeFunctions: Boolean = true +) extends IDEAnalysisScheduler[Fact, Value, Statement, Callable] with Logging.EnableAll with Logging.GlobalLogContext { + override def propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] = { + ideAnalysisScheduler.propertyMetaInformation + } + + override def createProblem(project: SomeProject): IDEProblem[Fact, Value, Statement, Callable] = { + val flowRecordingProblem = new FlowRecordingIDEProblem( + ideAnalysisScheduler.createProblem(project), + recorderMode, + uniqueFlowsOnly, + recordEdgeFunctions + ) + startRecording(project, flowRecordingProblem) + flowRecordingProblem + } + + override def requiredProjectInformation: ProjectInformationKeys = { + ideAnalysisScheduler.requiredProjectInformation + } + + override def uses: Set[PropertyBounds] = { + ideAnalysisScheduler.uses + } + + override def beforeSchedule(project: SomeProject, propertyStore: PropertyStore): Unit = { + ideAnalysisScheduler.beforeSchedule(project, propertyStore) + } + + override def afterPhaseScheduling(propertyStore: PropertyStore, analysis: FPCFAnalysis): Unit = { + ideAnalysisScheduler.afterPhaseScheduling(propertyStore, analysis) + } + + override def afterPhaseCompletion( + project: SomeProject, + propertyStore: PropertyStore, + analysis: FPCFAnalysis + ): Unit = { + ideAnalysisScheduler.afterPhaseCompletion(project, propertyStore, analysis) + stopRecording( + analysis.asInstanceOf[IDEAnalysis[Fact, Value, Statement, Callable]] + .problem.asInstanceOf[FlowRecordingIDEProblem[Fact, Value, Statement, Callable]] + ) + } /** * Associate used writers with the file they write to @@ -60,7 +95,7 @@ class FlowRecordingIDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Ca * Get the file to write the graph to. If [[path]] references a file then this method will return [[path]]. If * [[path]] references a directory, a new filename is created (s.t. no file gets overwritten). */ - private def getFile: File = { + private def getFile(project: SomeProject): File = { lazy val className = { val classFQN = project.projectClassFilesWithSources.head._1.thisType.fqn classFQN.substring(classFQN.lastIndexOf('/') + 1) @@ -95,10 +130,13 @@ class FlowRecordingIDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Ca completePath.toFile } - def startRecording(): Unit = { + private def startRecording( + project: SomeProject, + flowRecordingProblem: FlowRecordingIDEProblem[Fact, Value, Statement, Callable] + ): Unit = { logDebug("starting recording") - val file = getFile + val file = getFile(project) val directoryAsFile = file.getParentFile val directoryAsPath = directoryAsFile.toPath.toAbsolutePath.normalize() if (!directoryAsFile.exists()) { @@ -118,7 +156,7 @@ class FlowRecordingIDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Ca flowRecordingProblem.startRecording(writer) } - def stopRecording(): Unit = { + private def stopRecording(flowRecordingProblem: FlowRecordingIDEProblem[Fact, Value, Statement, Callable]): Unit = { logDebug("stopping recording") val writer = flowRecordingProblem.stopRecording() diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala index 5379af226a..4031143910 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala @@ -1,13 +1,18 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ide.integration +import scala.collection.immutable + +import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler +import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.fpcf.Entity import org.opalj.fpcf.PropertyBounds import org.opalj.fpcf.PropertyStore import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEProblem import org.opalj.ide.problem.IDEValue import org.opalj.ide.solver.IDEAnalysis @@ -20,12 +25,24 @@ abstract class IDEAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue, Statemen def propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] - override def derivesLazily: Some[PropertyBounds] = + def createProblem(project: SomeProject): IDEProblem[Fact, Value, Statement, Callable] + + override final def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(propertyMetaInformation.backingPropertyMetaInformation)) + override def requiredProjectInformation: ProjectInformationKeys = + Seq(PropertyStoreKey) + + override def uses: immutable.Set[PropertyBounds] = + immutable.Set.empty + override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} - override def register( + override final def init(project: SomeProject, ps: PropertyStore): IDEAnalysis[Fact, Value, Statement, Callable] = { + new IDEAnalysis(project, createProblem(project), propertyMetaInformation) + } + + override final def register( project: SomeProject, propertyStore: PropertyStore, analysis: IDEAnalysis[Fact, Value, Statement, Callable] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysis.scala deleted file mode 100644 index fb033542ae..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysis.scala +++ /dev/null @@ -1,20 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields - -import org.opalj.br.analyses.SomeProject -import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsProblem -import org.opalj.tac.fpcf.analyses.ide.solver.JavaIDEAnalysis - -/** - * Linear constant propagation on fields as IDE analysis. This implementation is mainly intended to be an example of a - * cyclic IDE analysis (see [[LCPOnFieldsProblem]]). - */ -class LCPOnFieldsAnalysis(project: SomeProject) - extends JavaIDEAnalysis( - project, - new LCPOnFieldsProblem(project), - LCPOnFieldsPropertyMetaInformation - ) { - val lcpOnFieldsProblem: LCPOnFieldsProblem = - problem.asInstanceOf[LCPOnFieldsProblem] -} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala new file mode 100644 index 0000000000..c722993452 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -0,0 +1,39 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields + +import scala.collection.immutable + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.PropertyBounds +import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.ide.problem.IDEProblem +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsFact +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsProblem +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsValue +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement + +/** + * Linear constant propagation on fields as IDE analysis. This implementation is mainly intended to be an example of a + * cyclic IDE analysis (see [[LCPOnFieldsProblem]]). + */ +abstract class LCPOnFieldsAnalysisScheduler extends JavaIDEAnalysisScheduler[LCPOnFieldsFact, LCPOnFieldsValue] { + override def propertyMetaInformation: IDEPropertyMetaInformation[LCPOnFieldsFact, LCPOnFieldsValue] = + LCPOnFieldsPropertyMetaInformation + + override def createProblem(project: SomeProject): IDEProblem[ + LCPOnFieldsFact, + LCPOnFieldsValue, + JavaStatement, + Method + ] = { + new LCPOnFieldsProblem(project) + } + + override def uses: Set[PropertyBounds] = + super.uses.union(immutable.Set( + PropertyBounds.ub(LinearConstantPropagationPropertyMetaInformation) + )) +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisExtended.scala deleted file mode 100644 index 991218f735..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisExtended.scala +++ /dev/null @@ -1,24 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields - -import org.opalj.br.analyses.SomeProject -import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LinearConstantPropagationProblemExtended -import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation -import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationProblem -import org.opalj.tac.fpcf.analyses.ide.solver.JavaIDEAnalysis - -/** - * Extended linear constant propagation as IDE analysis - */ -class LinearConstantPropagationAnalysisExtended(project: SomeProject) - extends JavaIDEAnalysis( - project, - new LinearConstantPropagationProblemExtended(project), - LinearConstantPropagationPropertyMetaInformation - ) { - val lcpProblemExtended: LinearConstantPropagationProblemExtended = - problem.asInstanceOf[LinearConstantPropagationProblemExtended] - - val lcpProblem: LinearConstantPropagationProblem = - lcpProblemExtended -} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala new file mode 100644 index 0000000000..09497ad1df --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala @@ -0,0 +1,41 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields + +import scala.collection.immutable + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.PropertyBounds +import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.ide.problem.IDEProblem +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LinearConstantPropagationProblemExtended +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement + +/** + * Extended linear constant propagation as IDE analysis + */ +abstract class LinearConstantPropagationAnalysisSchedulerExtended + extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] { + override def propertyMetaInformation: IDEPropertyMetaInformation[ + LinearConstantPropagationFact, + LinearConstantPropagationValue + ] = LinearConstantPropagationPropertyMetaInformation + + override def createProblem(project: SomeProject): IDEProblem[ + LinearConstantPropagationFact, + LinearConstantPropagationValue, + JavaStatement, + Method + ] = { + new LinearConstantPropagationProblemExtended(project) + } + + override def uses: Set[PropertyBounds] = + super.uses.union(immutable.Set( + PropertyBounds.ub(LCPOnFieldsPropertyMetaInformation) + )) +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysis.scala deleted file mode 100644 index 6fec7dbb07..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysis.scala +++ /dev/null @@ -1,19 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation - -import org.opalj.br.analyses.SomeProject -import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationProblem -import org.opalj.tac.fpcf.analyses.ide.solver.JavaIDEAnalysis - -/** - * Linear constant propagation as IDE analysis - */ -class LinearConstantPropagationAnalysis(project: SomeProject) - extends JavaIDEAnalysis( - project, - new LinearConstantPropagationProblem(project), - LinearConstantPropagationPropertyMetaInformation - ) { - val lcpProblem: LinearConstantPropagationProblem = - problem.asInstanceOf[LinearConstantPropagationProblem] -} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala new file mode 100644 index 0000000000..b627ff0113 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala @@ -0,0 +1,32 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.ide.problem.IDEProblem +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationProblem +import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement + +/** + * Linear constant propagation as IDE analysis + */ +abstract class LinearConstantPropagationAnalysisScheduler + extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] { + override def propertyMetaInformation: IDEPropertyMetaInformation[ + LinearConstantPropagationFact, + LinearConstantPropagationValue + ] = LinearConstantPropagationPropertyMetaInformation + + override def createProblem(project: SomeProject): IDEProblem[ + LinearConstantPropagationFact, + LinearConstantPropagationValue, + JavaStatement, + Method + ] = { + new LinearConstantPropagationProblem(project) + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala index b5f16bcc2a..e702671570 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala @@ -1,14 +1,51 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ide.integration +import scala.collection.immutable + import org.opalj.br.Method +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.fpcf.PropertyBounds import org.opalj.ide.integration.IDEAnalysisScheduler import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEValue +import org.opalj.tac.cg.CallGraphKey +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement +import org.opalj.tac.fpcf.properties.TACAI /** * Specialized IDE analysis scheduler for Java programs */ abstract class JavaIDEAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue] - extends IDEAnalysisScheduler[Fact, Value, JavaStatement, Method] + extends IDEAnalysisScheduler[Fact, Value, JavaStatement, Method] { + /** + * Key indicating which call graph should be used + */ + val callGraphKey: CallGraphKey + + override def requiredProjectInformation: ProjectInformationKeys = + super.requiredProjectInformation ++ Seq( + DeclaredMethodsKey, + TypeIteratorKey, + callGraphKey + ) + + override def uses: Set[PropertyBounds] = + super.uses.union(immutable.Set( + PropertyBounds.finalP(TACAI), + PropertyBounds.finalP(Callers) + )) +} + +object JavaIDEAnalysisScheduler { + /** + * Trait to drop-in [[RTACallGraphKey]] as [[callGraphKey]] + */ + trait RTACallGraph { + val callGraphKey: CallGraphKey = RTACallGraphKey + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala deleted file mode 100644 index d13c40a2a4..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaIDEAnalysis.scala +++ /dev/null @@ -1,19 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.solver - -import org.opalj.br.Method -import org.opalj.br.analyses.SomeProject -import org.opalj.ide.integration.IDEPropertyMetaInformation -import org.opalj.ide.problem.IDEFact -import org.opalj.ide.problem.IDEProblem -import org.opalj.ide.problem.IDEValue -import org.opalj.ide.solver.IDEAnalysis - -/** - * Solver for IDE problems specialized for Java programs - */ -class JavaIDEAnalysis[Fact <: IDEFact, Value <: IDEValue]( - project: SomeProject, - problem: IDEProblem[Fact, Value, JavaStatement, Method], - propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] -) extends IDEAnalysis[Fact, Value, JavaStatement, Method](project, problem, propertyMetaInformation) From 2e288d9b680c5388f226638df6f6cc6ba7b368fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 18 Sep 2024 16:42:40 +0200 Subject: [PATCH 079/167] Prepare for backward analyses --- .../problem/LCPOnFieldsProblem.scala | 4 +- .../LinearConstantPropagationProblem.scala | 4 +- .../ide/problem/JavaForwardIDEProblem.scala | 18 +++++ .../analyses/ide/problem/JavaIDEProblem.scala | 7 +- .../analyses/ide/solver/JavaBaseICFG.scala | 34 ++++++++ .../analyses/ide/solver/JavaForwardICFG.scala | 61 ++++++++++++++ .../fpcf/analyses/ide/solver/JavaICFG.scala | 79 +------------------ 7 files changed, 120 insertions(+), 87 deletions(-) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaForwardIDEProblem.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 8b80dd5069..8206e6fce0 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -23,7 +23,7 @@ import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationLattice import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue -import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem +import org.opalj.tac.fpcf.analyses.ide.problem.JavaForwardIDEProblem import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall @@ -35,7 +35,7 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall * This implementation is mainly intended to be an example of a cyclic IDE analysis. */ class LCPOnFieldsProblem(project: SomeProject) - extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValue](project) { + extends JavaForwardIDEProblem[LCPOnFieldsFact, LCPOnFieldsValue](project) { override val nullFact: LCPOnFieldsFact = NullFact diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 09d118c45a..e470226a0c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -22,7 +22,7 @@ import org.opalj.tac.GetField import org.opalj.tac.GetStatic import org.opalj.tac.IntConst import org.opalj.tac.Var -import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem +import org.opalj.tac.fpcf.analyses.ide.problem.JavaForwardIDEProblem import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall @@ -30,7 +30,7 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall * Definition of the linear constant propagation problem */ class LinearConstantPropagationProblem(project: SomeProject) - extends JavaIDEProblem[LinearConstantPropagationFact, LinearConstantPropagationValue](project) { + extends JavaForwardIDEProblem[LinearConstantPropagationFact, LinearConstantPropagationValue](project) { override val nullFact: LinearConstantPropagationFact = NullFact diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaForwardIDEProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaForwardIDEProblem.scala new file mode 100644 index 0000000000..8dead22176 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaForwardIDEProblem.scala @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.problem + +import org.opalj.br.analyses.SomeProject +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEValue +import org.opalj.tac.fpcf.analyses.ide.solver.JavaForwardICFG + +/** + * Specialized IDE problem for Java programs on a forward ICFG + */ +abstract class JavaForwardIDEProblem[Fact <: IDEFact, Value <: IDEValue]( + override val icfg: JavaForwardICFG +) extends JavaIDEProblem[Fact, Value](icfg) { + def this(project: SomeProject) = { + this(new JavaForwardICFG(project)) + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala index 0fc92f4ebc..6e0fbd6442 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala @@ -2,7 +2,6 @@ package org.opalj.tac.fpcf.analyses.ide.problem import org.opalj.br.Method -import org.opalj.br.analyses.SomeProject import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEProblem import org.opalj.ide.problem.IDEValue @@ -14,8 +13,4 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement */ abstract class JavaIDEProblem[Fact <: IDEFact, Value <: IDEValue]( override val icfg: JavaICFG -) extends IDEProblem[Fact, Value, JavaStatement, Method](icfg) { - def this(project: SomeProject) = { - this(new JavaICFG(project)) - } -} +) extends IDEProblem[Fact, Value, JavaStatement, Method](icfg) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala new file mode 100644 index 0000000000..9c6660eaa1 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala @@ -0,0 +1,34 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.solver + +import org.opalj.br.Method + +/** + * Base interprocedural control flow graph for Java programs + */ +abstract class JavaBaseICFG extends JavaICFG { + override def isNormalExitStatement(stmt: JavaStatement): Boolean = { + stmt.index == stmt.basicBlock.asBasicBlock.endPC && + stmt.basicBlock.successors.exists(_.isNormalReturnExitNode) + } + + override def isAbnormalExitStatement(stmt: JavaStatement): Boolean = { + stmt.index == stmt.basicBlock.asBasicBlock.endPC && + stmt.basicBlock.successors.exists(_.isAbnormalReturnExitNode) + } + + override def isCallStatement(stmt: JavaStatement): Boolean = { + getCalleesIfCallStatement(stmt).nonEmpty + } + + override def getCallable(stmt: JavaStatement): Method = stmt.method + + override def stringifyStatement(stmt: JavaStatement, indent: String = "", short: Boolean = false): String = { + val stringifiedStatement = stmt.toString + if (short) { + stringifiedStatement.substring(0, stringifiedStatement.indexOf("{")) + } else { + stringifiedStatement + } + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala new file mode 100644 index 0000000000..82c6a2621f --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala @@ -0,0 +1,61 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.solver + +import scala.collection.immutable + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.tac.fpcf.analyses.ifds + +/** + * Interprocedural control flow graph for Java programs in forward direction + */ +class JavaForwardICFG(project: SomeProject) extends JavaBaseICFG { + // TODO (IDE) CURRENTLY DEPENDS ON IMPLEMENTATION FROM IFDS + private val baseICFG = new ifds.JavaForwardICFG(project) + + override def getStartStatements(callable: Method): collection.Set[JavaStatement] = + baseICFG.startStatements(callable).map { + case org.opalj.tac.fpcf.analyses.ifds.JavaStatement(method, index, code, cfg) => + JavaStatement(method, index, isReturnNode = false, code, cfg) + } + + override def getNextStatements(stmt: JavaStatement): collection.Set[JavaStatement] = { + if (isCallStatement(stmt)) { + immutable.Set(JavaStatement(stmt.method, stmt.index, isReturnNode = true, stmt.code, stmt.cfg)) + } else { + baseICFG.nextStatements( + org.opalj.tac.fpcf.analyses.ifds.JavaStatement(stmt.method, stmt.index, stmt.code, stmt.cfg) + ).map { + case org.opalj.tac.fpcf.analyses.ifds.JavaStatement(method, index, code, cfg) => + JavaStatement(method, index, isReturnNode = false, code, cfg) + } + } + } + + // TODO (IDE) REFACTOR AS 'getCallees(...): Set[Method]' + override def getCalleesIfCallStatement(stmt: JavaStatement): Option[collection.Set[Method]] = { + if (stmt.isReturnNode) { + None + } else { + val calleesOption = baseICFG.getCalleesIfCallStatement( + org.opalj.tac.fpcf.analyses.ifds.JavaStatement(stmt.method, stmt.index, stmt.code, stmt.cfg) + ) + calleesOption match { + case None => None + case Some(callees) => + if (callees.isEmpty) { + throw new IllegalStateException( + s"Statement ${stringifyStatement(stmt)} is detected as call statement but no callees were found!" + ) + } else { + Some(callees) + } + } + } + } + + override def getCallablesCallableFromOutside: collection.Set[Method] = { + baseICFG.methodsCallableFromOutside.map { declaredMethod => declaredMethod.asDefinedMethod.definedMethod } + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala index 9c9cd25b81..aac6a1234a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala @@ -1,87 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ide.solver -import scala.collection.immutable - import org.opalj.br.Method -import org.opalj.br.analyses.SomeProject import org.opalj.ide.solver.ICFG -import org.opalj.tac.fpcf.analyses.ifds.JavaForwardICFG /** * Interprocedural control flow graph for Java programs */ -class JavaICFG(project: SomeProject) extends ICFG[JavaStatement, Method] { - // TODO (IDE) CURRENTLY DEPENDS ON IMPLEMENTATION FROM IFDS - private val baseICFG = new JavaForwardICFG(project) - - override def getStartStatements(callable: Method): collection.Set[JavaStatement] = - baseICFG.startStatements(callable).map { - case org.opalj.tac.fpcf.analyses.ifds.JavaStatement(method, index, code, cfg) => - JavaStatement(method, index, isReturnNode = false, code, cfg) - } - - override def getNextStatements(stmt: JavaStatement): collection.Set[JavaStatement] = { - if (isCallStatement(stmt)) { - immutable.Set(JavaStatement(stmt.method, stmt.index, isReturnNode = true, stmt.code, stmt.cfg)) - } else { - baseICFG.nextStatements( - org.opalj.tac.fpcf.analyses.ifds.JavaStatement(stmt.method, stmt.index, stmt.code, stmt.cfg) - ).map { - case org.opalj.tac.fpcf.analyses.ifds.JavaStatement(method, index, code, cfg) => - JavaStatement(method, index, isReturnNode = false, code, cfg) - } - } - } - - override def isNormalExitStatement(stmt: JavaStatement): Boolean = { - stmt.index == stmt.basicBlock.asBasicBlock.endPC && - stmt.basicBlock.successors.exists(_.isNormalReturnExitNode) - } - - override def isAbnormalExitStatement(stmt: JavaStatement): Boolean = { - stmt.index == stmt.basicBlock.asBasicBlock.endPC && - stmt.basicBlock.successors.exists(_.isAbnormalReturnExitNode) - } - - override def isCallStatement(stmt: JavaStatement): Boolean = { - getCalleesIfCallStatement(stmt).nonEmpty - } - - // TODO (IDE) REFACTOR AS 'getCallees(...): Set[Method]' - override def getCalleesIfCallStatement(stmt: JavaStatement): Option[collection.Set[Method]] = { - if (stmt.isReturnNode) { - None - } else { - val calleesOption = baseICFG.getCalleesIfCallStatement( - org.opalj.tac.fpcf.analyses.ifds.JavaStatement(stmt.method, stmt.index, stmt.code, stmt.cfg) - ) - calleesOption match { - case None => None - case Some(callees) => - if (callees.isEmpty) { - throw new IllegalStateException( - s"Statement ${stringifyStatement(stmt)} is detected as call statement but no callees were found!" - ) - } else { - Some(callees) - } - } - } - } - - override def getCallable(stmt: JavaStatement): Method = stmt.method - - def getCallablesCallableFromOutside: collection.Set[Method] = { - baseICFG.methodsCallableFromOutside.map { declaredMethod => declaredMethod.asDefinedMethod.definedMethod } - } - - override def stringifyStatement(stmt: JavaStatement, indent: String = "", short: Boolean = false): String = { - val stringifiedStatement = stmt.toString - if (short) { - stringifiedStatement.substring(0, stringifiedStatement.indexOf("{")) - } else { - stringifiedStatement - } - } +trait JavaICFG extends ICFG[JavaStatement, Method] { + def getCallablesCallableFromOutside: collection.Set[Method] } From 388386e7d3f663c4ad8fdd54ee6dae16bd6d4ab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 25 Sep 2024 10:13:30 +0200 Subject: [PATCH 080/167] Refactor ICFG and remove dependency to IFDS --- .../opalj/fpcf/ide/IDEPropertiesTest.scala | 12 --- .../scala/org/opalj/ide/solver/ICFG.scala | 16 +++- .../org/opalj/ide/solver/IDEAnalysis.scala | 22 +++-- .../problem/LCPOnFieldsProblem.scala | 8 +- .../LinearConstantPropagationProblem.scala | 4 +- .../analyses/ide/solver/JavaBaseICFG.scala | 86 ++++++++++++++++--- .../analyses/ide/solver/JavaForwardICFG.scala | 68 ++++++--------- .../fpcf/analyses/ide/solver/JavaICFG.scala | 4 +- .../analyses/ide/solver/JavaStatement.scala | 18 ++-- 9 files changed, 141 insertions(+), 97 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala index 3ee1336b17..4baae9822b 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala @@ -8,14 +8,12 @@ import com.typesafe.config.ConfigValueFactory import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey -import org.opalj.br.Method import org.opalj.br.analyses.Project import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey import org.opalj.fpcf.PropertiesTest import org.opalj.ide.ConfigKeyDebugLog import org.opalj.ide.ConfigKeyTraceLog import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG class IDEPropertiesTest extends PropertiesTest { override def withRT: Boolean = true @@ -36,14 +34,4 @@ class IDEPropertiesTest extends PropertiesTest { ) p.get(RTACallGraphKey) } - - def getEntryPointsByICFG(icfg: JavaICFG, project: Project[URL]): collection.Set[Method] = { - icfg.getCallablesCallableFromOutside - .filter { method => - val packageOfEntryPoint = method.classFile.thisType.packageName - project.allProjectClassFiles.exists { classFile => - packageOfEntryPoint.startsWith(classFile.thisType.packageName) - } - } - } } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala index 3dd76a88cc..1dd418c4a0 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala @@ -35,7 +35,21 @@ trait ICFG[Statement, Callable <: Entity] { /** * Get all possible callees a call statement could call */ - def getCalleesIfCallStatement(stmt: Statement): Option[collection.Set[? <: Callable]] + def getCallees(stmt: Statement): collection.Set[? <: Callable] + + /** + * Get all possible callees a call statement could call. Throws an exception if no callees could be found. + */ + def getCalleesNonEmpty(stmt: Statement): collection.Set[? <: Callable] = { + val callees = getCallees(stmt) + if (callees.isEmpty) { + throw new IllegalStateException( + s"Statement ${stringifyStatement(stmt)} is detected as call statement but no callees were found!" + ) + } else { + callees + } + } /** * Get the callable a statement belongs to diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index d736133b86..06f47a66d3 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -468,13 +468,12 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logTrace(s"path=${pathToString(path)}") logTrace(s"current jumpFunction=$f") - icfg.getCalleesIfCallStatement(n) match { // IDE P1 line 11 - case Some(qs) => - processCallFlow(path, f, qs) - case None if icfg.isNormalExitStatement(n) => // IDE P1 line 19 - processExitFlow(path, f) - case None => // IDE P1 line 30 - processNormalFlow(path, f) + if (icfg.isCallStatement(n)) { // IDE P1 line 11 + processCallFlow(path, f, icfg.getCalleesNonEmpty(n)) + } else if (icfg.isNormalExitStatement(n)) { // IDE P1 line 19 + processExitFlow(path, f) + } else { // IDE P1 line 30 + processNormalFlow(path, f) } logDebug(s"${s.getPathWorkListSize} path(s) remaining after processing last path") @@ -704,11 +703,10 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val (n, _) = node - icfg.getCalleesIfCallStatement(n) match { // IDE P2 line 11 - case Some(qs) => - processCallNode(node, qs) - case None => // IDE P2 line 7 - processStartNode(node) + if (icfg.isCallStatement(n)) { // IDE P2 line 11 + processCallNode(node, icfg.getCalleesNonEmpty(n)) + } else { // IDE P2 line 7 + processStartNode(node) } logDebug(s"${s.getNodeWorkListSize} node(s) remaining after processing last node") diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 8206e6fce0..95c04065c4 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -52,13 +52,13 @@ class LCPOnFieldsProblem(project: SomeProject) assignment.expr.astID match { case New.ASTID => /* Generate new object fact from null fact if assignment is a 'new' expression */ - immutable.Set(sourceFact, NewObjectFact(assignment.targetVar.name, source.index)) + immutable.Set(sourceFact, NewObjectFact(assignment.targetVar.name, source.pc)) case NewArray.ASTID => /* Generate new array fact from null fact if assignment is a 'new array' expression for an * integer array */ if (assignment.expr.asNewArray.tpe.componentType.isIntegerType) { - immutable.Set(sourceFact, NewArrayFact(assignment.targetVar.name, source.index)) + immutable.Set(sourceFact, NewArrayFact(assignment.targetVar.name, source.pc)) } else { immutable.Set(sourceFact) } @@ -191,9 +191,9 @@ class LCPOnFieldsProblem(project: SomeProject) if (returnExpr.asVar.definedBy.contains(f.definedAtIndex)) { immutable.Set(f match { case _: AbstractObjectFact => - ObjectFact(assignment.targetVar.name, returnSite.index) + ObjectFact(assignment.targetVar.name, returnSite.pc) case _: AbstractArrayFact => - ArrayFact(assignment.targetVar.name, returnSite.index) + ArrayFact(assignment.targetVar.name, returnSite.pc) }) } else { immutable.Set.empty diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index e470226a0c..d92cc6639f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -59,7 +59,7 @@ class LinearConstantPropagationProblem(project: SomeProject) if (isExpressionGeneratedByFact(assignment.expr)(source, sourceFact, target)) { /* Generate fact for target of assignment if the expression is influenced by the source * fact */ - immutable.Set(sourceFact, VariableFact(assignment.targetVar.name, source.index)) + immutable.Set(sourceFact, VariableFact(assignment.targetVar.name, source.pc)) } else { immutable.Set(sourceFact) } @@ -275,7 +275,7 @@ class LinearConstantPropagationProblem(project: SomeProject) /* Only propagate if the variable represented by the source fact is one possible * initializer of the variable at the return site */ if (returnExpr.asVar.definedBy.contains(definedAtIndex)) { - immutable.Set(VariableFact(assignment.targetVar.name, returnSite.index)) + immutable.Set(VariableFact(assignment.targetVar.name, returnSite.pc)) } else { immutable.Set.empty } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala index 9c6660eaa1..0142950eba 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala @@ -2,29 +2,89 @@ package org.opalj.tac.fpcf.analyses.ide.solver import org.opalj.br.Method +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.ContextProviderKey +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.fpcf.analyses.ContextProvider +import org.opalj.br.fpcf.properties.cg.Callees +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.AITACode +import org.opalj.tac.Assignment +import org.opalj.tac.ExprStmt +import org.opalj.tac.LazyDetachedTACAIKey +import org.opalj.tac.NonVirtualFunctionCall +import org.opalj.tac.NonVirtualMethodCall +import org.opalj.tac.StaticFunctionCall +import org.opalj.tac.StaticMethodCall +import org.opalj.tac.TACMethodParameter +import org.opalj.tac.VirtualFunctionCall +import org.opalj.tac.VirtualMethodCall +import org.opalj.value.ValueInformation /** - * Base interprocedural control flow graph for Java programs + * Base interprocedural control flow graph for Java programs. This implementation is based on the + * [[org.opalj.tac.fpcf.analyses.ifds.JavaICFG]] from IFDS. */ -abstract class JavaBaseICFG extends JavaICFG { - override def isNormalExitStatement(stmt: JavaStatement): Boolean = { - stmt.index == stmt.basicBlock.asBasicBlock.endPC && - stmt.basicBlock.successors.exists(_.isNormalReturnExitNode) +abstract class JavaBaseICFG(project: SomeProject) extends JavaICFG { + protected val tacProvider: Method => AITACode[TACMethodParameter, ValueInformation] = { + // TODO (IDE) DOCS SAY, THAT LazyDetachedTACAIKey DOES NOT CACHE ANYTHING + project.get(LazyDetachedTACAIKey) } + protected implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) + protected implicit val contextProvider: ContextProvider = project.get(ContextProviderKey) + protected val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) - override def isAbnormalExitStatement(stmt: JavaStatement): Boolean = { - stmt.index == stmt.basicBlock.asBasicBlock.endPC && - stmt.basicBlock.successors.exists(_.isAbnormalReturnExitNode) + override def isCallStatement(javaStmt: JavaStatement): Boolean = { + if (javaStmt.isReturnNode) { + return false + } + + val stmt = javaStmt.stmt + stmt.astID match { + case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID => true + case Assignment.ASTID | ExprStmt.ASTID => + val expr = stmt.astID match { + case Assignment.ASTID => stmt.asAssignment.expr + case ExprStmt.ASTID => stmt.asExprStmt.expr + } + expr.astID match { + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID => true + case _ => false + } + case _ => false + } } - override def isCallStatement(stmt: JavaStatement): Boolean = { - getCalleesIfCallStatement(stmt).nonEmpty + override def getCallees(javaStmt: JavaStatement): collection.Set[Method] = { + val caller = declaredMethods(javaStmt.method) + val calleesEOptionP = propertyStore(caller, Callees.key) + calleesEOptionP match { + case FinalP(callees) => + callees + .directCallees(contextProvider.newContext(caller), javaStmt.stmt.pc) + .map(_.method) + .flatMap { callee => + if (callee.hasSingleDefinedMethod) { + Seq(callee.definedMethod) + } else if (callee.hasMultipleDefinedMethods) { + callee.definedMethods + } else { + Seq.empty + } + } + .toSet + case _ => + throw new IllegalStateException("Call graph must be computed before the analysis starts!") + } } - override def getCallable(stmt: JavaStatement): Method = stmt.method + override def getCallable(javaStmt: JavaStatement): Method = javaStmt.method - override def stringifyStatement(stmt: JavaStatement, indent: String = "", short: Boolean = false): String = { - val stringifiedStatement = stmt.toString + override def stringifyStatement(javaStmt: JavaStatement, indent: String = "", short: Boolean = false): String = { + val stringifiedStatement = javaStmt.toString if (short) { stringifiedStatement.substring(0, stringifiedStatement.indexOf("{")) } else { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala index 82c6a2621f..ac722d0bd1 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala @@ -2,60 +2,46 @@ package org.opalj.tac.fpcf.analyses.ide.solver import scala.collection.immutable +import scala.collection.mutable import org.opalj.br.Method import org.opalj.br.analyses.SomeProject -import org.opalj.tac.fpcf.analyses.ifds /** - * Interprocedural control flow graph for Java programs in forward direction + * Interprocedural control flow graph for Java programs in forward direction. This implementation is based on the + * [[org.opalj.tac.fpcf.analyses.ifds.JavaForwardICFG]] from IFDS. */ -class JavaForwardICFG(project: SomeProject) extends JavaBaseICFG { - // TODO (IDE) CURRENTLY DEPENDS ON IMPLEMENTATION FROM IFDS - private val baseICFG = new ifds.JavaForwardICFG(project) - - override def getStartStatements(callable: Method): collection.Set[JavaStatement] = - baseICFG.startStatements(callable).map { - case org.opalj.tac.fpcf.analyses.ifds.JavaStatement(method, index, code, cfg) => - JavaStatement(method, index, isReturnNode = false, code, cfg) - } +class JavaForwardICFG(project: SomeProject) extends JavaBaseICFG(project) { + override def getStartStatements(callable: Method): collection.Set[JavaStatement] = { + val tac = tacProvider(callable) + immutable.Set( + JavaStatement(callable, 0, isReturnNode = false, tac.stmts, tac.cfg) + ) + } - override def getNextStatements(stmt: JavaStatement): collection.Set[JavaStatement] = { - if (isCallStatement(stmt)) { - immutable.Set(JavaStatement(stmt.method, stmt.index, isReturnNode = true, stmt.code, stmt.cfg)) + override def getNextStatements(javaStmt: JavaStatement): collection.Set[JavaStatement] = { + if (isCallStatement(javaStmt)) { + immutable.Set( + JavaStatement(javaStmt.method, javaStmt.pc, isReturnNode = true, javaStmt.stmts, javaStmt.cfg) + ) } else { - baseICFG.nextStatements( - org.opalj.tac.fpcf.analyses.ifds.JavaStatement(stmt.method, stmt.index, stmt.code, stmt.cfg) - ).map { - case org.opalj.tac.fpcf.analyses.ifds.JavaStatement(method, index, code, cfg) => - JavaStatement(method, index, isReturnNode = false, code, cfg) + val successors = mutable.Set.empty[JavaStatement] + javaStmt.cfg.foreachSuccessor(javaStmt.pc) { nextPc => + successors.add( + JavaStatement(javaStmt.method, nextPc, isReturnNode = false, javaStmt.stmts, javaStmt.cfg) + ) } + successors } } - // TODO (IDE) REFACTOR AS 'getCallees(...): Set[Method]' - override def getCalleesIfCallStatement(stmt: JavaStatement): Option[collection.Set[Method]] = { - if (stmt.isReturnNode) { - None - } else { - val calleesOption = baseICFG.getCalleesIfCallStatement( - org.opalj.tac.fpcf.analyses.ifds.JavaStatement(stmt.method, stmt.index, stmt.code, stmt.cfg) - ) - calleesOption match { - case None => None - case Some(callees) => - if (callees.isEmpty) { - throw new IllegalStateException( - s"Statement ${stringifyStatement(stmt)} is detected as call statement but no callees were found!" - ) - } else { - Some(callees) - } - } - } + override def isNormalExitStatement(javaStmt: JavaStatement): Boolean = { + javaStmt.pc == javaStmt.basicBlock.asBasicBlock.endPC && + javaStmt.basicBlock.successors.exists(_.isNormalReturnExitNode) } - override def getCallablesCallableFromOutside: collection.Set[Method] = { - baseICFG.methodsCallableFromOutside.map { declaredMethod => declaredMethod.asDefinedMethod.definedMethod } + override def isAbnormalExitStatement(javaStmt: JavaStatement): Boolean = { + javaStmt.pc == javaStmt.basicBlock.asBasicBlock.endPC && + javaStmt.basicBlock.successors.exists(_.isAbnormalReturnExitNode) } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala index aac6a1234a..ea6384d6b0 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala @@ -7,6 +7,4 @@ import org.opalj.ide.solver.ICFG /** * Interprocedural control flow graph for Java programs */ -trait JavaICFG extends ICFG[JavaStatement, Method] { - def getCallablesCallableFromOutside: collection.Set[Method] -} +trait JavaICFG extends ICFG[JavaStatement, Method] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala index 30288a735b..5509503e39 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala @@ -14,29 +14,29 @@ import org.opalj.value.ValueInformation /** * Class to model statements used with IDE analyses - * @param index the index of the statement in the code + * @param pc the pc of the statement in the code * @param isReturnNode whether the statement models the return node of a call */ case class JavaStatement( method: Method, - index: Int, + pc: Int, isReturnNode: Boolean = false, - code: Array[Stmt[JavaStatement.V]], + stmts: Array[Stmt[JavaStatement.V]], cfg: CFG[Stmt[JavaStatement.V], TACStmts[JavaStatement.V]] ) { // TODO (IDE) DOES THIS GET REEVALUATED EACH CALL? DO WE ALWAYS CALL IT AT LEAST ONCE? MAYBE MAKE IT A (LAZY) // PROPERTY - def stmt: Stmt[JavaStatement.V] = code(index) + def stmt: Stmt[JavaStatement.V] = stmts(pc) // TODO (IDE) DOES THIS GET REEVALUATED EACH CALL? DO WE ALWAYS CALL IT AT LEAST ONCE? MAYBE MAKE IT A (LAZY) // PROPERTY - def basicBlock: BasicBlock = cfg.bb(index) + def basicBlock: BasicBlock = cfg.bb(pc) - override def hashCode(): Int = method.hashCode() * 31 + index + override def hashCode(): Int = method.hashCode() * 31 + pc override def equals(obj: Any): Boolean = obj match { - case JavaStatement(method2, index2, isReturnNode2, _, _) => - method == method2 && index == index2 && isReturnNode == isReturnNode2 + case JavaStatement(method2, pc2, isReturnNode2, _, _) => + method == method2 && pc == pc2 && isReturnNode == isReturnNode2 case _ => false } @@ -44,7 +44,7 @@ case class JavaStatement( val returnOptional = if (isReturnNode) { "(return)" } else { "" } - s"${method.classFile.thisType.simpleName}:${method.name}[$index]$returnOptional{$stmt}" + s"${method.classFile.thisType.simpleName}:${method.name}[$pc]$returnOptional{$stmt}" } } From 5e886a514a6862f28004656582ce7d91c040e3d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 25 Sep 2024 10:24:57 +0200 Subject: [PATCH 081/167] Add backward problem and ICFG --- .../ide/problem/JavaBackwardIDEProblem.scala | 18 ++++++++ .../ide/solver/JavaBackwardICFG.scala | 44 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaBackwardIDEProblem.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBackwardICFG.scala diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaBackwardIDEProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaBackwardIDEProblem.scala new file mode 100644 index 0000000000..a5b59d5942 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaBackwardIDEProblem.scala @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.problem + +import org.opalj.br.analyses.SomeProject +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEValue +import org.opalj.tac.fpcf.analyses.ide.solver.JavaBackwardICFG + +/** + * Specialized IDE problem for Java programs on a backward ICFG + */ +abstract class JavaBackwardIDEProblem[Fact <: IDEFact, Value <: IDEValue]( + override val icfg: JavaBackwardICFG +) extends JavaIDEProblem[Fact, Value](icfg) { + def this(project: SomeProject) = { + this(new JavaBackwardICFG(project)) + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBackwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBackwardICFG.scala new file mode 100644 index 0000000000..0bcb8d473b --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBackwardICFG.scala @@ -0,0 +1,44 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.solver + +import scala.collection.immutable +import scala.collection.mutable + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject + +/** + * Interprocedural control flow graph for Java programs in backward direction. This implementation is based on the + * [[org.opalj.tac.fpcf.analyses.ifds.JavaBackwardICFG]] from IFDS. + */ +class JavaBackwardICFG(project: SomeProject) extends JavaBaseICFG(project) { + override def getStartStatements(callable: Method): collection.Set[JavaStatement] = { + val tac = tacProvider(callable) + (tac.cfg.normalReturnNode.predecessors ++ tac.cfg.abnormalReturnNode.predecessors) + .map { node => JavaStatement(callable, node.asBasicBlock.endPC, isReturnNode = false, tac.stmts, tac.cfg) } + } + + override def getNextStatements(javaStmt: JavaStatement): collection.Set[JavaStatement] = { + if (isCallStatement(javaStmt)) { + immutable.Set( + JavaStatement(javaStmt.method, javaStmt.pc, isReturnNode = true, javaStmt.stmts, javaStmt.cfg) + ) + } else { + val predecessors = mutable.Set.empty[JavaStatement] + javaStmt.cfg.foreachPredecessor(javaStmt.pc) { prevPc => + predecessors.add( + JavaStatement(javaStmt.method, prevPc, isReturnNode = false, javaStmt.stmts, javaStmt.cfg) + ) + } + predecessors + } + } + + override def isNormalExitStatement(stmt: JavaStatement): Boolean = { + stmt.pc == 0 + } + + override def isAbnormalExitStatement(stmt: JavaStatement): Boolean = { + false + } +} From bf67bcb77068d5a047bf814c6ba6ab4f9e306943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 25 Sep 2024 17:43:17 +0200 Subject: [PATCH 082/167] Refactor ICFG types --- OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala | 4 ++-- .../ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala index 1dd418c4a0..901864ddc1 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala @@ -35,12 +35,12 @@ trait ICFG[Statement, Callable <: Entity] { /** * Get all possible callees a call statement could call */ - def getCallees(stmt: Statement): collection.Set[? <: Callable] + def getCallees(stmt: Statement): collection.Set[Callable] /** * Get all possible callees a call statement could call. Throws an exception if no callees could be found. */ - def getCalleesNonEmpty(stmt: Statement): collection.Set[? <: Callable] = { + def getCalleesNonEmpty(stmt: Statement): collection.Set[Callable] = { val callees = getCallees(stmt) if (callees.isEmpty) { throw new IllegalStateException( diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 06f47a66d3..a7dd27d04b 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -480,7 +480,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } } - private def processCallFlow(path: Path, f: JumpFunction, qs: collection.Set[? <: Callable])( + private def processCallFlow(path: Path, f: JumpFunction, qs: collection.Set[Callable])( implicit s: State ): Unit = { logDebug("processing as call flow") @@ -787,7 +787,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } } - private def processCallNode(node: Node, qs: collection.Set[? <: Callable])(implicit s: State): Unit = { + private def processCallNode(node: Node, qs: collection.Set[Callable])(implicit s: State): Unit = { logDebug("processing as call node") val (n, d) = node From b5217e9c6b28323c3e1c4cc63c4f42c51885e7e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 7 Oct 2024 16:33:52 +0200 Subject: [PATCH 083/167] Add missing annotations and matchers for LCP on fields values --- .../lcp_on_fields/UnknownValue.java | 21 ++++ .../lcp_on_fields/UnknownValues.java | 17 ++++ .../lcp_on_fields/VariableValue.java | 21 ++++ .../lcp_on_fields/VariableValues.java | 18 ++++ .../lcp_on_fields/LCPOnFieldsMatcher.scala | 96 +++++++++++++++++++ 5 files changed, 173 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValue.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValues.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValue.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValues.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValue.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValue.java new file mode 100644 index 0000000000..5852b3cdff --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValue.java @@ -0,0 +1,21 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.lcp_on_fields; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.*; + +/** + * Annotation to state that a variables value is unknown + */ +@PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = UnknownValueMatcher.class) +@Repeatable(UnknownValues.class) +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface UnknownValue { + /** + * The name of the variable + */ + String variable() default ""; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValues.java new file mode 100644 index 0000000000..5bc3532bdb --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValues.java @@ -0,0 +1,17 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.lcp_on_fields; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.*; + +/** + * Container annotation for {@link UnknownValue} annotations + */ +@PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = UnknownValueMatcher.class) +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface UnknownValues { + UnknownValue[] value(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValue.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValue.java new file mode 100644 index 0000000000..cb88d243a3 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValue.java @@ -0,0 +1,21 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.lcp_on_fields; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.*; + +/** + * Annotation to state that a variable has a non-constant value + */ +@PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = VariableValueMatcher.class) +@Repeatable(VariableValues.class) +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface VariableValue { + /** + * The name of the variable + */ + String variable() default ""; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValues.java new file mode 100644 index 0000000000..cb8647d2e0 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValues.java @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.lcp_on_fields; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.*; + +/** + * Container annotation for {@link VariableValue} annotations + */ +@PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = VariableValueMatcher.class) +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface VariableValues { + VariableValue[] value(); +} + diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala index 4d5a1835db..b28467c649 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala @@ -230,3 +230,99 @@ class ArrayValueMatcher extends AbstractRepeatablePropertyMatcher { } } } + +/** + * Matcher for [[VariableValue]] and [[VariableValues]] annotations + */ +class VariableValueMatcher extends AbstractRepeatablePropertyMatcher { + override val singleAnnotationType: ObjectType = + ObjectType("org/opalj/fpcf/properties/lcp_on_fields/VariableValue") + override val containerAnnotationType: ObjectType = + ObjectType("org/opalj/fpcf/properties/lcp_on_fields/VariableValues") + + override def validateSingleProperty( + p: Project[?], + as: Set[ObjectType], + entity: Any, + a: AnnotationLike, + properties: Iterable[Property] + ): Option[String] = { + val expectedVariableName = + getValue(p, singleAnnotationType, a.elementValuePairs, "variable").asStringValue.value + + if (properties.exists { + case property: BasicIDEProperty[?, ?] => + property.results.exists { + case ( + lcp_on_fields.problem.ObjectFact(name, _), + lcp_on_fields.problem.VariableValue + ) => + expectedVariableName == name + case ( + lcp_on_fields.problem.ArrayFact(name, _), + lcp_on_fields.problem.VariableValue + ) => + expectedVariableName == name + + case _ => false + } + + case _ => false + } + ) { + None + } else { + Some( + s"Result should contain (${lcp_on_fields.problem.ObjectFact(expectedVariableName, 0)}, ${lcp_on_fields.problem.VariableValue})!" + ) + } + } +} + +/** + * Matcher for [[UnknownValue]] and [[UnknownValues]] annotations + */ +class UnknownValueMatcher extends AbstractRepeatablePropertyMatcher { + override val singleAnnotationType: ObjectType = + ObjectType("org/opalj/fpcf/properties/lcp_on_fields/UnknownValue") + override val containerAnnotationType: ObjectType = + ObjectType("org/opalj/fpcf/properties/lcp_on_fields/UnknownValues") + + override def validateSingleProperty( + p: Project[?], + as: Set[ObjectType], + entity: Any, + a: AnnotationLike, + properties: Iterable[Property] + ): Option[String] = { + val expectedVariableName = + getValue(p, singleAnnotationType, a.elementValuePairs, "variable").asStringValue.value + + if (properties.exists { + case property: BasicIDEProperty[?, ?] => + property.results.exists { + case ( + lcp_on_fields.problem.ObjectFact(name, _), + lcp_on_fields.problem.UnknownValue + ) => + expectedVariableName == name + case ( + lcp_on_fields.problem.ArrayFact(name, _), + lcp_on_fields.problem.UnknownValue + ) => + expectedVariableName == name + + case _ => false + } + + case _ => false + } + ) { + None + } else { + Some( + s"Result should contain (${lcp_on_fields.problem.ObjectFact(expectedVariableName, 0)}, ${lcp_on_fields.problem.UnknownValue})!" + ) + } + } +} From fa06f933d35241ef983ca26f84c3819c227c2ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 7 Oct 2024 17:05:39 +0200 Subject: [PATCH 084/167] Add possibility to provide precomputed summary functions per problem definition for arbitrary callees --- .../ide/problem/FlowRecordingIDEProblem.scala | 35 ++++++ .../org/opalj/ide/problem/IDEProblem.scala | 53 ++++++++- .../org/opalj/ide/solver/IDEAnalysis.scala | 112 +++++++++++------- 3 files changed, 156 insertions(+), 44 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index aa432622b7..fa62cb620b 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -174,6 +174,41 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal edgeFunctionResult } + override def hasPrecomputedFlowAndSummaryFunction( + callSite: Statement, + callSiteFact: Fact, + callee: Callable + )(implicit propertyStore: PropertyStore): Boolean = { + baseProblem.hasPrecomputedFlowAndSummaryFunction(callSite, callSiteFact, callee) + } + + override def getPrecomputedFlowFunction(callSite: Statement, callee: Callable, returnSite: Statement)( + implicit propertyStore: PropertyStore + ): FlowFunction[Fact] = { + new RecordingFlowFunction( + baseProblem.getPrecomputedFlowFunction(callSite, callee, returnSite), + callSite, + returnSite, + "precomputed flow" + ) + } + + override def getPrecomputedSummaryFunction( + callSite: Statement, + callSiteFact: Fact, + callee: Callable, + returnSite: Statement, + returnSiteFact: Fact + )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { + val edgeFunction = + baseProblem.getPrecomputedSummaryFunction(callSite, callSiteFact, callee, returnSite, returnSiteFact) + collectedEdgeFunctions.put( + createDotEdge(callSite, callSiteFact, returnSite, returnSiteFact, "precomputed flow"), + edgeFunction + ) + edgeFunction + } + private def createDotEdge( source: Statement, sourceFact: Fact, diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index 72d3a38913..a5a91ce002 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -14,7 +14,7 @@ import org.opalj.ide.solver.ICFG * Interface for modeling IDE problems */ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( - val icfg: ICFG[Statement, Callable] + val icfg: ICFG[Statement, Callable] ) { implicit def edgeFunctionToFinalEdgeFunction(edgeFunction: EdgeFunction[Value]): EdgeFunctionResult[Value] = { FinalEdgeFunction(edgeFunction) @@ -155,4 +155,55 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl returnSite: Statement, returnSiteFact: Fact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] + + /** + * Whether precomputed flow and summary functions for a `(callSite, callSiteFact, callee)` combination exist + * (resp. can be generated). + * @param callSite where the flow starts + * @param callSiteFact the fact the flow starts with + * @param callee the callable this flow is about + */ + def hasPrecomputedFlowAndSummaryFunction(callSite: Statement, callSiteFact: Fact, callee: Callable)( + implicit propertyStore: PropertyStore + ): Boolean = { + false + } + + /** + * Generate a flow function that yields the facts that are valid when going through the callable and reaching the + * return site. Similar to a call-to-return flow (cfg. [[getCallToReturnFlowFunction]]) but capturing the effects + * that flow through the callable. + * @param callSite where the flow starts (always a call statement) + * @param callee the callable this flow is about + * @param returnSite where the flow ends (e.g. the next statement after the call) + */ + def getPrecomputedFlowFunction(callSite: Statement, callee: Callable, returnSite: Statement)( + implicit propertyStore: PropertyStore + ): FlowFunction[Fact] = { + throw new IllegalArgumentException( + s"No precomputed flow function for callSite=$callSite, callee=$callee and returnSite=$returnSite exists!" + ) + } + + /** + * Generate a summary function from a call-site node up to a return-site node (just what summary functions are in + * the foundation paper, but in one step). + * @param callSite where the flow starts (always a call statement) + * @param callSiteFact the fact the flow starts with + * @param callee the callable the flow is about + * @param returnSite where the flow ends (e.g. the next statement after the call) + * @param returnSiteFact the fact the flow ends with + */ + def getPrecomputedSummaryFunction( + callSite: Statement, + callSiteFact: Fact, + callee: Callable, + returnSite: Statement, + returnSiteFact: Fact + )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { + throw new IllegalArgumentException( + s"No precomputed summary function for callSite=$callSite, callSiteFact=$callSiteFact, " + + s"callee=$callee, returnSite=$returnSite and returnSiteFact=$returnSiteFact exists!" + ) + } } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index a7dd27d04b..cd25e95371 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -492,47 +492,71 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent qs.foreach { q => logDebug(s"handling call target q=$q") - // TODO (IDE) ALSO COLLECTS JRE METHODS -> P2 part (ii) MAY GET VERY SLOW - s.rememberCallable(q) + if (problem.hasPrecomputedFlowAndSummaryFunction(n, d2, q)) { + logDebug(s"handling path with precomputed information") - val sqs = icfg.getStartStatements(q) - sqs.foreach { sq => - // IDE P1 lines 12 - 13 - val d3s = problem.getCallFlowFunction(n, sq, q).compute(d2) + /* Handling for precomputed summaries */ + rs.foreach { r => + val d5s = problem.getPrecomputedFlowFunction(n, q, r).compute(d2) - logTrace(s"generated the following d3s=$d3s for start statement sq=${icfg.stringifyStatement(sq)}") + logTrace(s"generated the following d5s=$d5s for return statement r=${icfg.stringifyStatement(r)}") - d3s.foreach { d3 => - s.rememberCallEdge(((n, d2), (sq, d3))) - - val endSummaries = s.getEndSummaries((sq, d3)) - // Handling for end summaries extension - if (endSummaries.nonEmpty) { - endSummaries.foreach { case ((eq, d4), fEndSummary) => - val f4 = handleEdgeFunctionResult(problem.getCallEdgeFunction(n, d2, sq, d3, q), path) - rs.foreach { r => - val d5s = problem.getReturnFlowFunction(eq, q, r).compute(d4) - d5s.foreach { d5 => - val f5 = handleEdgeFunctionResult( - problem.getReturnEdgeFunction(eq, d4, q, r, d5), - path - ) - val callToReturnPath = ((n, d2), (r, d5)) - val oldSummaryFunction = s.getSummaryFunction(callToReturnPath) - val fPrime = - f4.composeWith(fEndSummary).composeWith(f5).meetWith(oldSummaryFunction) - - if (!fPrime.equalTo(oldSummaryFunction)) { - s.setSummaryFunction(callToReturnPath, fPrime) - } + d5s.foreach { d5 => + val summaryFunction = problem.getPrecomputedSummaryFunction(n, d2, q, r, d5) + val callToReturnPath = ((n, d2), (r, d5)) + val oldSummaryFunction = s.getSummaryFunction(callToReturnPath) + val fPrime = summaryFunction.meetWith(oldSummaryFunction) + + if (!fPrime.equalTo(oldSummaryFunction)) { + s.setSummaryFunction(callToReturnPath, fPrime) + } - propagate(((sp, d1), (r, d5)), f.composeWith(fPrime)) + propagate(((sp, d1), (r, d5)), f.composeWith(fPrime)) + } + } + } else { + // TODO (IDE) ALSO COLLECTS JRE METHODS -> P2 part (ii) MAY GET VERY SLOW + s.rememberCallable(q) + + val sqs = icfg.getStartStatements(q) + sqs.foreach { sq => + // IDE P1 lines 12 - 13 + val d3s = problem.getCallFlowFunction(n, sq, q).compute(d2) + + logTrace(s"generated the following d3s=$d3s for start statement sq=${icfg.stringifyStatement(sq)}") + + d3s.foreach { d3 => + s.rememberCallEdge(((n, d2), (sq, d3))) + + val endSummaries = s.getEndSummaries((sq, d3)) + // Handling for end summaries extension + if (endSummaries.nonEmpty) { + endSummaries.foreach { case ((eq, d4), fEndSummary) => + val f4 = handleEdgeFunctionResult(problem.getCallEdgeFunction(n, d2, sq, d3, q), path) + rs.foreach { r => + val d5s = problem.getReturnFlowFunction(eq, q, r).compute(d4) + d5s.foreach { d5 => + val f5 = handleEdgeFunctionResult( + problem.getReturnEdgeFunction(eq, d4, q, r, d5), + path + ) + val callToReturnPath = ((n, d2), (r, d5)) + val oldSummaryFunction = s.getSummaryFunction(callToReturnPath) + val fPrime = + f4.composeWith(fEndSummary).composeWith(f5).meetWith(oldSummaryFunction) + + if (!fPrime.equalTo(oldSummaryFunction)) { + s.setSummaryFunction(callToReturnPath, fPrime) + } + + propagate(((sp, d1), (r, d5)), f.composeWith(fPrime)) + } } } + } else { + // Default algorithm behavior + propagate(((sq, d3), (sq, d3)), identityEdgeFunction) } - } else { - // Default algorithm behavior - propagate(((sq, d3), (sq, d3)), identityEdgeFunction) } } } @@ -794,15 +818,17 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // IDE P2 lines 12 - 13 qs.foreach { q => - val sqs = icfg.getStartStatements(q) - sqs.foreach { sq => - val dPrimes = problem.getCallFlowFunction(n, sq, q).compute(d) - dPrimes.foreach { dPrime => - propagateValue( - (sq, dPrime), - enforceFinalEdgeFunction(problem.getCallEdgeFunction(n, d, sq, dPrime, q)) - .compute(s.getValue(node)) - ) + if (!problem.hasPrecomputedFlowAndSummaryFunction(n, d, q)) { + val sqs = icfg.getStartStatements(q) + sqs.foreach { sq => + val dPrimes = problem.getCallFlowFunction(n, sq, q).compute(d) + dPrimes.foreach { dPrime => + propagateValue( + (sq, dPrime), + enforceFinalEdgeFunction(problem.getCallEdgeFunction(n, d, sq, dPrime, q)) + .compute(s.getValue(node)) + ) + } } } } From a1acb0176a7666d43dfe67543748571f3e6ef08a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 7 Oct 2024 17:09:36 +0200 Subject: [PATCH 085/167] Use precomputed summaries for linear constant propagation and LCP on fields to deal with native callees --- .../ArrayNativeMethodExample.java | 40 +++ .../ObjectNativeMethodExample.java | 26 ++ .../problem/LCPOnFieldsEdgeFunctions.scala | 89 +++++-- .../problem/LCPOnFieldsProblem.scala | 236 +++++++++++------- .../LinearConstantPropagationProblem.scala | 37 +++ 5 files changed, 317 insertions(+), 111 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayNativeMethodExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ObjectNativeMethodExample.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayNativeMethodExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayNativeMethodExample.java new file mode 100644 index 0000000000..dec1119a49 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayNativeMethodExample.java @@ -0,0 +1,40 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.lcp_on_fields; + +import org.opalj.fpcf.properties.lcp_on_fields.ArrayValue; +import org.opalj.fpcf.properties.lcp_on_fields.ArrayValues; +import org.opalj.fpcf.properties.lcp_on_fields.ConstantArrayElement; +import org.opalj.fpcf.properties.lcp_on_fields.VariableArrayElement; + +public class ArrayNativeMethodExample { + @ArrayValues({ + @ArrayValue(variable = "lv1", variableElements = { + @VariableArrayElement(index = 0), + @VariableArrayElement(index = 1), + @VariableArrayElement(index = 2), + @VariableArrayElement(index = 3) + }), + @ArrayValue(variable = "lvf", constantElements = { + @ConstantArrayElement(index = 0, value = 42), + @ConstantArrayElement(index = 1, value = 23) + }), + @ArrayValue(variable = "lv17", variableElements = { + @VariableArrayElement(index = 0), + @VariableArrayElement(index = 1), + @VariableArrayElement(index = 2), + @VariableArrayElement(index = 3), + @VariableArrayElement(index = 4), + @VariableArrayElement(index = 5) + }) + }) + public static void main(String[] args) { + int[] arr1 = new int[]{4, 5, 6, 7}; + int[] arr2 = new int[]{42, 23}; + int[] arr3 = new int[6]; + System.arraycopy(arr1, 1, arr3, 2, 3); + + System.out.println("arr1: {" + arr1[0] + ", " + arr1[1] + ", " + arr1[2] + ", " + arr1[3] + "}; arr2: {" + + arr2[0] + ", " + arr2[1] + "}; arr3: {" + arr3[0] + ", " + arr3[1] + ", " + arr3[2] + ", " + arr3[3] + + ", " + arr3[4] + ", " + arr3[5] + "}"); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ObjectNativeMethodExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ObjectNativeMethodExample.java new file mode 100644 index 0000000000..c570bb1718 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ObjectNativeMethodExample.java @@ -0,0 +1,26 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.lcp_on_fields; + +import org.opalj.fpcf.properties.lcp_on_fields.ObjectValue; +import org.opalj.fpcf.properties.lcp_on_fields.ObjectValues; +import org.opalj.fpcf.properties.lcp_on_fields.VariableValue; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; + +public class ObjectNativeMethodExample { + int a = 2; + + @VariableValue(variable = "lv0") + @ObjectValues({ + @ObjectValue(variable = "lv2", constantValues = { + @ConstantValue(variable = "a", value = 2) + }) + }) + public static void main(String[] args) { + ObjectNativeMethodExample example1 = new ObjectNativeMethodExample(); + ObjectNativeMethodExample example2 = new ObjectNativeMethodExample(); + + Class clazz = example1.getClass(); + + System.out.println("example1.a: " + example1.a + ", example2.a: " + example2.a + ", clazz: " + clazz.getName()); + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala index 0e73ed2a15..e4832c1f6c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -3,6 +3,7 @@ package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem import scala.collection.immutable +import org.opalj.ide.problem.AllBottomEdgeFunction import org.opalj.ide.problem.AllTopEdgeFunction import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.IdentityEdgeFunction @@ -34,8 +35,9 @@ case class ObjectEdgeFunction( case PutFieldEdgeFunction(fieldName, value) => ObjectEdgeFunction((values - fieldName) + (fieldName -> value)) - case IdentityEdgeFunction() => this - case AllTopEdgeFunction(_) => secondEdgeFunction + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => secondEdgeFunction + case AllBottomEdgeFunction(_) => secondEdgeFunction case _ => throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") @@ -56,8 +58,9 @@ case class ObjectEdgeFunction( case PutFieldEdgeFunction(_, _) => throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") - case IdentityEdgeFunction() => this - case AllTopEdgeFunction(_) => this + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => this + case AllBottomEdgeFunction(_) => otherEdgeFunction case _ => throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") @@ -103,8 +106,9 @@ case class PutFieldEdgeFunction( case PutFieldEdgeFunction(fieldName2, value2) => ObjectEdgeFunction(immutable.Map(fieldName -> value, fieldName2 -> value2)) - case IdentityEdgeFunction() => this - case AllTopEdgeFunction(_) => secondEdgeFunction + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => secondEdgeFunction + case AllBottomEdgeFunction(_) => secondEdgeFunction case _ => throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") @@ -121,8 +125,9 @@ case class PutFieldEdgeFunction( case PutFieldEdgeFunction(_, _) => throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") - case IdentityEdgeFunction() => this - case AllTopEdgeFunction(_) => this + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => this + case AllBottomEdgeFunction(_) => otherEdgeFunction case _ => throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") @@ -143,9 +148,9 @@ case class PutFieldEdgeFunction( * The initial value is used as a fallback/default value for elements that are not in the collection of elements yet * (will likely be one of `ConstantValue(0)` and `VariableValue`). */ -case class ArrayEdgeFunction( - initValue: LinearConstantPropagationValue, - elements: immutable.Map[Int, LinearConstantPropagationValue] +class ArrayEdgeFunction( + val initValue: LinearConstantPropagationValue, + val elements: immutable.Map[Int, LinearConstantPropagationValue] ) extends EdgeFunction[LCPOnFieldsValue] { override def compute(sourceValue: LCPOnFieldsValue): LCPOnFieldsValue = sourceValue match { @@ -159,6 +164,8 @@ case class ArrayEdgeFunction( override def composeWith(secondEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = secondEdgeFunction match { + case NewArrayEdgeFunction(_) => secondEdgeFunction + case ArrayEdgeFunction(_, _) => throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") @@ -168,14 +175,15 @@ case class ArrayEdgeFunction( /* In this case it is unknown which indices will be affected */ UnknownValueEdgeFunction case linear_constant_propagation.problem.ConstantValue(i) => - ArrayEdgeFunction(initValue, (elements - i) + (i -> value)) + new ArrayEdgeFunction(initValue, (elements - i) + (i -> value)) case linear_constant_propagation.problem.VariableValue => /* In this case any index could be affected */ - ArrayEdgeFunction(linear_constant_propagation.problem.VariableValue, immutable.Map.empty) + new ArrayEdgeFunction(linear_constant_propagation.problem.VariableValue, immutable.Map.empty) } - case IdentityEdgeFunction() => this - case AllTopEdgeFunction(_) => secondEdgeFunction + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => secondEdgeFunction + case AllBottomEdgeFunction(_) => secondEdgeFunction case _ => throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") @@ -184,7 +192,7 @@ case class ArrayEdgeFunction( override def meetWith(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = otherEdgeFunction match { case ArrayEdgeFunction(initValue2, elements2) => - ArrayEdgeFunction( + new ArrayEdgeFunction( LinearConstantPropagationLattice.meet(initValue, initValue2), elements.keySet.union(elements2.keySet) .map { index => @@ -199,8 +207,9 @@ case class ArrayEdgeFunction( case PutElementEdgeFunction(_, _) => throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") - case IdentityEdgeFunction() => this - case AllTopEdgeFunction(_) => this + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => this + case AllBottomEdgeFunction(_) => otherEdgeFunction case _ => throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") @@ -214,12 +223,20 @@ case class ArrayEdgeFunction( }) } +object ArrayEdgeFunction { + def unapply(arrayEdgeFunction: ArrayEdgeFunction): Some[( + LinearConstantPropagationValue, + immutable.Map[Int, LinearConstantPropagationValue] + )] = Some((arrayEdgeFunction.initValue, arrayEdgeFunction.elements)) +} + /** * Edge function for initializing an array */ -object NewArrayEdgeFunction - extends ArrayEdgeFunction(linear_constant_propagation.problem.ConstantValue(0), immutable.Map.empty) { - override def toString: String = "NewArrayEdgeFunction()" +case class NewArrayEdgeFunction( + override val initValue: LinearConstantPropagationValue = linear_constant_propagation.problem.ConstantValue(0) +) extends ArrayEdgeFunction(initValue, immutable.Map.empty) { + override def toString: String = s"NewArrayEdgeFunction($initValue)" } /** @@ -248,16 +265,19 @@ case class PutElementEdgeFunction( override def composeWith(secondEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = { secondEdgeFunction match { + case NewArrayEdgeFunction(_) => secondEdgeFunction + case ArrayEdgeFunction(_, _) => throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") case PutElementEdgeFunction(index2, _) if index == index2 => secondEdgeFunction case PutElementEdgeFunction(_, _) => - ArrayEdgeFunction(linear_constant_propagation.problem.UnknownValue, immutable.Map.empty) + new ArrayEdgeFunction(linear_constant_propagation.problem.UnknownValue, immutable.Map.empty) .composeWith(this).composeWith(secondEdgeFunction) - case IdentityEdgeFunction() => this - case AllTopEdgeFunction(_) => secondEdgeFunction + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => secondEdgeFunction + case AllBottomEdgeFunction(_) => secondEdgeFunction case _ => throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") @@ -275,8 +295,9 @@ case class PutElementEdgeFunction( LinearConstantPropagationLattice.meet(value, value2) ) - case IdentityEdgeFunction() => this - case AllTopEdgeFunction(_) => this + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => this + case AllBottomEdgeFunction(_) => otherEdgeFunction case _ => throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") @@ -300,4 +321,20 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LCPOnFieldsValue](Unk ): EdgeFunction[LCPOnFieldsValue] = { this } + + override def toString: String = "UnknownValueEdgeFunction()" +} + +/** + * Edge function for cases where a value is variable + */ +object VariableValueEdgeFunction extends AllBottomEdgeFunction[LCPOnFieldsValue](VariableValue) { + override def composeWith(secondEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = { + secondEdgeFunction match { + case NewObjectEdgeFunction => secondEdgeFunction + case _ => this + } + } + + override def toString: String = "VariableValueEdgeFunction()" } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 95c04065c4..c12b8cf339 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -9,6 +9,7 @@ import org.opalj.fpcf.FinalP import org.opalj.fpcf.InterimUBP import org.opalj.fpcf.Property import org.opalj.fpcf.PropertyStore +import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.FinalEdgeFunction import org.opalj.ide.problem.FlowFunction @@ -44,62 +45,64 @@ class LCPOnFieldsProblem(project: SomeProject) override def getNormalFlowFunction(source: JavaStatement, target: JavaStatement)( implicit propertyStore: PropertyStore - ): FlowFunction[LCPOnFieldsFact] = - (sourceFact: LCPOnFieldsFact) => { - (source.stmt.astID, sourceFact) match { - case (Assignment.ASTID, NullFact) => - val assignment = source.stmt.asAssignment - assignment.expr.astID match { - case New.ASTID => - /* Generate new object fact from null fact if assignment is a 'new' expression */ - immutable.Set(sourceFact, NewObjectFact(assignment.targetVar.name, source.pc)) - - case NewArray.ASTID => - /* Generate new array fact from null fact if assignment is a 'new array' expression for an - * integer array */ - if (assignment.expr.asNewArray.tpe.componentType.isIntegerType) { - immutable.Set(sourceFact, NewArrayFact(assignment.targetVar.name, source.pc)) + ): FlowFunction[LCPOnFieldsFact] = { + (sourceFact: LCPOnFieldsFact) => + { + (source.stmt.astID, sourceFact) match { + case (Assignment.ASTID, NullFact) => + val assignment = source.stmt.asAssignment + assignment.expr.astID match { + case New.ASTID => + /* Generate new object fact from null fact if assignment is a 'new' expression */ + immutable.Set(sourceFact, NewObjectFact(assignment.targetVar.name, source.pc)) + + case NewArray.ASTID => + /* Generate new array fact from null fact if assignment is a 'new array' expression for + * an integer array */ + if (assignment.expr.asNewArray.tpe.componentType.isIntegerType) { + immutable.Set(sourceFact, NewArrayFact(assignment.targetVar.name, source.pc)) + } else { + immutable.Set(sourceFact) + } + + case _ => immutable.Set(sourceFact) + } + + case (PutField.ASTID, f: AbstractObjectFact) => + val putField = source.stmt.asPutField + /* Only consider field assignments for integers */ + if (putField.declaredFieldType.isIntegerType) { + val targetObject = putField.objRef.asVar + if (targetObject.definedBy.contains(f.definedAtIndex)) { + /* Generate new (short-lived) fact to handle field assignment */ + immutable.Set(PutFieldFact(f.name, f.definedAtIndex, putField.name)) } else { - immutable.Set(sourceFact) + immutable.Set(f.toObjectFact) } - - case _ => immutable.Set(sourceFact) - } - - case (PutField.ASTID, f: AbstractObjectFact) => - val putField = source.stmt.asPutField - /* Only consider field assignments for integers */ - if (putField.declaredFieldType.isIntegerType) { - val targetObject = putField.objRef.asVar - if (targetObject.definedBy.contains(f.definedAtIndex)) { - /* Generate new (short-lived) fact to handle field assignment */ - immutable.Set(PutFieldFact(f.name, f.definedAtIndex, putField.name)) } else { immutable.Set(f.toObjectFact) } - } else { - immutable.Set(f.toObjectFact) - } - - case (ArrayStore.ASTID, f: AbstractArrayFact) => - val arrayStore = source.stmt.asArrayStore - val arrayVar = arrayStore.arrayRef.asVar - if (arrayVar.definedBy.contains(f.definedAtIndex)) { - immutable.Set(PutElementFact(f.name, f.definedAtIndex)) - } else { - immutable.Set(f.toArrayFact) - } - - case (_, f: AbstractEntityFact) => - /* Specialized facts only live for one step and are turned back into basic ones afterwards */ - immutable.Set(f match { - case f: AbstractObjectFact => f.toObjectFact - case f: AbstractArrayFact => f.toArrayFact - }) - - case _ => immutable.Set(sourceFact) + + case (ArrayStore.ASTID, f: AbstractArrayFact) => + val arrayStore = source.stmt.asArrayStore + val arrayVar = arrayStore.arrayRef.asVar + if (arrayVar.definedBy.contains(f.definedAtIndex)) { + immutable.Set(PutElementFact(f.name, f.definedAtIndex)) + } else { + immutable.Set(f.toArrayFact) + } + + case (_, f: AbstractEntityFact) => + /* Specialized facts only live for one step and are turned back into basic ones afterwards */ + immutable.Set(f match { + case f: AbstractObjectFact => f.toObjectFact + case f: AbstractArrayFact => f.toArrayFact + }) + + case _ => immutable.Set(sourceFact) + } } - } + } override def getCallFlowFunction( callSite: JavaStatement, @@ -108,44 +111,39 @@ class LCPOnFieldsProblem(project: SomeProject) )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { (sourceFact: LCPOnFieldsFact) => { - // TODO (IDE) REMOVE ONCE PRECOMPUTED SUMMARIES ARE IMPLEMENTED - if (callee.classFile.thisType.fqn.startsWith("java/") && !callee.isConstructor) { - immutable.Set.empty - } else { - sourceFact match { - case NullFact => - /* Only propagate null fact if function returns an object or an array of integers */ - if (callee.returnType.isObjectType) { - immutable.Set(sourceFact) - } else if (callee.returnType.isArrayType && - callee.returnType.asArrayType.componentType.isIntegerType - ) { - immutable.Set(sourceFact) - } else { - immutable.Set.empty - } + sourceFact match { + case NullFact => + /* Only propagate null fact if function returns an object or an array of integers */ + if (callee.returnType.isObjectType) { + immutable.Set(sourceFact) + } else if (callee.returnType.isArrayType && + callee.returnType.asArrayType.componentType.isIntegerType + ) { + immutable.Set(sourceFact) + } else { + immutable.Set.empty + } - case f: AbstractEntityFact => - val callStmt = callSite.stmt.asCall() + case f: AbstractEntityFact => + val callStmt = callSite.stmt.asCall() - /* All parameters (including the implicit 'this' reference) */ - val allParams = callStmt.allParams + /* All parameters (including the implicit 'this' reference) */ + val allParams = callStmt.allParams - allParams - .zipWithIndex - .filter { case (param, _) => - /* Only parameters where the variable represented by the source fact is one possible - * initializer */ - param.asVar.definedBy.contains(f.definedAtIndex) - } - .map { case (_, index) => - f match { - case _: AbstractObjectFact => ObjectFact(s"param$index", -(index + 1)) - case _: AbstractArrayFact => ArrayFact(s"param$index", -(index + 1)) - } + allParams + .zipWithIndex + .filter { case (param, _) => + /* Only parameters where the variable represented by the source fact is one possible + * initializer */ + param.asVar.definedBy.contains(f.definedAtIndex) + } + .map { case (_, index) => + f match { + case _: AbstractObjectFact => ObjectFact(s"param$index", -(index + 1)) + case _: AbstractArrayFact => ArrayFact(s"param$index", -(index + 1)) } - .toSet - } + } + .toSet } } } @@ -289,7 +287,7 @@ class LCPOnFieldsProblem(project: SomeProject) } case NewArrayFact(_, _) => - FinalEdgeFunction(NewArrayEdgeFunction) + FinalEdgeFunction(NewArrayEdgeFunction()) case PutElementFact(_, _) => val arrayStore = source.stmt.asArrayStore @@ -366,4 +364,72 @@ class LCPOnFieldsProblem(project: SomeProject) returnSite: JavaStatement, returnSiteFact: LCPOnFieldsFact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LCPOnFieldsValue] = identityEdgeFunction + + override def hasPrecomputedFlowAndSummaryFunction( + callSite: JavaStatement, + callSiteFact: LCPOnFieldsFact, + callee: Method + )(implicit propertyStore: PropertyStore): Boolean = { + if (callee.isNative) { + return true + } + + super.hasPrecomputedFlowAndSummaryFunction(callSite, callSiteFact, callee) + } + + override def getPrecomputedFlowFunction(callSite: JavaStatement, callee: Method, returnSite: JavaStatement)(implicit + propertyStore: PropertyStore + ): FlowFunction[LCPOnFieldsFact] = { + if (callee.isNative) { + return (sourceFact: LCPOnFieldsFact) => { + val callStmt = callSite.stmt.asCall() + + sourceFact match { + case NullFact => + returnSite.stmt.astID match { + case Assignment.ASTID => + val assignment = returnSite.stmt.asAssignment + immutable.Set(NewObjectFact(assignment.targetVar.name, returnSite.pc)) + + case _ => immutable.Set.empty + } + + case f: AbstractEntityFact => + /* Check whether fact corresponds to one of the parameters */ + if (callStmt.allParams.exists { param => param.asVar.definedBy.contains(f.definedAtIndex) }) { + immutable.Set(f match { + case f: AbstractObjectFact => f.toObjectFact + case f: AbstractArrayFact => f.toArrayFact + }) + } else { + immutable.Set.empty + } + } + } + } + + super.getPrecomputedFlowFunction(callSite, callee, returnSite) + } + + override def getPrecomputedSummaryFunction( + callSite: JavaStatement, + callSiteFact: LCPOnFieldsFact, + callee: Method, + returnSite: JavaStatement, + returnSiteFact: LCPOnFieldsFact + )(implicit propertyStore: PropertyStore): EdgeFunction[LCPOnFieldsValue] = { + if (callee.isNative) { + return returnSiteFact match { + case _: AbstractObjectFact => + VariableValueEdgeFunction + + case _: AbstractArrayFact => + NewArrayEdgeFunction(linear_constant_propagation.problem.VariableValue) + + case _ => identityEdgeFunction + } + } + + super.getPrecomputedSummaryFunction(callSite, callSiteFact, callee, returnSite, returnSiteFact) + } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index d92cc6639f..d091d51a12 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -10,6 +10,7 @@ import org.opalj.ai.domain.l1.DefaultIntegerRangeValues import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.PropertyStore +import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.FlowFunction import org.opalj.ide.problem.MeetLattice @@ -503,4 +504,40 @@ class LinearConstantPropagationProblem(project: SomeProject) returnSite: JavaStatement, returnSiteFact: LinearConstantPropagationFact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = identityEdgeFunction + + override def hasPrecomputedFlowAndSummaryFunction( + callSite: JavaStatement, + callSiteFact: LinearConstantPropagationFact, + callee: Method + )(implicit propertyStore: PropertyStore): Boolean = { + if (callee.isNative) { + return true + } + + super.hasPrecomputedFlowAndSummaryFunction(callSite, callSiteFact, callee) + } + + override def getPrecomputedFlowFunction(callSite: JavaStatement, callee: Method, returnSite: JavaStatement)( + implicit propertyStore: PropertyStore + ): FlowFunction[LinearConstantPropagationFact] = { + if (callee.isNative) { + return emptyFlowFunction + } + + super.getPrecomputedFlowFunction(callSite, callee, returnSite) + } + + override def getPrecomputedSummaryFunction( + callSite: JavaStatement, + callSiteFact: LinearConstantPropagationFact, + callee: Method, + returnSite: JavaStatement, + returnSiteFact: LinearConstantPropagationFact + )(implicit propertyStore: PropertyStore): EdgeFunction[LinearConstantPropagationValue] = { + if (callee.isNative) { + return identityEdgeFunction + } + + super.getPrecomputedSummaryFunction(callSite, callSiteFact, callee, returnSite, returnSiteFact) + } } From 9d3725b1726087ad906fb20ab78de2c8abd760dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 7 Oct 2024 17:20:25 +0200 Subject: [PATCH 086/167] Refactor LCP on fields facts --- .../lcp_on_fields/problem/LCPOnFieldsFact.scala | 6 ++++++ .../problem/LCPOnFieldsProblem.scala | 15 +++------------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala index 7184a5c48e..ce534f4044 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala @@ -25,6 +25,8 @@ trait AbstractEntityFact extends LCPOnFieldsFact { * Where the variable is defined (used to uniquely identify a variable/variable fact) */ val definedAtIndex: Int + + def toObjectOrArrayFact: AbstractEntityFact } /** @@ -32,6 +34,8 @@ trait AbstractEntityFact extends LCPOnFieldsFact { */ trait AbstractObjectFact extends AbstractEntityFact { def toObjectFact: ObjectFact = ObjectFact(name, definedAtIndex) + + override def toObjectOrArrayFact: AbstractEntityFact = toObjectFact } /** @@ -63,6 +67,8 @@ case class PutFieldFact(name: String, definedAtIndex: Int, fieldName: String) ex */ trait AbstractArrayFact extends AbstractEntityFact { def toArrayFact: ArrayFact = ArrayFact(name, definedAtIndex) + + override def toObjectOrArrayFact: AbstractEntityFact = toArrayFact } /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index c12b8cf339..bd032a7d20 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -94,10 +94,7 @@ class LCPOnFieldsProblem(project: SomeProject) case (_, f: AbstractEntityFact) => /* Specialized facts only live for one step and are turned back into basic ones afterwards */ - immutable.Set(f match { - case f: AbstractObjectFact => f.toObjectFact - case f: AbstractArrayFact => f.toArrayFact - }) + immutable.Set(f.toObjectOrArrayFact) case _ => immutable.Set(sourceFact) } @@ -224,10 +221,7 @@ class LCPOnFieldsProblem(project: SomeProject) if (callStmt.allParams.exists { param => param.asVar.definedBy.contains(f.definedAtIndex) }) { immutable.Set.empty } else { - immutable.Set(f match { - case f: AbstractObjectFact => f.toObjectFact - case f: AbstractArrayFact => f.toArrayFact - }) + immutable.Set(f.toObjectOrArrayFact) } } } @@ -397,10 +391,7 @@ class LCPOnFieldsProblem(project: SomeProject) case f: AbstractEntityFact => /* Check whether fact corresponds to one of the parameters */ if (callStmt.allParams.exists { param => param.asVar.definedBy.contains(f.definedAtIndex) }) { - immutable.Set(f match { - case f: AbstractObjectFact => f.toObjectFact - case f: AbstractArrayFact => f.toArrayFact - }) + immutable.Set(f.toObjectOrArrayFact) } else { immutable.Set.empty } From 60f04a98cd9d4970bc95885be3ced2801f8204a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 7 Oct 2024 17:28:16 +0200 Subject: [PATCH 087/167] Prepare problem definitions for interim flow functions --- .../ide/problem/FlowRecordingIDEProblem.scala | 16 +++--- .../problem/LCPOnFieldsProblem.scala | 56 ++++++++++--------- .../LinearConstantPropagationProblem.scala | 15 +++-- 3 files changed, 48 insertions(+), 39 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index fa62cb620b..3fcc040a42 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -30,19 +30,19 @@ object FlowRecorderModes extends Enumeration { * @param recordEdgeFunctions whether to record edge functions too or just stick with the flow */ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( - val baseProblem: IDEProblem[Fact, Value, Statement, Callable], - val recorderMode: FlowRecorderModes.FlowRecorderMode = FlowRecorderModes.NODE_AS_STMT, - val uniqueFlowsOnly: Boolean = true, - val recordEdgeFunctions: Boolean = false + val baseProblem: IDEProblem[Fact, Value, Statement, Callable], + val recorderMode: FlowRecorderModes.FlowRecorderMode = FlowRecorderModes.NODE_AS_STMT, + val uniqueFlowsOnly: Boolean = true, + val recordEdgeFunctions: Boolean = false ) extends IDEProblem[Fact, Value, Statement, Callable](baseProblem.icfg) { /** * Wrapper class for flow functions doing the actual recording */ private class RecordingFlowFunction( - baseFlowFunction: FlowFunction[Fact], - val source: Statement, - val target: Statement, - val flowType: String + baseFlowFunction: FlowFunction[Fact], + val source: Statement, + val target: Statement, + val flowType: String ) extends FlowFunction[Fact] { override def compute(sourceFact: Fact): collection.Set[Fact] = { val facts = baseFlowFunction.compute(sourceFact) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index bd032a7d20..47d9a346a2 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -46,8 +46,8 @@ class LCPOnFieldsProblem(project: SomeProject) override def getNormalFlowFunction(source: JavaStatement, target: JavaStatement)( implicit propertyStore: PropertyStore ): FlowFunction[LCPOnFieldsFact] = { - (sourceFact: LCPOnFieldsFact) => - { + new FlowFunction[LCPOnFieldsFact] { + override def compute(sourceFact: LCPOnFieldsFact): collection.Set[LCPOnFieldsFact] = { (source.stmt.astID, sourceFact) match { case (Assignment.ASTID, NullFact) => val assignment = source.stmt.asAssignment @@ -99,6 +99,7 @@ class LCPOnFieldsProblem(project: SomeProject) case _ => immutable.Set(sourceFact) } } + } } override def getCallFlowFunction( @@ -106,8 +107,8 @@ class LCPOnFieldsProblem(project: SomeProject) calleeEntry: JavaStatement, callee: Method )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { - (sourceFact: LCPOnFieldsFact) => - { + new FlowFunction[LCPOnFieldsFact] { + override def compute(sourceFact: LCPOnFieldsFact): collection.Set[LCPOnFieldsFact] = { sourceFact match { case NullFact => /* Only propagate null fact if function returns an object or an array of integers */ @@ -143,6 +144,7 @@ class LCPOnFieldsProblem(project: SomeProject) .toSet } } + } } override def getReturnFlowFunction( @@ -150,8 +152,8 @@ class LCPOnFieldsProblem(project: SomeProject) callee: Method, returnSite: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { - (sourceFact: LCPOnFieldsFact) => - { + new FlowFunction[LCPOnFieldsFact] { + override def compute(sourceFact: LCPOnFieldsFact): collection.Set[LCPOnFieldsFact] = { sourceFact match { case NullFact => /* Always propagate null fact */ @@ -199,6 +201,7 @@ class LCPOnFieldsProblem(project: SomeProject) } } } + } } override def getCallToReturnFlowFunction( @@ -206,8 +209,8 @@ class LCPOnFieldsProblem(project: SomeProject) callee: Method, returnSite: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { - (sourceFact: LCPOnFieldsFact) => - { + new FlowFunction[LCPOnFieldsFact] { + override def compute(sourceFact: LCPOnFieldsFact): collection.Set[LCPOnFieldsFact] = { val callStmt = returnSite.stmt.asCall() sourceFact match { @@ -225,6 +228,7 @@ class LCPOnFieldsProblem(project: SomeProject) } } } + } } override def getNormalEdgeFunction( @@ -375,26 +379,28 @@ class LCPOnFieldsProblem(project: SomeProject) propertyStore: PropertyStore ): FlowFunction[LCPOnFieldsFact] = { if (callee.isNative) { - return (sourceFact: LCPOnFieldsFact) => { - val callStmt = callSite.stmt.asCall() + return new FlowFunction[LCPOnFieldsFact] { + override def compute(sourceFact: LCPOnFieldsFact): collection.Set[LCPOnFieldsFact] = { + val callStmt = callSite.stmt.asCall() - sourceFact match { - case NullFact => - returnSite.stmt.astID match { - case Assignment.ASTID => - val assignment = returnSite.stmt.asAssignment - immutable.Set(NewObjectFact(assignment.targetVar.name, returnSite.pc)) + sourceFact match { + case NullFact => + returnSite.stmt.astID match { + case Assignment.ASTID => + val assignment = returnSite.stmt.asAssignment + immutable.Set(NewObjectFact(assignment.targetVar.name, returnSite.pc)) - case _ => immutable.Set.empty - } + case _ => immutable.Set.empty + } - case f: AbstractEntityFact => - /* Check whether fact corresponds to one of the parameters */ - if (callStmt.allParams.exists { param => param.asVar.definedBy.contains(f.definedAtIndex) }) { - immutable.Set(f.toObjectOrArrayFact) - } else { - immutable.Set.empty - } + case f: AbstractEntityFact => + /* Check whether fact corresponds to one of the parameters */ + if (callStmt.allParams.exists { param => param.asVar.definedBy.contains(f.definedAtIndex) }) { + immutable.Set(f.toObjectOrArrayFact) + } else { + immutable.Set.empty + } + } } } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index d091d51a12..3a175ec051 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -52,8 +52,8 @@ class LinearConstantPropagationProblem(project: SomeProject) source: JavaStatement, target: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { - (sourceFact: LinearConstantPropagationFact) => - { + new FlowFunction[LinearConstantPropagationFact] { + override def compute(sourceFact: LinearConstantPropagationFact): collection.Set[LinearConstantPropagationFact] = { source.stmt.astID match { case Assignment.ASTID => val assignment = source.stmt.asAssignment @@ -68,6 +68,7 @@ class LinearConstantPropagationProblem(project: SomeProject) case _ => immutable.Set(sourceFact) } } + } } private def isExpressionGeneratedByFact( @@ -217,8 +218,8 @@ class LinearConstantPropagationProblem(project: SomeProject) calleeEntry: JavaStatement, callee: Method )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { - (sourceFact: LinearConstantPropagationFact) => - { + new FlowFunction[LinearConstantPropagationFact] { + override def compute(sourceFact: LinearConstantPropagationFact): collection.Set[LinearConstantPropagationFact] = { /* Only propagate to callees that return integers */ if (!callee.returnType.isIntegerType) { immutable.Set.empty @@ -249,6 +250,7 @@ class LinearConstantPropagationProblem(project: SomeProject) } } } + } } override def getReturnFlowFunction( @@ -256,8 +258,8 @@ class LinearConstantPropagationProblem(project: SomeProject) callee: Method, returnSite: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { - (sourceFact: LinearConstantPropagationFact) => - { + new FlowFunction[LinearConstantPropagationFact] { + override def compute(sourceFact: LinearConstantPropagationFact): collection.Set[LinearConstantPropagationFact] = { /* Only propagate to return site if callee returns an integer */ if (!callee.returnType.isIntegerType) { immutable.Set.empty @@ -286,6 +288,7 @@ class LinearConstantPropagationProblem(project: SomeProject) } } } + } } override def getCallToReturnFlowFunction( From 8a2eb83984e3332a08ca6522c0e8b92770ec7159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 9 Oct 2024 10:43:24 +0200 Subject: [PATCH 088/167] Implement interim flow functions --- .../org/opalj/ide/problem/FlowFunction.scala | 25 ++++++++-- .../ide/problem/FlowRecordingIDEProblem.scala | 6 +-- .../org/opalj/ide/solver/IDEAnalysis.scala | 48 ++++++++++++++++--- .../problem/LCPOnFieldsProblem.scala | 10 ++-- .../LinearConstantPropagationProblem.scala | 6 +-- 5 files changed, 72 insertions(+), 23 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala index 8a13f01e97..38691cba80 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala @@ -1,24 +1,39 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ide.problem +import scala.language.implicitConversions + import scala.collection.immutable +import org.opalj.fpcf.SomeEOptionP + /** * Interface representing IDE flow functions */ trait FlowFunction[Fact <: IDEFact] { + type FactsAndDependees = FlowFunction.FactsAndDependees[Fact] + + implicit def setOfFactsToFactsAndDependees(facts: collection.Set[? <: Fact]): FactsAndDependees = { + (facts.toSet, immutable.Set.empty) + } + /** - * Compute the facts that are generated by this flow function when seeing a fact + * Compute the facts that are generated by this flow function when seeing a fact and the dependees that can cause + * new facts to be generated * @param sourceFact the incoming fact */ - def compute(sourceFact: Fact): collection.Set[Fact] + def compute(sourceFact: Fact): FactsAndDependees +} + +object FlowFunction { + type FactsAndDependees[Fact] = (collection.Set[Fact], collection.Set[SomeEOptionP]) } /** * Special flow function that always returns the input fact */ case class IdentityFlowFunction[Fact <: IDEFact]() extends FlowFunction[Fact] { - override def compute(sourceFact: Fact): collection.Set[Fact] = + override def compute(sourceFact: Fact): FactsAndDependees = immutable.Set(sourceFact) } @@ -26,6 +41,6 @@ case class IdentityFlowFunction[Fact <: IDEFact]() extends FlowFunction[Fact] { * Special flow function that always returns an empty set */ case class EmptyFlowFunction[Fact <: IDEFact]() extends FlowFunction[Fact] { - override def compute(sourceFact: Fact): collection.Set[Fact] = - immutable.Set.empty + override def compute(sourceFact: Fact): FactsAndDependees = + immutable.Set.empty[Fact] } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index 3fcc040a42..e5bd8fb972 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -44,10 +44,10 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal val target: Statement, val flowType: String ) extends FlowFunction[Fact] { - override def compute(sourceFact: Fact): collection.Set[Fact] = { - val facts = baseFlowFunction.compute(sourceFact) + override def compute(sourceFact: Fact): FactsAndDependees = { + val (facts, dependees) = baseFlowFunction.compute(sourceFact) facts.foreach { fact => collectedFlows.addOne(createDotEdge(source, sourceFact, target, fact, flowType)) } - facts + (facts, dependees) } } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index cd25e95371..535d25d139 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -23,6 +23,7 @@ import org.opalj.ide.problem.AllTopEdgeFunction import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.FinalEdgeFunction +import org.opalj.ide.problem.FlowFunction import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IdentityEdgeFunction import org.opalj.ide.problem.IDEProblem @@ -497,7 +498,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent /* Handling for precomputed summaries */ rs.foreach { r => - val d5s = problem.getPrecomputedFlowFunction(n, q, r).compute(d2) + val d5s = handleFlowFunctionResult(problem.getPrecomputedFlowFunction(n, q, r).compute(d2), path) logTrace(s"generated the following d5s=$d5s for return statement r=${icfg.stringifyStatement(r)}") @@ -521,7 +522,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val sqs = icfg.getStartStatements(q) sqs.foreach { sq => // IDE P1 lines 12 - 13 - val d3s = problem.getCallFlowFunction(n, sq, q).compute(d2) + val d3s = handleFlowFunctionResult(problem.getCallFlowFunction(n, sq, q).compute(d2), path) logTrace(s"generated the following d3s=$d3s for start statement sq=${icfg.stringifyStatement(sq)}") @@ -534,7 +535,10 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent endSummaries.foreach { case ((eq, d4), fEndSummary) => val f4 = handleEdgeFunctionResult(problem.getCallEdgeFunction(n, d2, sq, d3, q), path) rs.foreach { r => - val d5s = problem.getReturnFlowFunction(eq, q, r).compute(d4) + val d5s = handleFlowFunctionResult( + problem.getReturnFlowFunction(eq, q, r).compute(d4), + path + ) d5s.foreach { d5 => val f5 = handleEdgeFunctionResult( problem.getReturnEdgeFunction(eq, d4, q, r, d5), @@ -562,7 +566,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } rs.foreach { r => - val d3s = problem.getCallToReturnFlowFunction(n, q, r).compute(d2) + val d3s = handleFlowFunctionResult(problem.getCallToReturnFlowFunction(n, q, r).compute(d2), path) logTrace(s"generated the following d3s=$d3s for return-site statement r=${icfg.stringifyStatement(r)}") @@ -606,7 +610,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logDebug(s"handling calling statement c=${icfg.stringifyStatement(c)}, d4=$d4 and return-site statement r=${icfg.stringifyStatement(r)}") // IDE P1 line 21 - val d5s = problem.getReturnFlowFunction(n, p, r).compute(d2) + val d5s = handleFlowFunctionResult(problem.getReturnFlowFunction(n, p, r).compute(d2), path) logDebug(s"generated the following d5s=$d5s") @@ -646,7 +650,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // IDE P1 lines 31 - 32 icfg.getNextStatements(n).foreach { m => - val d3s = problem.getNormalFlowFunction(n, m).compute(d2) + val d3s = handleFlowFunctionResult(problem.getNormalFlowFunction(n, m).compute(d2), path) logTrace(s"generated the following d3s=$d3s for next statement m=${icfg.stringifyStatement(m)}") @@ -676,6 +680,26 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } } + /** + * @param path the path to re-enqueue when encountering an interim flow function + * @return the (interim) generated flow facts + */ + private def handleFlowFunctionResult( + factsAndDependees: FlowFunction.FactsAndDependees[Fact], + path: Path + )(implicit s: State): collection.Set[Fact] = { + val (facts, dependees) = factsAndDependees + if (dependees.nonEmpty) { + dependees.foreach { dependee => + s.addDependee( + dependee, + () => s.enqueuePath(path) + ) + } + } + facts + } + /** * @param path the path to re-enqueue when getting an interim edge function * @return the (interim) edge function from the result @@ -821,7 +845,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent if (!problem.hasPrecomputedFlowAndSummaryFunction(n, d, q)) { val sqs = icfg.getStartStatements(q) sqs.foreach { sq => - val dPrimes = problem.getCallFlowFunction(n, sq, q).compute(d) + val dPrimes = extractFlowFunctionResult(problem.getCallFlowFunction(n, sq, q).compute(d)) dPrimes.foreach { dPrime => propagateValue( (sq, dPrime), @@ -851,6 +875,16 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } } + /** + * Extract flow function result while ignoring the dependees + */ + private def extractFlowFunctionResult( + factsAndDependees: FlowFunction.FactsAndDependees[Fact] + ): collection.Set[Fact] = { + val (facts, dependees) = factsAndDependees + facts + } + // TODO (IDE) THIS WILL NOT BE POSSIBLE ANY LONGER IF RETURNING AN INTERIM RESULT INVOLVES EXECUTING PHASE 2 private def enforceFinalEdgeFunction(edgeFunctionResult: EdgeFunctionResult[Value]): EdgeFunction[Value] = { edgeFunctionResult match { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 47d9a346a2..302e215b00 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -47,7 +47,7 @@ class LCPOnFieldsProblem(project: SomeProject) implicit propertyStore: PropertyStore ): FlowFunction[LCPOnFieldsFact] = { new FlowFunction[LCPOnFieldsFact] { - override def compute(sourceFact: LCPOnFieldsFact): collection.Set[LCPOnFieldsFact] = { + override def compute(sourceFact: LCPOnFieldsFact): FactsAndDependees = { (source.stmt.astID, sourceFact) match { case (Assignment.ASTID, NullFact) => val assignment = source.stmt.asAssignment @@ -108,7 +108,7 @@ class LCPOnFieldsProblem(project: SomeProject) callee: Method )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { new FlowFunction[LCPOnFieldsFact] { - override def compute(sourceFact: LCPOnFieldsFact): collection.Set[LCPOnFieldsFact] = { + override def compute(sourceFact: LCPOnFieldsFact): FactsAndDependees = { sourceFact match { case NullFact => /* Only propagate null fact if function returns an object or an array of integers */ @@ -153,7 +153,7 @@ class LCPOnFieldsProblem(project: SomeProject) returnSite: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { new FlowFunction[LCPOnFieldsFact] { - override def compute(sourceFact: LCPOnFieldsFact): collection.Set[LCPOnFieldsFact] = { + override def compute(sourceFact: LCPOnFieldsFact): FactsAndDependees = { sourceFact match { case NullFact => /* Always propagate null fact */ @@ -210,7 +210,7 @@ class LCPOnFieldsProblem(project: SomeProject) returnSite: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { new FlowFunction[LCPOnFieldsFact] { - override def compute(sourceFact: LCPOnFieldsFact): collection.Set[LCPOnFieldsFact] = { + override def compute(sourceFact: LCPOnFieldsFact): FactsAndDependees = { val callStmt = returnSite.stmt.asCall() sourceFact match { @@ -380,7 +380,7 @@ class LCPOnFieldsProblem(project: SomeProject) ): FlowFunction[LCPOnFieldsFact] = { if (callee.isNative) { return new FlowFunction[LCPOnFieldsFact] { - override def compute(sourceFact: LCPOnFieldsFact): collection.Set[LCPOnFieldsFact] = { + override def compute(sourceFact: LCPOnFieldsFact): FactsAndDependees = { val callStmt = callSite.stmt.asCall() sourceFact match { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 3a175ec051..a40ddc23ba 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -53,7 +53,7 @@ class LinearConstantPropagationProblem(project: SomeProject) target: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { new FlowFunction[LinearConstantPropagationFact] { - override def compute(sourceFact: LinearConstantPropagationFact): collection.Set[LinearConstantPropagationFact] = { + override def compute(sourceFact: LinearConstantPropagationFact): FactsAndDependees = { source.stmt.astID match { case Assignment.ASTID => val assignment = source.stmt.asAssignment @@ -219,7 +219,7 @@ class LinearConstantPropagationProblem(project: SomeProject) callee: Method )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { new FlowFunction[LinearConstantPropagationFact] { - override def compute(sourceFact: LinearConstantPropagationFact): collection.Set[LinearConstantPropagationFact] = { + override def compute(sourceFact: LinearConstantPropagationFact): FactsAndDependees = { /* Only propagate to callees that return integers */ if (!callee.returnType.isIntegerType) { immutable.Set.empty @@ -259,7 +259,7 @@ class LinearConstantPropagationProblem(project: SomeProject) returnSite: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { new FlowFunction[LinearConstantPropagationFact] { - override def compute(sourceFact: LinearConstantPropagationFact): collection.Set[LinearConstantPropagationFact] = { + override def compute(sourceFact: LinearConstantPropagationFact): FactsAndDependees = { /* Only propagate to return site if callee returns an integer */ if (!callee.returnType.isIntegerType) { immutable.Set.empty From c5b4d393aa0d6a6821fce63ae5a45027abcf8ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 9 Oct 2024 10:52:03 +0200 Subject: [PATCH 089/167] Adjust dependee handling in phase 2 --- .../org/opalj/ide/solver/IDEAnalysis.scala | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 535d25d139..9527331b70 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -845,11 +845,11 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent if (!problem.hasPrecomputedFlowAndSummaryFunction(n, d, q)) { val sqs = icfg.getStartStatements(q) sqs.foreach { sq => - val dPrimes = extractFlowFunctionResult(problem.getCallFlowFunction(n, sq, q).compute(d)) + val dPrimes = extractFlowFunctionFromResult(problem.getCallFlowFunction(n, sq, q).compute(d)) dPrimes.foreach { dPrime => propagateValue( (sq, dPrime), - enforceFinalEdgeFunction(problem.getCallEdgeFunction(n, d, sq, dPrime, q)) + extractEdgeFunctionFromResult(problem.getCallEdgeFunction(n, d, sq, dPrime, q)) .compute(s.getValue(node)) ) } @@ -876,24 +876,24 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } /** - * Extract flow function result while ignoring the dependees + * Extract facts from flow function result while ignoring the dependees */ - private def extractFlowFunctionResult( + private def extractFlowFunctionFromResult( factsAndDependees: FlowFunction.FactsAndDependees[Fact] ): collection.Set[Fact] = { - val (facts, dependees) = factsAndDependees + val (facts, _) = factsAndDependees facts } - // TODO (IDE) THIS WILL NOT BE POSSIBLE ANY LONGER IF RETURNING AN INTERIM RESULT INVOLVES EXECUTING PHASE 2 - private def enforceFinalEdgeFunction(edgeFunctionResult: EdgeFunctionResult[Value]): EdgeFunction[Value] = { + /** + * Extract edge function from result ignoring the dependees + */ + private def extractEdgeFunctionFromResult(edgeFunctionResult: EdgeFunctionResult[Value]): EdgeFunction[Value] = { edgeFunctionResult match { case FinalEdgeFunction(edgeFunction) => edgeFunction - case _ => - throw new IllegalStateException( - s"All edge functions should be final in phase 2 but got $edgeFunctionResult!" - ) + case InterimEdgeFunction(interimEdgeFunction, _) => + interimEdgeFunction } } } From f7413d38920b43386acb3aeed4f0d4a7f688b8f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 9 Oct 2024 10:56:38 +0200 Subject: [PATCH 090/167] Document restrictions for interim flow functions --- .../ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala index 38691cba80..442ae2a7bb 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala @@ -21,6 +21,8 @@ trait FlowFunction[Fact <: IDEFact] { * Compute the facts that are generated by this flow function when seeing a fact and the dependees that can cause * new facts to be generated * @param sourceFact the incoming fact + * @return a set of facts and a set of dependees (a fact that is returned once must also be returned with every + * subsequent call) */ def compute(sourceFact: Fact): FactsAndDependees } From 42b07312404330c88e3c9667df50a5be87c1a644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 9 Oct 2024 15:02:05 +0200 Subject: [PATCH 091/167] Add IFDS integration based on IDE problems/solver --- .../IFDSPropertyMetaInformation.scala | 11 ++ .../ide/ifds/problem/IFDSEdgeFunctions.scala | 15 +++ .../opalj/ide/ifds/problem/IFDSLattice.scala | 18 +++ .../opalj/ide/ifds/problem/IFDSProblem.scala | 110 ++++++++++++++++++ .../opalj/ide/ifds/problem/IFDSValue.scala | 19 +++ .../JavaIFDSAnalysisScheduler.scala | 20 ++++ .../problem/JavaBackwardIFDSProblem.scala | 17 +++ .../ifds/problem/JavaForwardIFDSProblem.scala | 17 +++ .../ide/ifds/problem/JavaIFDSProblem.scala | 15 +++ 9 files changed, 242 insertions(+) create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSEdgeFunctions.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSLattice.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSValue.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaBackwardIFDSProblem.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaForwardIFDSProblem.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala new file mode 100644 index 0000000000..04fb3a1b71 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala @@ -0,0 +1,11 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.ifds.integration + +import org.opalj.ide.ifds.problem.IFDSValue +import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.ide.problem.IDEFact + +/** + * Interface for property meta information for IFDS problems based on an IDE problem + */ +trait IFDSPropertyMetaInformation[Fact <: IDEFact] extends IDEPropertyMetaInformation[Fact, IFDSValue] diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSEdgeFunctions.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSEdgeFunctions.scala new file mode 100644 index 0000000000..7c9d5ddf72 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSEdgeFunctions.scala @@ -0,0 +1,15 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.ifds.problem + +import org.opalj.ide.problem.EdgeFunction + +/** + * Edge function evaluating all source values to the bottom value + */ +object AllBottomEdgeFunction extends org.opalj.ide.problem.AllBottomEdgeFunction[IFDSValue](Bottom) { + override def composeWith(secondEdgeFunction: EdgeFunction[IFDSValue]): EdgeFunction[IFDSValue] = { + this + } + + override def toString: String = "AllBottomEdgeFunction()" +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSLattice.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSLattice.scala new file mode 100644 index 0000000000..ae8fc95c54 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSLattice.scala @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.ifds.problem + +import org.opalj.ide.problem.MeetLattice + +/** + * Lattice to use for IFDS problems that are solved with an IDE solver + */ +object IFDSLattice extends MeetLattice[IFDSValue] { + override def top: IFDSValue = Top + + override def bottom: IFDSValue = Bottom + + override def meet(x: IFDSValue, y: IFDSValue): IFDSValue = (x, y) match { + case (Top, Top) => Top + case _ => Bottom + } +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala new file mode 100644 index 0000000000..a4c6b0f9d6 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala @@ -0,0 +1,110 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.ifds.problem + +import org.opalj.fpcf.Entity +import org.opalj.fpcf.PropertyStore +import org.opalj.ide.problem.EdgeFunction +import org.opalj.ide.problem.EdgeFunctionResult +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEProblem +import org.opalj.ide.problem.MeetLattice +import org.opalj.ide.solver.ICFG + +/** + * Interface for modeling IFDS problems based on an IDE problem + */ +abstract class IFDSProblem[Fact <: IDEFact, Statement, Callable <: Entity]( + icfg: ICFG[Statement, Callable] +) extends IDEProblem[Fact, IFDSValue, Statement, Callable](icfg) { + override final val lattice: MeetLattice[IFDSValue] = IFDSLattice + + override final def getNormalEdgeFunction( + source: Statement, + sourceFact: Fact, + target: Statement, + targetFact: Fact + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[IFDSValue] = { + if (sourceFact == nullFact) { + AllBottomEdgeFunction + } else { + identityEdgeFunction + } + } + + override final def getCallEdgeFunction( + callSite: Statement, + callSiteFact: Fact, + calleeEntry: Statement, + calleeEntryFact: Fact, + callee: Callable + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[IFDSValue] = { + if (callSiteFact == nullFact) { + AllBottomEdgeFunction + } else { + identityEdgeFunction + } + } + + override final def getReturnEdgeFunction( + calleeExit: Statement, + calleeExitFact: Fact, + callee: Callable, + returnSite: Statement, + returnSiteFact: Fact + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[IFDSValue] = { + if (calleeExitFact == nullFact) { + AllBottomEdgeFunction + } else { + identityEdgeFunction + } + } + + override final def getCallToReturnEdgeFunction( + callSite: Statement, + callSiteFact: Fact, + callee: Callable, + returnSite: Statement, + returnSiteFact: Fact + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[IFDSValue] = { + if (callSiteFact == nullFact) { + AllBottomEdgeFunction + } else { + identityEdgeFunction + } + } + + /** + * Whether precomputed flow functions for a `(callSite, callSiteFact, callee)` combination exist (resp. can be + * generated). + * @param callSite where the flow starts + * @param callSiteFact the fact the flow starts with + * @param callee the callable this flow is about + */ + def hasPrecomputedFlowFunction(callSite: Statement, callSiteFact: Fact, callee: Callable)( + implicit propertyStore: PropertyStore + ): Boolean = { + false + } + + override final def hasPrecomputedFlowAndSummaryFunction( + callSite: Statement, + callSiteFact: Fact, + callee: Callable + )(implicit propertyStore: PropertyStore): Boolean = { + hasPrecomputedFlowFunction(callSite, callSiteFact, callee) + } + + override final def getPrecomputedSummaryFunction( + callSite: Statement, + callSiteFact: Fact, + callee: Callable, + returnSite: Statement, + returnSiteFact: Fact + )(implicit propertyStore: PropertyStore): EdgeFunction[IFDSValue] = { + if (callSiteFact == nullFact) { + AllBottomEdgeFunction + } else { + identityEdgeFunction + } + } +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSValue.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSValue.scala new file mode 100644 index 0000000000..faaa131d35 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSValue.scala @@ -0,0 +1,19 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.ifds.problem + +import org.opalj.ide.problem.IDEValue + +/** + * Type for modeling values for IFDS problems that are solved with an IDE solver + */ +trait IFDSValue extends IDEValue + +/** + * Top value + */ +case object Top extends IFDSValue + +/** + * Bottom value (all result fact have the bottom value) + */ +case object Bottom extends IFDSValue diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala new file mode 100644 index 0000000000..cff2f469dd --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala @@ -0,0 +1,20 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.ifds.integration + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.ide.ifds.integration.IFDSPropertyMetaInformation +import org.opalj.ide.ifds.problem.IFDSProblem +import org.opalj.ide.ifds.problem.IFDSValue +import org.opalj.ide.problem.IDEFact +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement + +/** + * Specialized IDE analysis scheduler for IFDS problems with Java programs + */ +abstract class JavaIFDSAnalysisScheduler[Fact <: IDEFact] extends JavaIDEAnalysisScheduler[Fact, IFDSValue] { + override def propertyMetaInformation: IFDSPropertyMetaInformation[Fact] + + override def createProblem(project: SomeProject): IFDSProblem[Fact, JavaStatement, Method] +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaBackwardIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaBackwardIFDSProblem.scala new file mode 100644 index 0000000000..c0f93ba438 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaBackwardIFDSProblem.scala @@ -0,0 +1,17 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.ifds.problem + +import org.opalj.br.analyses.SomeProject +import org.opalj.ide.problem.IDEFact +import org.opalj.tac.fpcf.analyses.ide.solver.JavaBackwardICFG + +/** + * Specialized IFDS problem for Java programs on a backward ICFG based on an IDE problem + */ +abstract class JavaBackwardIFDSProblem[Fact <: IDEFact]( + override val icfg: JavaBackwardICFG +) extends JavaIFDSProblem[Fact](icfg) { + def this(project: SomeProject) = { + this(new JavaBackwardICFG(project)) + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaForwardIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaForwardIFDSProblem.scala new file mode 100644 index 0000000000..36ce24e74c --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaForwardIFDSProblem.scala @@ -0,0 +1,17 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.ifds.problem + +import org.opalj.br.analyses.SomeProject +import org.opalj.ide.problem.IDEFact +import org.opalj.tac.fpcf.analyses.ide.solver.JavaForwardICFG + +/** + * Specialized IFDS problem for Java programs on a forward ICFG based on an IDE problem + */ +abstract class JavaForwardIFDSProblem[Fact <: IDEFact]( + override val icfg: JavaForwardICFG +) extends JavaIFDSProblem[Fact](icfg) { + def this(project: SomeProject) = { + this(new JavaForwardICFG(project)) + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala new file mode 100644 index 0000000000..bd1219aabf --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala @@ -0,0 +1,15 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.ifds.problem + +import org.opalj.br.Method +import org.opalj.ide.ifds.problem.IFDSProblem +import org.opalj.ide.problem.IDEFact +import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement + +/** + * Specialized IFDS problem for Java programs based on an IDE problem + */ +abstract class JavaIFDSProblem[Fact <: IDEFact]( + override val icfg: JavaICFG +) extends IFDSProblem[Fact, JavaStatement, Method](icfg) From 4f8f40c454ab2fe58dd8cbe010c17c4f6983ff7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 11 Oct 2024 14:09:34 +0200 Subject: [PATCH 092/167] Fix LCP on fields tests --- .../lcp_on_fields/ArrayUnknownIndicesExample.java | 11 ++++++++--- .../fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala | 7 ++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayUnknownIndicesExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayUnknownIndicesExample.java index a7b016c603..008c13c84c 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayUnknownIndicesExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayUnknownIndicesExample.java @@ -4,6 +4,7 @@ import org.opalj.fpcf.properties.lcp_on_fields.ArrayValue; import org.opalj.fpcf.properties.lcp_on_fields.ConstantArrayElement; import org.opalj.fpcf.properties.lcp_on_fields.VariableArrayElement; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; public class ArrayUnknownIndicesExample { @@ -19,7 +20,8 @@ public class ArrayUnknownIndicesExample { }, constantElements = { @ConstantArrayElement(index = 50, value = 99) }) - @VariableValue(variable = "lv9") + @ConstantValue(variable = "lv9", value = 0) + @VariableValue(variable = "lvf") public static void main(String[] args) { int[] arr = new int[100]; @@ -33,10 +35,13 @@ public static void main(String[] args) { j = 12; } - int a = arr[i]; + int a1 = arr[i]; + arr[j] = 13; arr[50] = 99; - System.out.println("a" + a); + int a2 = arr[i]; + + System.out.println("a1: " + a1 + ", a2: " + a2); } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala index d9399a4691..519cee66fd 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala @@ -5,6 +5,7 @@ import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.ide.IDEPropertiesTest import org.opalj.fpcf.properties.lcp_on_fields.LCPOnFieldsProperty +import org.opalj.fpcf.properties.linear_constant_propagation.LinearConstantPropagationProperty import org.opalj.ide.integration.LazyIDEAnalysisProxyScheduler class LCPOnFieldsTests extends IDEPropertiesTest { @@ -22,6 +23,10 @@ class LCPOnFieldsTests extends IDEPropertiesTest { val entryPoints = methodsWithAnnotations(analysis.project) entryPoints.foreach { case (method, _, _) => propertyStore.force(method, LCPOnFieldsAnalysisScheduler.propertyMetaInformation.key) + propertyStore.force( + method, + LinearConstantPropagationAnalysisSchedulerExtended.propertyMetaInformation.key + ) } } } @@ -32,7 +37,7 @@ class LCPOnFieldsTests extends IDEPropertiesTest { validateProperties( testContext, methodsWithAnnotations(testContext.project), - Set(LCPOnFieldsProperty.KEY) + Set(LCPOnFieldsProperty.KEY, LinearConstantPropagationProperty.KEY) ) } } From 7cff6f9334d3ad3d128585457d572e5ca6a3cd5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 11 Oct 2024 14:30:56 +0200 Subject: [PATCH 093/167] Fix ClassFile::staticInitializer loop termination --- OPAL/br/src/main/scala/org/opalj/br/ClassFile.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/ClassFile.scala b/OPAL/br/src/main/scala/org/opalj/br/ClassFile.scala index 4c51623448..ea6d14bb6f 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/ClassFile.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/ClassFile.scala @@ -702,7 +702,7 @@ final class ClassFile private ( (majorVersion < 51 || method.isStatic) ) return Some(method); - else if (methodNameComparison < 0) + else if (methodNameComparison > 0) return None; i += 1 From 971703f8b902a350fb62bfb15c5709bef5eadc75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 11 Oct 2024 17:08:32 +0200 Subject: [PATCH 094/167] Extend LCP on fields analysis with immutability information to be able to detect simple static fields --- .../StaticFieldImmutableExample.java | 35 ++++++ .../StaticFieldNonImmutableExample.java | 32 ++++++ .../ide/lcp_on_fields/LCPOnFieldsTests.scala | 12 +- ...PropagationAnalysisSchedulerExtended.scala | 2 + ...arConstantPropagationProblemExtended.scala | 103 ++++++++++++++++++ 5 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldNonImmutableExample.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java new file mode 100644 index 0000000000..846a425fa5 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java @@ -0,0 +1,35 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.lcp_on_fields; + +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValues; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; + +public final class StaticFieldImmutableExample { + protected static int a = 42; + static int b; + private static int c; + + static { + b = 23; + + if (System.out != null) { + c = 11; + } else { + c = 12; + } + } + + @ConstantValues({ + @ConstantValue(variable = "lv0", value = 42), + @ConstantValue(variable = "lv1", value = 23) + }) + @VariableValue(variable = "lv2") + public static void main(String[] args) { + int a = StaticFieldImmutableExample.a; + int b = StaticFieldImmutableExample.b; + int c = StaticFieldImmutableExample.c; + + System.out.println("a: " + a + ", b: " + b + ", c: " + c); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldNonImmutableExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldNonImmutableExample.java new file mode 100644 index 0000000000..1dc448b9a2 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldNonImmutableExample.java @@ -0,0 +1,32 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.lcp_on_fields; + +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; + +public class StaticFieldNonImmutableExample { + private static int a = 11; + static int b = 42; + protected static int c = 23; + + @VariableValues({ + @VariableValue(variable = "lv2"), + @VariableValue(variable = "lv3"), + @VariableValue(variable = "lv4") + }) + public static void main(String[] args) { + a = 12; + + int a = StaticFieldNonImmutableExample.a; + int b = StaticFieldNonImmutableExample.b; + int c = StaticFieldNonImmutableExample.c; + + System.out.println("a: " + a + ", b: " + b + ", c: " + c); + } +} + +class StaticFieldNonImmutable2Example { + void foobar() { + StaticFieldNonImmutableExample.b = 23; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala index 519cee66fd..6101e41890 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala @@ -2,11 +2,16 @@ package org.opalj.fpcf.ide.lcp_on_fields import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.analyses.immutability.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.immutability.LazyTypeImmutabilityAnalysis import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.ide.IDEPropertiesTest import org.opalj.fpcf.properties.lcp_on_fields.LCPOnFieldsProperty import org.opalj.fpcf.properties.linear_constant_propagation.LinearConstantPropagationProperty import org.opalj.ide.integration.LazyIDEAnalysisProxyScheduler +import org.opalj.tac.fpcf.analyses.LazyFieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.fieldaccess.EagerFieldAccessInformationAnalysis +import org.opalj.tac.fpcf.analyses.fieldassignability.LazyL2FieldAssignabilityAnalysis class LCPOnFieldsTests extends IDEPropertiesTest { override def fixtureProjectPackage: List[String] = { @@ -29,7 +34,12 @@ class LCPOnFieldsTests extends IDEPropertiesTest { ) } } - } + }, + LazyFieldImmutabilityAnalysis, + LazyL2FieldAssignabilityAnalysis, + LazyTypeImmutabilityAnalysis, + LazyClassImmutabilityAnalysis, + EagerFieldAccessInformationAnalysis )) testContext.propertyStore.shutdown() diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala index 09497ad1df..45ffc21077 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala @@ -5,6 +5,7 @@ import scala.collection.immutable import org.opalj.br.Method import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.immutability.FieldImmutability import org.opalj.fpcf.PropertyBounds import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.ide.problem.IDEProblem @@ -36,6 +37,7 @@ abstract class LinearConstantPropagationAnalysisSchedulerExtended override def uses: Set[PropertyBounds] = super.uses.union(immutable.Set( + PropertyBounds.ub(FieldImmutability), PropertyBounds.ub(LCPOnFieldsPropertyMetaInformation) )) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala index 077679e2f9..459c5ab99f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala @@ -2,9 +2,14 @@ package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem import scala.collection.immutable +import scala.collection.mutable import org.opalj.ai.domain.l1.DefaultIntegerRangeValues +import org.opalj.br.Field +import org.opalj.br.analyses.DeclaredFieldsKey import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.immutability.FieldImmutability +import org.opalj.br.fpcf.properties.immutability.TransitivelyImmutableField import org.opalj.fpcf.FinalP import org.opalj.fpcf.InterimUBP import org.opalj.fpcf.Property @@ -14,6 +19,8 @@ import org.opalj.ide.problem.FinalEdgeFunction import org.opalj.ide.problem.InterimEdgeFunction import org.opalj.tac.ArrayLoad import org.opalj.tac.GetField +import org.opalj.tac.GetStatic +import org.opalj.tac.PutStatic import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.ConstantValue import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearCombinationEdgeFunction @@ -193,4 +200,100 @@ class LinearConstantPropagationProblemExtended(project: SomeProject) extends Lin lattice.meet(value, values(fieldName)) } } + + override def getNormalEdgeFunctionForGetStatic( + getStaticExpr: GetStatic + )( + source: JavaStatement, + sourceFact: LinearConstantPropagationFact, + target: JavaStatement, + targetFact: LinearConstantPropagationFact + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { + val field = + project.get(DeclaredFieldsKey)( + getStaticExpr.declaringClass, + getStaticExpr.name, + getStaticExpr.declaredFieldType + ).definedField + + /* We enhance the analysis with immutability information. When a static field is immutable and we have knowledge + * of an assignment site, then this will always be the value of the field. This way we can make this analysis + * more precise without the need to add precise handling of static initializers. */ + val fieldImmutabilityEOptionP = propertyStore(field, FieldImmutability.key) + + fieldImmutabilityEOptionP match { + case FinalP(fieldImmutability) => + fieldImmutability match { + case TransitivelyImmutableField => + getValueForGetStaticExpr(getStaticExpr, field) match { + case ConstantValue(c) => FinalEdgeFunction(LinearCombinationEdgeFunction(0, c, lattice.top)) + case _ => FinalEdgeFunction(VariableValueEdgeFunction) + } + case _ => FinalEdgeFunction(VariableValueEdgeFunction) + } + + case InterimUBP(fieldImmutability) => + fieldImmutability match { + case TransitivelyImmutableField => + getValueForGetStaticExpr(getStaticExpr, field) match { + case ConstantValue(c) => + InterimEdgeFunction( + LinearCombinationEdgeFunction(0, c, lattice.top), + immutable.Set(fieldImmutabilityEOptionP) + ) + case _ => FinalEdgeFunction(VariableValueEdgeFunction) + } + case _ => FinalEdgeFunction(VariableValueEdgeFunction) + } + + case _ => + InterimEdgeFunction(UnknownValueEdgeFunction, immutable.Set(fieldImmutabilityEOptionP)) + } + } + + private def getValueForGetStaticExpr(getStaticExpr: GetStatic, field: Field): LinearConstantPropagationValue = { + var value: LinearConstantPropagationValue = UnknownValue + + /* Search for statements that write the field in static initializer of the class belonging to the field. */ + field.classFile.staticInitializer match { + case Some(method) => + var workingStmts: mutable.Set[JavaStatement] = mutable.Set.from(icfg.getStartStatements(method)) + val seenStmts = mutable.Set.empty[JavaStatement] + + while (workingStmts.nonEmpty) { + workingStmts.foreach { stmt => + stmt.stmt.astID match { + case PutStatic.ASTID => + val putStatic = stmt.stmt.asPutStatic + if (getStaticExpr.declaringClass == putStatic.declaringClass && + getStaticExpr.name == putStatic.name + ) { + stmt.stmt.asPutStatic.value.asVar.value match { + case intRange: DefaultIntegerRangeValues#IntegerRange => + if (intRange.lowerBound == intRange.upperBound) { + value = lattice.meet(value, ConstantValue(intRange.upperBound)) + } else { + return VariableValue + } + + case _ => + return VariableValue + } + } + + case _ => + } + } + + seenStmts.addAll(workingStmts) + workingStmts = workingStmts.foldLeft(mutable.Set.empty[JavaStatement]) { (nextStmts, stmt) => + nextStmts.addAll(icfg.getNextStatements(stmt)) + }.diff(seenStmts) + } + + case _ => + } + + value + } } From fcfc831799b0906d2122bf9746ead8862164edf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 28 Oct 2024 10:19:48 +0100 Subject: [PATCH 095/167] Refactor ICFG being rooted at analysis scheduler and not at problem definition --- .../opalj/ide/ifds/problem/IFDSProblem.scala | 6 ++--- .../FlowRecordingAnalysisScheduler.scala | 11 +++++++++ .../integration/IDEAnalysisScheduler.scala | 5 +++- .../ide/problem/FlowRecordingIDEProblem.scala | 6 +++-- .../org/opalj/ide/problem/IDEProblem.scala | 5 +--- .../org/opalj/ide/solver/IDEAnalysis.scala | 3 +-- .../problem/JavaBackwardIFDSProblem.scala | 17 ------------- .../ifds/problem/JavaForwardIFDSProblem.scala | 17 ------------- .../ide/ifds/problem/JavaIFDSProblem.scala | 5 +--- .../LCPOnFieldsAnalysisScheduler.scala | 5 ++-- ...PropagationAnalysisSchedulerExtended.scala | 5 ++-- .../problem/LCPOnFieldsProblem.scala | 6 ++--- ...arConstantPropagationProblemExtended.scala | 6 ++++- ...ConstantPropagationAnalysisScheduler.scala | 5 ++-- .../LinearConstantPropagationProblem.scala | 7 +++--- .../JavaIDEAnalysisScheduler.scala | 24 +++++++++++++++++++ .../ide/problem/JavaBackwardIDEProblem.scala | 18 -------------- .../ide/problem/JavaForwardIDEProblem.scala | 18 -------------- .../analyses/ide/problem/JavaIDEProblem.scala | 5 +--- 19 files changed, 68 insertions(+), 106 deletions(-) delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaBackwardIFDSProblem.scala delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaForwardIFDSProblem.scala delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaBackwardIDEProblem.scala delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaForwardIDEProblem.scala diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala index a4c6b0f9d6..4c7e2ea60d 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala @@ -8,14 +8,12 @@ import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEProblem import org.opalj.ide.problem.MeetLattice -import org.opalj.ide.solver.ICFG /** * Interface for modeling IFDS problems based on an IDE problem */ -abstract class IFDSProblem[Fact <: IDEFact, Statement, Callable <: Entity]( - icfg: ICFG[Statement, Callable] -) extends IDEProblem[Fact, IFDSValue, Statement, Callable](icfg) { +abstract class IFDSProblem[Fact <: IDEFact, Statement, Callable <: Entity] + extends IDEProblem[Fact, IFDSValue, Statement, Callable] { override final val lattice: MeetLattice[IFDSValue] = IFDSLattice override final def getNormalEdgeFunction( diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala index 3c6b0bc041..d937e7d584 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala @@ -24,6 +24,7 @@ import org.opalj.ide.problem.FlowRecordingIDEProblem import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEProblem import org.opalj.ide.problem.IDEValue +import org.opalj.ide.solver.ICFG import org.opalj.ide.solver.IDEAnalysis import org.opalj.ide.util.Logging @@ -50,6 +51,7 @@ class FlowRecordingAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue, Stateme override def createProblem(project: SomeProject): IDEProblem[Fact, Value, Statement, Callable] = { val flowRecordingProblem = new FlowRecordingIDEProblem( ideAnalysisScheduler.createProblem(project), + createICFG(project), recorderMode, uniqueFlowsOnly, recordEdgeFunctions @@ -58,6 +60,15 @@ class FlowRecordingAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue, Stateme flowRecordingProblem } + private var icfg: ICFG[Statement, Callable] = _ + + override def createICFG(project: SomeProject): ICFG[Statement, Callable] = { + if (icfg == null) { + icfg = ideAnalysisScheduler.createICFG(project) + } + icfg + } + override def requiredProjectInformation: ProjectInformationKeys = { ideAnalysisScheduler.requiredProjectInformation } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala index 4031143910..89161948de 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala @@ -14,6 +14,7 @@ import org.opalj.fpcf.PropertyStore import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEProblem import org.opalj.ide.problem.IDEValue +import org.opalj.ide.solver.ICFG import org.opalj.ide.solver.IDEAnalysis /** @@ -27,6 +28,8 @@ abstract class IDEAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue, Statemen def createProblem(project: SomeProject): IDEProblem[Fact, Value, Statement, Callable] + def createICFG(project: SomeProject): ICFG[Statement, Callable] + override final def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(propertyMetaInformation.backingPropertyMetaInformation)) @@ -39,7 +42,7 @@ abstract class IDEAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue, Statemen override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} override final def init(project: SomeProject, ps: PropertyStore): IDEAnalysis[Fact, Value, Statement, Callable] = { - new IDEAnalysis(project, createProblem(project), propertyMetaInformation) + new IDEAnalysis(project, createProblem(project), createICFG(project), propertyMetaInformation) } override final def register( diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index e5bd8fb972..3fc88bbb20 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -6,6 +6,7 @@ import scala.collection.mutable import org.opalj.fpcf.Entity import org.opalj.fpcf.PropertyStore +import org.opalj.ide.solver.ICFG object FlowRecorderModes extends Enumeration { type FlowRecorderMode = Value @@ -31,10 +32,11 @@ object FlowRecorderModes extends Enumeration { */ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( val baseProblem: IDEProblem[Fact, Value, Statement, Callable], + val icfg: ICFG[Statement, Callable], val recorderMode: FlowRecorderModes.FlowRecorderMode = FlowRecorderModes.NODE_AS_STMT, val uniqueFlowsOnly: Boolean = true, val recordEdgeFunctions: Boolean = false -) extends IDEProblem[Fact, Value, Statement, Callable](baseProblem.icfg) { +) extends IDEProblem[Fact, Value, Statement, Callable] { /** * Wrapper class for flow functions doing the actual recording */ @@ -57,7 +59,7 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal private val collectedEdgeFunctions = mutable.Map.empty[DotEdge, EdgeFunction[Value]] - private var writer: Writer = null + private var writer: Writer = _ override val nullFact: Fact = baseProblem.nullFact diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index a5a91ce002..f7f88a8be4 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -8,14 +8,11 @@ import scala.collection.immutable import org.opalj.fpcf.Entity import org.opalj.fpcf.PropertyStore -import org.opalj.ide.solver.ICFG /** * Interface for modeling IDE problems */ -abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( - val icfg: ICFG[Statement, Callable] -) { +abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity] { implicit def edgeFunctionToFinalEdgeFunction(edgeFunction: EdgeFunction[Value]): EdgeFunctionResult[Value] = { FinalEdgeFunction(edgeFunction) } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 9527331b70..eda579d3ac 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -38,6 +38,7 @@ import org.opalj.ide.util.Logging class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( val project: SomeProject, val problem: IDEProblem[Fact, Value, Statement, Callable], + val icfg: ICFG[Statement, Callable], val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] ) extends FPCFAnalysis with Logging.ByProjectConfig { private type Node = (Statement, Fact) @@ -285,8 +286,6 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } } - private val icfg: ICFG[Statement, Callable] = problem.icfg - private def nodeToString(node: Node, indent: String = ""): String = { s"Node(\n$indent\t${icfg.stringifyStatement(node._1, s"$indent\t")},\n$indent\t${node._2}\n$indent)" } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaBackwardIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaBackwardIFDSProblem.scala deleted file mode 100644 index c0f93ba438..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaBackwardIFDSProblem.scala +++ /dev/null @@ -1,17 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.ifds.problem - -import org.opalj.br.analyses.SomeProject -import org.opalj.ide.problem.IDEFact -import org.opalj.tac.fpcf.analyses.ide.solver.JavaBackwardICFG - -/** - * Specialized IFDS problem for Java programs on a backward ICFG based on an IDE problem - */ -abstract class JavaBackwardIFDSProblem[Fact <: IDEFact]( - override val icfg: JavaBackwardICFG -) extends JavaIFDSProblem[Fact](icfg) { - def this(project: SomeProject) = { - this(new JavaBackwardICFG(project)) - } -} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaForwardIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaForwardIFDSProblem.scala deleted file mode 100644 index 36ce24e74c..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaForwardIFDSProblem.scala +++ /dev/null @@ -1,17 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.ifds.problem - -import org.opalj.br.analyses.SomeProject -import org.opalj.ide.problem.IDEFact -import org.opalj.tac.fpcf.analyses.ide.solver.JavaForwardICFG - -/** - * Specialized IFDS problem for Java programs on a forward ICFG based on an IDE problem - */ -abstract class JavaForwardIFDSProblem[Fact <: IDEFact]( - override val icfg: JavaForwardICFG -) extends JavaIFDSProblem[Fact](icfg) { - def this(project: SomeProject) = { - this(new JavaForwardICFG(project)) - } -} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala index bd1219aabf..59b6bdc655 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala @@ -4,12 +4,9 @@ package org.opalj.tac.fpcf.analyses.ide.ifds.problem import org.opalj.br.Method import org.opalj.ide.ifds.problem.IFDSProblem import org.opalj.ide.problem.IDEFact -import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement /** * Specialized IFDS problem for Java programs based on an IDE problem */ -abstract class JavaIFDSProblem[Fact <: IDEFact]( - override val icfg: JavaICFG -) extends IFDSProblem[Fact, JavaStatement, Method](icfg) +abstract class JavaIFDSProblem[Fact <: IDEFact] extends IFDSProblem[Fact, JavaStatement, Method] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala index c722993452..06b03ba8d5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -19,7 +19,8 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement * Linear constant propagation on fields as IDE analysis. This implementation is mainly intended to be an example of a * cyclic IDE analysis (see [[LCPOnFieldsProblem]]). */ -abstract class LCPOnFieldsAnalysisScheduler extends JavaIDEAnalysisScheduler[LCPOnFieldsFact, LCPOnFieldsValue] { +abstract class LCPOnFieldsAnalysisScheduler extends JavaIDEAnalysisScheduler[LCPOnFieldsFact, LCPOnFieldsValue] + with JavaIDEAnalysisScheduler.ForwardICFG { override def propertyMetaInformation: IDEPropertyMetaInformation[LCPOnFieldsFact, LCPOnFieldsValue] = LCPOnFieldsPropertyMetaInformation @@ -29,7 +30,7 @@ abstract class LCPOnFieldsAnalysisScheduler extends JavaIDEAnalysisScheduler[LCP JavaStatement, Method ] = { - new LCPOnFieldsProblem(project) + new LCPOnFieldsProblem } override def uses: Set[PropertyBounds] = diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala index 45ffc21077..948d457586 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala @@ -20,7 +20,8 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement * Extended linear constant propagation as IDE analysis */ abstract class LinearConstantPropagationAnalysisSchedulerExtended - extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] { + extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] + with JavaIDEAnalysisScheduler.ForwardICFG { override def propertyMetaInformation: IDEPropertyMetaInformation[ LinearConstantPropagationFact, LinearConstantPropagationValue @@ -32,7 +33,7 @@ abstract class LinearConstantPropagationAnalysisSchedulerExtended JavaStatement, Method ] = { - new LinearConstantPropagationProblemExtended(project) + new LinearConstantPropagationProblemExtended(project, createICFG(project)) } override def uses: Set[PropertyBounds] = diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 302e215b00..12149c8e2d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -4,7 +4,6 @@ package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem import scala.collection.immutable import org.opalj.br.Method -import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.FinalP import org.opalj.fpcf.InterimUBP import org.opalj.fpcf.Property @@ -24,7 +23,7 @@ import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationLattice import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue -import org.opalj.tac.fpcf.analyses.ide.problem.JavaForwardIDEProblem +import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall @@ -35,8 +34,7 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall * resolved if the index is a constant literal. * This implementation is mainly intended to be an example of a cyclic IDE analysis. */ -class LCPOnFieldsProblem(project: SomeProject) - extends JavaForwardIDEProblem[LCPOnFieldsFact, LCPOnFieldsValue](project) { +class LCPOnFieldsProblem extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValue] { override val nullFact: LCPOnFieldsFact = NullFact diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala index 459c5ab99f..935840254b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala @@ -33,6 +33,7 @@ import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.pro import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.VariableFact import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.VariableValue import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.VariableValueEdgeFunction +import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.V @@ -40,7 +41,10 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.V * Extended definition of the linear constant propagation problem, trying to resolve field accesses with the LCP on * fields analysis. */ -class LinearConstantPropagationProblemExtended(project: SomeProject) extends LinearConstantPropagationProblem(project) { +class LinearConstantPropagationProblemExtended( + project: SomeProject, + icfg: JavaICFG +) extends LinearConstantPropagationProblem { override def isArrayLoadExpressionGeneratedByFact( arrayLoadExpr: ArrayLoad[V] )( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala index b627ff0113..ffb320f47b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala @@ -15,7 +15,8 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement * Linear constant propagation as IDE analysis */ abstract class LinearConstantPropagationAnalysisScheduler - extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] { + extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] + with JavaIDEAnalysisScheduler.ForwardICFG { override def propertyMetaInformation: IDEPropertyMetaInformation[ LinearConstantPropagationFact, LinearConstantPropagationValue @@ -27,6 +28,6 @@ abstract class LinearConstantPropagationAnalysisScheduler JavaStatement, Method ] = { - new LinearConstantPropagationProblem(project) + new LinearConstantPropagationProblem } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index a40ddc23ba..c23620ad26 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -8,7 +8,6 @@ import scala.collection.immutable import org.opalj.BinaryArithmeticOperators import org.opalj.ai.domain.l1.DefaultIntegerRangeValues import org.opalj.br.Method -import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.PropertyStore import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.EdgeFunctionResult @@ -23,15 +22,15 @@ import org.opalj.tac.GetField import org.opalj.tac.GetStatic import org.opalj.tac.IntConst import org.opalj.tac.Var -import org.opalj.tac.fpcf.analyses.ide.problem.JavaForwardIDEProblem +import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall /** * Definition of the linear constant propagation problem */ -class LinearConstantPropagationProblem(project: SomeProject) - extends JavaForwardIDEProblem[LinearConstantPropagationFact, LinearConstantPropagationValue](project) { +class LinearConstantPropagationProblem + extends JavaIDEProblem[LinearConstantPropagationFact, LinearConstantPropagationValue] { override val nullFact: LinearConstantPropagationFact = NullFact diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala index e702671570..f576663c25 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala @@ -6,6 +6,7 @@ import scala.collection.immutable import org.opalj.br.Method import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.cg.Callers import org.opalj.fpcf.PropertyBounds import org.opalj.ide.integration.IDEAnalysisScheduler @@ -14,6 +15,9 @@ import org.opalj.ide.problem.IDEValue import org.opalj.tac.cg.CallGraphKey import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.cg.TypeIteratorKey +import org.opalj.tac.fpcf.analyses.ide.solver.JavaBackwardICFG +import org.opalj.tac.fpcf.analyses.ide.solver.JavaForwardICFG +import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement import org.opalj.tac.fpcf.properties.TACAI @@ -27,6 +31,8 @@ abstract class JavaIDEAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue] */ val callGraphKey: CallGraphKey + override def createICFG(project: SomeProject): JavaICFG + override def requiredProjectInformation: ProjectInformationKeys = super.requiredProjectInformation ++ Seq( DeclaredMethodsKey, @@ -48,4 +54,22 @@ object JavaIDEAnalysisScheduler { trait RTACallGraph { val callGraphKey: CallGraphKey = RTACallGraphKey } + + /** + * Trait to drop-in [[JavaForwardICFG]] for [[createICFG]] + */ + trait ForwardICFG { + def createICFG(project: SomeProject): JavaICFG = { + new JavaForwardICFG(project) + } + } + + /** + * Trait to drop-in [[JavaBackwardICFG]] for [[createICFG]] + */ + trait BackwardICFG { + def createICFG(project: SomeProject): JavaICFG = { + new JavaBackwardICFG(project) + } + } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaBackwardIDEProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaBackwardIDEProblem.scala deleted file mode 100644 index a5b59d5942..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaBackwardIDEProblem.scala +++ /dev/null @@ -1,18 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.problem - -import org.opalj.br.analyses.SomeProject -import org.opalj.ide.problem.IDEFact -import org.opalj.ide.problem.IDEValue -import org.opalj.tac.fpcf.analyses.ide.solver.JavaBackwardICFG - -/** - * Specialized IDE problem for Java programs on a backward ICFG - */ -abstract class JavaBackwardIDEProblem[Fact <: IDEFact, Value <: IDEValue]( - override val icfg: JavaBackwardICFG -) extends JavaIDEProblem[Fact, Value](icfg) { - def this(project: SomeProject) = { - this(new JavaBackwardICFG(project)) - } -} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaForwardIDEProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaForwardIDEProblem.scala deleted file mode 100644 index 8dead22176..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaForwardIDEProblem.scala +++ /dev/null @@ -1,18 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.problem - -import org.opalj.br.analyses.SomeProject -import org.opalj.ide.problem.IDEFact -import org.opalj.ide.problem.IDEValue -import org.opalj.tac.fpcf.analyses.ide.solver.JavaForwardICFG - -/** - * Specialized IDE problem for Java programs on a forward ICFG - */ -abstract class JavaForwardIDEProblem[Fact <: IDEFact, Value <: IDEValue]( - override val icfg: JavaForwardICFG -) extends JavaIDEProblem[Fact, Value](icfg) { - def this(project: SomeProject) = { - this(new JavaForwardICFG(project)) - } -} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala index 6e0fbd6442..16423d1641 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala @@ -5,12 +5,9 @@ import org.opalj.br.Method import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEProblem import org.opalj.ide.problem.IDEValue -import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement /** * Specialized IDE problem for Java programs */ -abstract class JavaIDEProblem[Fact <: IDEFact, Value <: IDEValue]( - override val icfg: JavaICFG -) extends IDEProblem[Fact, Value, JavaStatement, Method](icfg) +abstract class JavaIDEProblem[Fact <: IDEFact, Value <: IDEValue] extends IDEProblem[Fact, Value, JavaStatement, Method] From dddf278f6d1a540e7c6b09008173c2c4f30d58f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 28 Oct 2024 11:28:11 +0100 Subject: [PATCH 096/167] Refactor FlowFunction sourceFact parameter --- .../org/opalj/ide/problem/FlowFunction.scala | 13 ++-- .../ide/problem/FlowRecordingIDEProblem.scala | 65 +++++++++++------- .../org/opalj/ide/problem/IDEProblem.scala | 19 +++--- .../org/opalj/ide/solver/IDEAnalysis.scala | 14 ++-- .../problem/LCPOnFieldsProblem.scala | 66 +++++++++++-------- .../LinearConstantPropagationProblem.scala | 54 ++++++++------- 6 files changed, 134 insertions(+), 97 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala index 442ae2a7bb..506f37ba53 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala @@ -18,13 +18,12 @@ trait FlowFunction[Fact <: IDEFact] { } /** - * Compute the facts that are generated by this flow function when seeing a fact and the dependees that can cause - * new facts to be generated - * @param sourceFact the incoming fact + * Compute the facts that are generated by this flow function and the dependees that can cause new facts to be + * generated * @return a set of facts and a set of dependees (a fact that is returned once must also be returned with every * subsequent call) */ - def compute(sourceFact: Fact): FactsAndDependees + def compute(): FactsAndDependees } object FlowFunction { @@ -34,8 +33,8 @@ object FlowFunction { /** * Special flow function that always returns the input fact */ -case class IdentityFlowFunction[Fact <: IDEFact]() extends FlowFunction[Fact] { - override def compute(sourceFact: Fact): FactsAndDependees = +case class IdentityFlowFunction[Fact <: IDEFact](sourceFact: Fact) extends FlowFunction[Fact] { + override def compute(): FactsAndDependees = immutable.Set(sourceFact) } @@ -43,6 +42,6 @@ case class IdentityFlowFunction[Fact <: IDEFact]() extends FlowFunction[Fact] { * Special flow function that always returns an empty set */ case class EmptyFlowFunction[Fact <: IDEFact]() extends FlowFunction[Fact] { - override def compute(sourceFact: Fact): FactsAndDependees = + override def compute(): FactsAndDependees = immutable.Set.empty[Fact] } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index 3fc88bbb20..7173b2ed6c 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -43,11 +43,12 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal private class RecordingFlowFunction( baseFlowFunction: FlowFunction[Fact], val source: Statement, + val sourceFact: Fact, val target: Statement, val flowType: String ) extends FlowFunction[Fact] { - override def compute(sourceFact: Fact): FactsAndDependees = { - val (facts, dependees) = baseFlowFunction.compute(sourceFact) + override def compute(): FactsAndDependees = { + val (facts, dependees) = baseFlowFunction.compute() facts.foreach { fact => collectedFlows.addOne(createDotEdge(source, sourceFact, target, fact, flowType)) } (facts, dependees) } @@ -71,44 +72,60 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal baseProblem.getAdditionalSeeds(stmt, callee) } - override def getNormalFlowFunction(source: Statement, target: Statement)( - implicit propertyStore: PropertyStore - ): FlowFunction[Fact] = { - new RecordingFlowFunction(baseProblem.getNormalFlowFunction(source, target), source, target, "normal flow") + override def getNormalFlowFunction( + source: Statement, + sourceFact: Fact, + target: Statement + )(implicit propertyStore: PropertyStore): FlowFunction[Fact] = { + new RecordingFlowFunction( + baseProblem.getNormalFlowFunction(source, sourceFact, target), + source, + sourceFact, + target, + "normal flow" + ) } override def getCallFlowFunction( - callSite: Statement, - calleeEntry: Statement, - callee: Callable + callSite: Statement, + callSiteFact: Fact, + calleeEntry: Statement, + callee: Callable )(implicit propertyStore: PropertyStore): FlowFunction[Fact] = { new RecordingFlowFunction( - baseProblem.getCallFlowFunction(callSite, calleeEntry, callee), + baseProblem.getCallFlowFunction(callSite, callSiteFact, calleeEntry, callee), callSite, + callSiteFact, calleeEntry, "call flow" ) } override def getReturnFlowFunction( - calleeExit: Statement, - callee: Callable, - returnSite: Statement + calleeExit: Statement, + calleeExitFact: Fact, + callee: Callable, + returnSite: Statement )(implicit propertyStore: PropertyStore): FlowFunction[Fact] = { new RecordingFlowFunction( - baseProblem.getReturnFlowFunction(calleeExit, callee, returnSite), + baseProblem.getReturnFlowFunction(calleeExit, calleeExitFact, callee, returnSite), calleeExit, + calleeExitFact, returnSite, "return flow" ) } - override def getCallToReturnFlowFunction(callSite: Statement, callee: Callable, returnSite: Statement)( - implicit propertyStore: PropertyStore - ): FlowFunction[Fact] = { + override def getCallToReturnFlowFunction( + callSite: Statement, + callSiteFact: Fact, + callee: Callable, + returnSite: Statement + )(implicit propertyStore: PropertyStore): FlowFunction[Fact] = { new RecordingFlowFunction( - baseProblem.getCallToReturnFlowFunction(callSite, callee, returnSite), + baseProblem.getCallToReturnFlowFunction(callSite, callSiteFact, callee, returnSite), callSite, + callSiteFact, returnSite, "call-to-return flow" ) @@ -184,12 +201,16 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal baseProblem.hasPrecomputedFlowAndSummaryFunction(callSite, callSiteFact, callee) } - override def getPrecomputedFlowFunction(callSite: Statement, callee: Callable, returnSite: Statement)( - implicit propertyStore: PropertyStore - ): FlowFunction[Fact] = { + override def getPrecomputedFlowFunction( + callSite: Statement, + callSiteFact: Fact, + callee: Callable, + returnSite: Statement + )(implicit propertyStore: PropertyStore): FlowFunction[Fact] = { new RecordingFlowFunction( - baseProblem.getPrecomputedFlowFunction(callSite, callee, returnSite), + baseProblem.getPrecomputedFlowFunction(callSite, callSiteFact, callee, returnSite), callSite, + callSiteFact, returnSite, "precomputed flow" ) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index f7f88a8be4..31540d9928 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -17,10 +17,6 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl FinalEdgeFunction(edgeFunction) } - /** - * Identity flow function that can be used when implementing problems - */ - protected val identityFlowFunction = new IdentityFlowFunction[Fact] /** * Empty flow function that can be used when implementing problems */ @@ -55,39 +51,43 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl /** * Generate a flow function for a normal flow * @param source where the normal flow starts + * @param sourceFact the fact the flow starts with * @param target where the normal flow ends */ - def getNormalFlowFunction(source: Statement, target: Statement)( + def getNormalFlowFunction(source: Statement, sourceFact: Fact, target: Statement)( implicit propertyStore: PropertyStore ): FlowFunction[Fact] /** * Generate a flow function for a call flow * @param callSite where the call flow starts (always a call statement) + * @param callSiteFact the fact the flow starts with * @param calleeEntry where the callable starts (the statement which the callable is started with) * @param callee the callable that is called */ - def getCallFlowFunction(callSite: Statement, calleeEntry: Statement, callee: Callable)( + def getCallFlowFunction(callSite: Statement, callSiteFact: Fact, calleeEntry: Statement, callee: Callable)( implicit propertyStore: PropertyStore ): FlowFunction[Fact] /** * Generate a flow function for a return flow * @param calleeExit where the return flow starts (the statement the callable is exited with) + * @param calleeExitFact the fact the flow starts with * @param callee the callable that is returned from * @param returnSite where the return flow ends (e.g. the next statement after the call in the callers code) */ - def getReturnFlowFunction(calleeExit: Statement, callee: Callable, returnSite: Statement)( + def getReturnFlowFunction(calleeExit: Statement, calleeExitFact: Fact, callee: Callable, returnSite: Statement)( implicit propertyStore: PropertyStore ): FlowFunction[Fact] /** * Generate a flow function for a call-to-return flow * @param callSite where the call-to-return flow starts (always a call statement) + * @param callSiteFact the fact the flow starts with * @param callee the callable this flow is about * @param returnSite where the call-to-return flow ends (e.g. the next statement after the call) */ - def getCallToReturnFlowFunction(callSite: Statement, callee: Callable, returnSite: Statement)( + def getCallToReturnFlowFunction(callSite: Statement, callSiteFact: Fact, callee: Callable, returnSite: Statement)( implicit propertyStore: PropertyStore ): FlowFunction[Fact] @@ -171,10 +171,11 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl * return site. Similar to a call-to-return flow (cfg. [[getCallToReturnFlowFunction]]) but capturing the effects * that flow through the callable. * @param callSite where the flow starts (always a call statement) + * @param callSiteFact the fact the flow starts with * @param callee the callable this flow is about * @param returnSite where the flow ends (e.g. the next statement after the call) */ - def getPrecomputedFlowFunction(callSite: Statement, callee: Callable, returnSite: Statement)( + def getPrecomputedFlowFunction(callSite: Statement, callSiteFact: Fact, callee: Callable, returnSite: Statement)( implicit propertyStore: PropertyStore ): FlowFunction[Fact] = { throw new IllegalArgumentException( diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index eda579d3ac..369b33352e 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -497,7 +497,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent /* Handling for precomputed summaries */ rs.foreach { r => - val d5s = handleFlowFunctionResult(problem.getPrecomputedFlowFunction(n, q, r).compute(d2), path) + val d5s = handleFlowFunctionResult(problem.getPrecomputedFlowFunction(n, d2, q, r).compute(), path) logTrace(s"generated the following d5s=$d5s for return statement r=${icfg.stringifyStatement(r)}") @@ -521,7 +521,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val sqs = icfg.getStartStatements(q) sqs.foreach { sq => // IDE P1 lines 12 - 13 - val d3s = handleFlowFunctionResult(problem.getCallFlowFunction(n, sq, q).compute(d2), path) + val d3s = handleFlowFunctionResult(problem.getCallFlowFunction(n, d2, sq, q).compute(), path) logTrace(s"generated the following d3s=$d3s for start statement sq=${icfg.stringifyStatement(sq)}") @@ -535,7 +535,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val f4 = handleEdgeFunctionResult(problem.getCallEdgeFunction(n, d2, sq, d3, q), path) rs.foreach { r => val d5s = handleFlowFunctionResult( - problem.getReturnFlowFunction(eq, q, r).compute(d4), + problem.getReturnFlowFunction(eq, d4, q, r).compute(), path ) d5s.foreach { d5 => @@ -565,7 +565,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } rs.foreach { r => - val d3s = handleFlowFunctionResult(problem.getCallToReturnFlowFunction(n, q, r).compute(d2), path) + val d3s = handleFlowFunctionResult(problem.getCallToReturnFlowFunction(n, d2, q, r).compute(), path) logTrace(s"generated the following d3s=$d3s for return-site statement r=${icfg.stringifyStatement(r)}") @@ -609,7 +609,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logDebug(s"handling calling statement c=${icfg.stringifyStatement(c)}, d4=$d4 and return-site statement r=${icfg.stringifyStatement(r)}") // IDE P1 line 21 - val d5s = handleFlowFunctionResult(problem.getReturnFlowFunction(n, p, r).compute(d2), path) + val d5s = handleFlowFunctionResult(problem.getReturnFlowFunction(n, d2, p, r).compute(), path) logDebug(s"generated the following d5s=$d5s") @@ -649,7 +649,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // IDE P1 lines 31 - 32 icfg.getNextStatements(n).foreach { m => - val d3s = handleFlowFunctionResult(problem.getNormalFlowFunction(n, m).compute(d2), path) + val d3s = handleFlowFunctionResult(problem.getNormalFlowFunction(n, d2, m).compute(), path) logTrace(s"generated the following d3s=$d3s for next statement m=${icfg.stringifyStatement(m)}") @@ -844,7 +844,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent if (!problem.hasPrecomputedFlowAndSummaryFunction(n, d, q)) { val sqs = icfg.getStartStatements(q) sqs.foreach { sq => - val dPrimes = extractFlowFunctionFromResult(problem.getCallFlowFunction(n, sq, q).compute(d)) + val dPrimes = extractFlowFunctionFromResult(problem.getCallFlowFunction(n, d, sq, q).compute()) dPrimes.foreach { dPrime => propagateValue( (sq, dPrime), diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 12149c8e2d..34c8b5136f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -41,11 +41,13 @@ class LCPOnFieldsProblem extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValu override val lattice: MeetLattice[LCPOnFieldsValue] = LCPOnFieldsLattice - override def getNormalFlowFunction(source: JavaStatement, target: JavaStatement)( - implicit propertyStore: PropertyStore - ): FlowFunction[LCPOnFieldsFact] = { + override def getNormalFlowFunction( + source: JavaStatement, + sourceFact: LCPOnFieldsFact, + target: JavaStatement + )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { new FlowFunction[LCPOnFieldsFact] { - override def compute(sourceFact: LCPOnFieldsFact): FactsAndDependees = { + override def compute(): FactsAndDependees = { (source.stmt.astID, sourceFact) match { case (Assignment.ASTID, NullFact) => val assignment = source.stmt.asAssignment @@ -101,21 +103,22 @@ class LCPOnFieldsProblem extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValu } override def getCallFlowFunction( - callSite: JavaStatement, - calleeEntry: JavaStatement, - callee: Method + callSite: JavaStatement, + callSiteFact: LCPOnFieldsFact, + calleeEntry: JavaStatement, + callee: Method )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { new FlowFunction[LCPOnFieldsFact] { - override def compute(sourceFact: LCPOnFieldsFact): FactsAndDependees = { - sourceFact match { + override def compute(): FactsAndDependees = { + callSiteFact match { case NullFact => /* Only propagate null fact if function returns an object or an array of integers */ if (callee.returnType.isObjectType) { - immutable.Set(sourceFact) + immutable.Set(callSiteFact) } else if (callee.returnType.isArrayType && callee.returnType.asArrayType.componentType.isIntegerType ) { - immutable.Set(sourceFact) + immutable.Set(callSiteFact) } else { immutable.Set.empty } @@ -146,16 +149,17 @@ class LCPOnFieldsProblem extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValu } override def getReturnFlowFunction( - calleeExit: JavaStatement, - callee: Method, - returnSite: JavaStatement + calleeExit: JavaStatement, + calleeExitFact: LCPOnFieldsFact, + callee: Method, + returnSite: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { new FlowFunction[LCPOnFieldsFact] { - override def compute(sourceFact: LCPOnFieldsFact): FactsAndDependees = { - sourceFact match { + override def compute(): FactsAndDependees = { + calleeExitFact match { case NullFact => /* Always propagate null fact */ - immutable.Set(sourceFact) + immutable.Set(calleeExitFact) case f: AbstractEntityFact => val definedAtIndex = f.definedAtIndex @@ -203,18 +207,19 @@ class LCPOnFieldsProblem extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValu } override def getCallToReturnFlowFunction( - callSite: JavaStatement, - callee: Method, - returnSite: JavaStatement + callSite: JavaStatement, + callSiteFact: LCPOnFieldsFact, + callee: Method, + returnSite: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { new FlowFunction[LCPOnFieldsFact] { - override def compute(sourceFact: LCPOnFieldsFact): FactsAndDependees = { + override def compute(): FactsAndDependees = { val callStmt = returnSite.stmt.asCall() - sourceFact match { + callSiteFact match { case NullFact => /* Always propagate null fact */ - immutable.Set(sourceFact) + immutable.Set(callSiteFact) case f: AbstractEntityFact => /* Only propagate if the variable represented by the source fact is no initializer of one of the @@ -373,15 +378,18 @@ class LCPOnFieldsProblem extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValu super.hasPrecomputedFlowAndSummaryFunction(callSite, callSiteFact, callee) } - override def getPrecomputedFlowFunction(callSite: JavaStatement, callee: Method, returnSite: JavaStatement)(implicit - propertyStore: PropertyStore - ): FlowFunction[LCPOnFieldsFact] = { + override def getPrecomputedFlowFunction( + callSite: JavaStatement, + callSiteFact: LCPOnFieldsFact, + callee: Method, + returnSite: JavaStatement + )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { if (callee.isNative) { return new FlowFunction[LCPOnFieldsFact] { - override def compute(sourceFact: LCPOnFieldsFact): FactsAndDependees = { + override def compute(): FactsAndDependees = { val callStmt = callSite.stmt.asCall() - sourceFact match { + callSiteFact match { case NullFact => returnSite.stmt.astID match { case Assignment.ASTID => @@ -403,7 +411,7 @@ class LCPOnFieldsProblem extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValu } } - super.getPrecomputedFlowFunction(callSite, callee, returnSite) + super.getPrecomputedFlowFunction(callSite, callSiteFact, callee, returnSite) } override def getPrecomputedSummaryFunction( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index c23620ad26..bb8c945adf 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -12,6 +12,7 @@ import org.opalj.fpcf.PropertyStore import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.FlowFunction +import org.opalj.ide.problem.IdentityFlowFunction import org.opalj.ide.problem.MeetLattice import org.opalj.tac.ArrayLength import org.opalj.tac.ArrayLoad @@ -48,11 +49,12 @@ class LinearConstantPropagationProblem } override def getNormalFlowFunction( - source: JavaStatement, - target: JavaStatement + source: JavaStatement, + sourceFact: LinearConstantPropagationFact, + target: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { new FlowFunction[LinearConstantPropagationFact] { - override def compute(sourceFact: LinearConstantPropagationFact): FactsAndDependees = { + override def compute(): FactsAndDependees = { source.stmt.astID match { case Assignment.ASTID => val assignment = source.stmt.asAssignment @@ -213,20 +215,21 @@ class LinearConstantPropagationProblem } override def getCallFlowFunction( - callSite: JavaStatement, - calleeEntry: JavaStatement, - callee: Method + callSite: JavaStatement, + callSiteFact: LinearConstantPropagationFact, + calleeEntry: JavaStatement, + callee: Method )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { new FlowFunction[LinearConstantPropagationFact] { - override def compute(sourceFact: LinearConstantPropagationFact): FactsAndDependees = { + override def compute(): FactsAndDependees = { /* Only propagate to callees that return integers */ if (!callee.returnType.isIntegerType) { immutable.Set.empty } else { - sourceFact match { + callSiteFact match { case NullFact => /* Always propagate null facts */ - immutable.Set(sourceFact) + immutable.Set(callSiteFact) case VariableFact(_, definedAtIndex) => val callStmt = callSite.stmt.asCall() @@ -253,20 +256,21 @@ class LinearConstantPropagationProblem } override def getReturnFlowFunction( - calleeExit: JavaStatement, - callee: Method, - returnSite: JavaStatement + calleeExit: JavaStatement, + calleeExitFact: LinearConstantPropagationFact, + callee: Method, + returnSite: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { new FlowFunction[LinearConstantPropagationFact] { - override def compute(sourceFact: LinearConstantPropagationFact): FactsAndDependees = { + override def compute(): FactsAndDependees = { /* Only propagate to return site if callee returns an integer */ if (!callee.returnType.isIntegerType) { immutable.Set.empty } else { - sourceFact match { + calleeExitFact match { case NullFact => /* Always propagate null fact */ - immutable.Set(sourceFact) + immutable.Set(calleeExitFact) case VariableFact(_, definedAtIndex) => returnSite.stmt.astID match { @@ -291,11 +295,12 @@ class LinearConstantPropagationProblem } override def getCallToReturnFlowFunction( - callSite: JavaStatement, - callee: Method, - returnSite: JavaStatement + callSite: JavaStatement, + callSiteFact: LinearConstantPropagationFact, + callee: Method, + returnSite: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { - identityFlowFunction + IdentityFlowFunction[LinearConstantPropagationFact](callSiteFact) } override def getNormalEdgeFunction( @@ -519,14 +524,17 @@ class LinearConstantPropagationProblem super.hasPrecomputedFlowAndSummaryFunction(callSite, callSiteFact, callee) } - override def getPrecomputedFlowFunction(callSite: JavaStatement, callee: Method, returnSite: JavaStatement)( - implicit propertyStore: PropertyStore - ): FlowFunction[LinearConstantPropagationFact] = { + override def getPrecomputedFlowFunction( + callSite: JavaStatement, + callSiteFact: LinearConstantPropagationFact, + callee: Method, + returnSite: JavaStatement + )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { if (callee.isNative) { return emptyFlowFunction } - super.getPrecomputedFlowFunction(callSite, callee, returnSite) + super.getPrecomputedFlowFunction(callSite, callSiteFact, callee, returnSite) } override def getPrecomputedSummaryFunction( From de9d160968807f320e82c9c51f7fbe261a701e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 28 Oct 2024 14:47:25 +0100 Subject: [PATCH 097/167] Refactor ICFG handling (and passing) --- .../LCPOnFieldsAnalysisScheduler.scala | 4 +- ...PropagationAnalysisSchedulerExtended.scala | 4 +- ...ConstantPropagationAnalysisScheduler.scala | 4 +- .../EagerIDEAnalysisProxyScheduler.scala | 4 +- .../FlowRecordingAnalysisScheduler.scala | 28 +++---- .../integration/IDEAnalysisScheduler.scala | 17 +++-- .../LazyIDEAnalysisProxyScheduler.scala | 2 +- .../JavaIFDSAnalysisScheduler.scala | 11 ++- .../LCPOnFieldsAnalysisScheduler.scala | 14 ++-- ...PropagationAnalysisSchedulerExtended.scala | 14 ++-- ...ConstantPropagationAnalysisScheduler.scala | 14 ++-- .../JavaIDEAnalysisScheduler.scala | 65 +---------------- .../JavaIDEAnalysisSchedulerBase.scala | 73 +++++++++++++++++++ 13 files changed, 135 insertions(+), 119 deletions(-) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala index 990d156f0e..b43c3fa43c 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -2,7 +2,7 @@ package org.opalj.fpcf.ide.lcp_on_fields import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsAnalysisScheduler -import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase object LCPOnFieldsAnalysisScheduler - extends LCPOnFieldsAnalysisScheduler with JavaIDEAnalysisScheduler.RTACallGraph + extends LCPOnFieldsAnalysisScheduler with JavaIDEAnalysisSchedulerBase.RTACallGraph diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala index eb51a67b48..1b7e305972 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala @@ -2,7 +2,7 @@ package org.opalj.fpcf.ide.lcp_on_fields import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LinearConstantPropagationAnalysisSchedulerExtended -import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase object LinearConstantPropagationAnalysisSchedulerExtended - extends LinearConstantPropagationAnalysisSchedulerExtended with JavaIDEAnalysisScheduler.RTACallGraph + extends LinearConstantPropagationAnalysisSchedulerExtended with JavaIDEAnalysisSchedulerBase.RTACallGraph diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala index 67293af126..ed2e5be782 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala @@ -2,7 +2,7 @@ package org.opalj.fpcf.ide.linear_constant_propagation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationAnalysisScheduler -import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase object LinearConstantPropagationAnalysisScheduler - extends LinearConstantPropagationAnalysisScheduler with JavaIDEAnalysisScheduler.RTACallGraph + extends LinearConstantPropagationAnalysisScheduler with JavaIDEAnalysisSchedulerBase.RTACallGraph diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala index 1c101e6604..5bdf84c932 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala @@ -20,12 +20,12 @@ class EagerIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Stateme val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value], methodProvider: SomeProject => Iterable[Method] = { project => project.allMethodsWithBody } ) extends BaseIDEAnalysisProxyScheduler[Fact, Value, Statement, Callable] with FPCFEagerAnalysisScheduler { - def this(ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable]) = { + def this(ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable, ?]) = { this(ideAnalysisScheduler.propertyMetaInformation) } def this( - ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable], + ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable, ?], methodProvider: SomeProject => Iterable[Method] ) = { this(ideAnalysisScheduler.propertyMetaInformation, methodProvider) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala index d937e7d584..8ae93260dc 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala @@ -37,21 +37,28 @@ import org.opalj.ide.util.Logging * @param uniqueFlowsOnly whether to drop or to keep duplicated flows * @param recordEdgeFunctions whether to record edge functions too or just stick with the flow */ -class FlowRecordingAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( - ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable], +class FlowRecordingAnalysisScheduler[ + Fact <: IDEFact, + Value <: IDEValue, + Statement, + Callable <: Entity, + _ICFG <: ICFG[Statement, Callable] +]( + ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable, _ICFG], path: Option[Path] = Some(Paths.get("target/flow-recordings")), recorderMode: FlowRecorderMode = FlowRecorderModes.NODE_AS_STMT_AND_FACT, uniqueFlowsOnly: Boolean = true, recordEdgeFunctions: Boolean = true -) extends IDEAnalysisScheduler[Fact, Value, Statement, Callable] with Logging.EnableAll with Logging.GlobalLogContext { +) extends IDEAnalysisScheduler[Fact, Value, Statement, Callable, _ICFG] + with Logging.EnableAll with Logging.GlobalLogContext { override def propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] = { ideAnalysisScheduler.propertyMetaInformation } - override def createProblem(project: SomeProject): IDEProblem[Fact, Value, Statement, Callable] = { + override def createProblem(project: SomeProject, icfg: _ICFG): IDEProblem[Fact, Value, Statement, Callable] = { val flowRecordingProblem = new FlowRecordingIDEProblem( - ideAnalysisScheduler.createProblem(project), - createICFG(project), + ideAnalysisScheduler.createProblem(project, icfg), + icfg, recorderMode, uniqueFlowsOnly, recordEdgeFunctions @@ -60,13 +67,8 @@ class FlowRecordingAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue, Stateme flowRecordingProblem } - private var icfg: ICFG[Statement, Callable] = _ - - override def createICFG(project: SomeProject): ICFG[Statement, Callable] = { - if (icfg == null) { - icfg = ideAnalysisScheduler.createICFG(project) - } - icfg + override def createICFG(project: SomeProject): _ICFG = { + ideAnalysisScheduler.createICFG(project) } override def requiredProjectInformation: ProjectInformationKeys = { diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala index 89161948de..69ad80d5b0 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala @@ -20,15 +20,20 @@ import org.opalj.ide.solver.IDEAnalysis /** * A base scheduler for IDE analyses adding common default behavior */ -abstract class IDEAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity] - extends FPCFLazyAnalysisScheduler { +abstract class IDEAnalysisScheduler[ + Fact <: IDEFact, + Value <: IDEValue, + Statement, + Callable <: Entity, + _ICFG <: ICFG[Statement, Callable] +] extends FPCFLazyAnalysisScheduler { override final type InitializationData = IDEAnalysis[Fact, Value, Statement, Callable] def propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] - def createProblem(project: SomeProject): IDEProblem[Fact, Value, Statement, Callable] + def createProblem(project: SomeProject, icfg: _ICFG): IDEProblem[Fact, Value, Statement, Callable] - def createICFG(project: SomeProject): ICFG[Statement, Callable] + def createICFG(project: SomeProject): _ICFG override final def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(propertyMetaInformation.backingPropertyMetaInformation)) @@ -42,7 +47,9 @@ abstract class IDEAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue, Statemen override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} override final def init(project: SomeProject, ps: PropertyStore): IDEAnalysis[Fact, Value, Statement, Callable] = { - new IDEAnalysis(project, createProblem(project), createICFG(project), propertyMetaInformation) + val icfg = createICFG(project) + val problem = createProblem(project, icfg) + new IDEAnalysis(project, problem, icfg, propertyMetaInformation) } override final def register( diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala index 2f718934b2..258982fe64 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala @@ -17,7 +17,7 @@ import org.opalj.ide.solver.IDEAnalysisProxy class LazyIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] ) extends BaseIDEAnalysisProxyScheduler[Fact, Value, Statement, Callable] with FPCFLazyAnalysisScheduler { - def this(ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable]) = { + def this(ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable, ?]) = { this(ideAnalysisScheduler.propertyMetaInformation) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala index cff2f469dd..877dac4d26 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala @@ -1,20 +1,19 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ide.ifds.integration -import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.ide.ifds.integration.IFDSPropertyMetaInformation -import org.opalj.ide.ifds.problem.IFDSProblem import org.opalj.ide.ifds.problem.IFDSValue import org.opalj.ide.problem.IDEFact -import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler -import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement +import org.opalj.tac.fpcf.analyses.ide.ifds.problem.JavaIFDSProblem +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase +import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG /** * Specialized IDE analysis scheduler for IFDS problems with Java programs */ -abstract class JavaIFDSAnalysisScheduler[Fact <: IDEFact] extends JavaIDEAnalysisScheduler[Fact, IFDSValue] { +abstract class JavaIFDSAnalysisScheduler[Fact <: IDEFact] extends JavaIDEAnalysisSchedulerBase[Fact, IFDSValue] { override def propertyMetaInformation: IFDSPropertyMetaInformation[Fact] - override def createProblem(project: SomeProject): IFDSProblem[Fact, JavaStatement, Method] + override def createProblem(project: SomeProject, icfg: JavaICFG): JavaIFDSProblem[Fact] } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala index 06b03ba8d5..f766956abf 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -3,32 +3,30 @@ package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields import scala.collection.immutable -import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.PropertyBounds import org.opalj.ide.integration.IDEPropertyMetaInformation -import org.opalj.ide.problem.IDEProblem import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsFact import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsProblem import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsValue import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler -import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase +import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem +import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG /** * Linear constant propagation on fields as IDE analysis. This implementation is mainly intended to be an example of a * cyclic IDE analysis (see [[LCPOnFieldsProblem]]). */ abstract class LCPOnFieldsAnalysisScheduler extends JavaIDEAnalysisScheduler[LCPOnFieldsFact, LCPOnFieldsValue] - with JavaIDEAnalysisScheduler.ForwardICFG { + with JavaIDEAnalysisSchedulerBase.ForwardICFG { override def propertyMetaInformation: IDEPropertyMetaInformation[LCPOnFieldsFact, LCPOnFieldsValue] = LCPOnFieldsPropertyMetaInformation - override def createProblem(project: SomeProject): IDEProblem[ + override def createProblem(project: SomeProject, icfg: JavaICFG): JavaIDEProblem[ LCPOnFieldsFact, - LCPOnFieldsValue, - JavaStatement, - Method + LCPOnFieldsValue ] = { new LCPOnFieldsProblem } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala index 948d457586..3cf810340a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala @@ -3,35 +3,33 @@ package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields import scala.collection.immutable -import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.immutability.FieldImmutability import org.opalj.fpcf.PropertyBounds import org.opalj.ide.integration.IDEPropertyMetaInformation -import org.opalj.ide.problem.IDEProblem import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LinearConstantPropagationProblemExtended import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler -import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase +import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem +import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG /** * Extended linear constant propagation as IDE analysis */ abstract class LinearConstantPropagationAnalysisSchedulerExtended extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] - with JavaIDEAnalysisScheduler.ForwardICFG { + with JavaIDEAnalysisSchedulerBase.ForwardICFG { override def propertyMetaInformation: IDEPropertyMetaInformation[ LinearConstantPropagationFact, LinearConstantPropagationValue ] = LinearConstantPropagationPropertyMetaInformation - override def createProblem(project: SomeProject): IDEProblem[ + override def createProblem(project: SomeProject, icfg: JavaICFG): JavaIDEProblem[ LinearConstantPropagationFact, - LinearConstantPropagationValue, - JavaStatement, - Method + LinearConstantPropagationValue ] = { new LinearConstantPropagationProblemExtended(project, createICFG(project)) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala index ffb320f47b..33c55aa82d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala @@ -1,32 +1,30 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation -import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.ide.integration.IDEPropertyMetaInformation -import org.opalj.ide.problem.IDEProblem import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationProblem import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler -import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase +import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem +import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG /** * Linear constant propagation as IDE analysis */ abstract class LinearConstantPropagationAnalysisScheduler extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] - with JavaIDEAnalysisScheduler.ForwardICFG { + with JavaIDEAnalysisSchedulerBase.ForwardICFG { override def propertyMetaInformation: IDEPropertyMetaInformation[ LinearConstantPropagationFact, LinearConstantPropagationValue ] = LinearConstantPropagationPropertyMetaInformation - override def createProblem(project: SomeProject): IDEProblem[ + override def createProblem(project: SomeProject, icfg: JavaICFG): JavaIDEProblem[ LinearConstantPropagationFact, - LinearConstantPropagationValue, - JavaStatement, - Method + LinearConstantPropagationValue ] = { new LinearConstantPropagationProblem } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala index f576663c25..29a03d713f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala @@ -1,75 +1,16 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ide.integration -import scala.collection.immutable - -import org.opalj.br.Method -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties.cg.Callers -import org.opalj.fpcf.PropertyBounds -import org.opalj.ide.integration.IDEAnalysisScheduler import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEValue -import org.opalj.tac.cg.CallGraphKey -import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.cg.TypeIteratorKey -import org.opalj.tac.fpcf.analyses.ide.solver.JavaBackwardICFG -import org.opalj.tac.fpcf.analyses.ide.solver.JavaForwardICFG +import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG -import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement -import org.opalj.tac.fpcf.properties.TACAI /** * Specialized IDE analysis scheduler for Java programs */ abstract class JavaIDEAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue] - extends IDEAnalysisScheduler[Fact, Value, JavaStatement, Method] { - /** - * Key indicating which call graph should be used - */ - val callGraphKey: CallGraphKey - - override def createICFG(project: SomeProject): JavaICFG - - override def requiredProjectInformation: ProjectInformationKeys = - super.requiredProjectInformation ++ Seq( - DeclaredMethodsKey, - TypeIteratorKey, - callGraphKey - ) - - override def uses: Set[PropertyBounds] = - super.uses.union(immutable.Set( - PropertyBounds.finalP(TACAI), - PropertyBounds.finalP(Callers) - )) -} - -object JavaIDEAnalysisScheduler { - /** - * Trait to drop-in [[RTACallGraphKey]] as [[callGraphKey]] - */ - trait RTACallGraph { - val callGraphKey: CallGraphKey = RTACallGraphKey - } - - /** - * Trait to drop-in [[JavaForwardICFG]] for [[createICFG]] - */ - trait ForwardICFG { - def createICFG(project: SomeProject): JavaICFG = { - new JavaForwardICFG(project) - } - } - - /** - * Trait to drop-in [[JavaBackwardICFG]] for [[createICFG]] - */ - trait BackwardICFG { - def createICFG(project: SomeProject): JavaICFG = { - new JavaBackwardICFG(project) - } - } + extends JavaIDEAnalysisSchedulerBase[Fact, Value] { + override def createProblem(project: SomeProject, icfg: JavaICFG): JavaIDEProblem[Fact, Value] } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala new file mode 100644 index 0000000000..6d36d15fbd --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala @@ -0,0 +1,73 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.integration + +import scala.collection.immutable + +import org.opalj.br.Method +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.fpcf.PropertyBounds +import org.opalj.ide.integration.IDEAnalysisScheduler +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEValue +import org.opalj.tac.cg.CallGraphKey +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.cg.TypeIteratorKey +import org.opalj.tac.fpcf.analyses.ide.solver.JavaBackwardICFG +import org.opalj.tac.fpcf.analyses.ide.solver.JavaForwardICFG +import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement +import org.opalj.tac.fpcf.properties.TACAI + +/** + * A base IDE analysis scheduler for Java programs + */ +abstract class JavaIDEAnalysisSchedulerBase[Fact <: IDEFact, Value <: IDEValue] + extends IDEAnalysisScheduler[Fact, Value, JavaStatement, Method, JavaICFG] { + /** + * Key indicating which call graph should be used + */ + val callGraphKey: CallGraphKey + + override def requiredProjectInformation: ProjectInformationKeys = + super.requiredProjectInformation ++ Seq( + DeclaredMethodsKey, + TypeIteratorKey, + callGraphKey + ) + + override def uses: Set[PropertyBounds] = + super.uses.union(immutable.Set( + PropertyBounds.finalP(TACAI), + PropertyBounds.finalP(Callers) + )) +} + +object JavaIDEAnalysisSchedulerBase { + /** + * Trait to drop-in [[RTACallGraphKey]] as [[callGraphKey]] + */ + trait RTACallGraph { + val callGraphKey: CallGraphKey = RTACallGraphKey + } + + /** + * Trait to drop-in [[JavaForwardICFG]] for [[createICFG]] + */ + trait ForwardICFG { + def createICFG(project: SomeProject): JavaICFG = { + new JavaForwardICFG(project) + } + } + + /** + * Trait to drop-in [[JavaBackwardICFG]] for [[createICFG]] + */ + trait BackwardICFG { + def createICFG(project: SomeProject): JavaICFG = { + new JavaBackwardICFG(project) + } + } +} From 781925988a7115aa481ad1f31bf2152a24236f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 15 Nov 2024 12:36:27 +0100 Subject: [PATCH 098/167] Fix callable result for analysis targets with multiple returns In cases where the same fact reaches multiple returns, merging of the computed values did not happen correctly before. --- .../FieldReadWriteWithBranchingExample.java | 12 ++++++++++++ .../scala/org/opalj/ide/solver/IDEAnalysis.scala | 7 +++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java index 813ca6e792..c945a3e33a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java @@ -9,6 +9,18 @@ public class FieldReadWriteWithBranchingExample { private int a = -1; + @ObjectValue(variable = "lv0", variableValues = {@VariableValue(variable = "a")}) + public static FieldReadWriteWithBranchingExample multipleReturns(int y) { + FieldReadWriteWithBranchingExample e = new FieldReadWriteWithBranchingExample(); + if (y > 0) { + e.a = 42; + return e; + } else { + e.a = 23; + return e; + } + } + @ObjectValues({ @ObjectValue(variable = "lv0", constantValues = {@ConstantValue(variable = "a", value = 42)}), @ObjectValue(variable = "lv2", variableValues = {@VariableValue(variable = "a")}), diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 369b33352e..aaacd6f452 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -253,7 +253,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val resultsForExit = resultsByStatement .filter { case (n, _) => icfg.isNormalExitStatement(n) } - .flatMap(_._2.toList) + .map(_._2.toList) + .flatten .groupMapReduce(_._1)(_._2) { (value1, value2) => problem.lattice.meet(value1, value2) } @@ -706,9 +707,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent private def handleEdgeFunctionResult( edgeFunctionResult: EdgeFunctionResult[Value], path: Path - )( - implicit s: State - ): EdgeFunction[Value] = { + )(implicit s: State): EdgeFunction[Value] = { edgeFunctionResult match { case FinalEdgeFunction(edgeFunction) => edgeFunction From db957e7ea9f9279e52fc4f6d61e2fd7662aca878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 15 Nov 2024 15:17:18 +0100 Subject: [PATCH 099/167] Add cache for TAC --- .../tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala index 0142950eba..ddb41abf6e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala @@ -1,6 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ide.solver +import scala.collection.mutable + import org.opalj.br.Method import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey @@ -29,14 +31,20 @@ import org.opalj.value.ValueInformation * [[org.opalj.tac.fpcf.analyses.ifds.JavaICFG]] from IFDS. */ abstract class JavaBaseICFG(project: SomeProject) extends JavaICFG { - protected val tacProvider: Method => AITACode[TACMethodParameter, ValueInformation] = { - // TODO (IDE) DOCS SAY, THAT LazyDetachedTACAIKey DOES NOT CACHE ANYTHING + private val lazyTacProvider: Method => AITACode[TACMethodParameter, ValueInformation] = { project.get(LazyDetachedTACAIKey) } + protected implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) protected implicit val contextProvider: ContextProvider = project.get(ContextProviderKey) protected val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + private val tacProviderCache = mutable.Map.empty[Method, AITACode[TACMethodParameter, ValueInformation]] + + def tacProvider(callable: Method): AITACode[TACMethodParameter, ValueInformation] = { + tacProviderCache.getOrElseUpdate(callable, { lazyTacProvider(callable) }) + } + override def isCallStatement(javaStmt: JavaStatement): Boolean = { if (javaStmt.isReturnNode) { return false From 12ef9512408a4c886952e6befa8d1cc8df120e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 15 Nov 2024 15:22:57 +0100 Subject: [PATCH 100/167] Resolve TODOs --- .../problem/LinearConstantPropagationProblem.scala | 2 -- .../tac/fpcf/analyses/ide/solver/JavaStatement.scala | 8 ++------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index bb8c945adf..0fc5116040 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -390,8 +390,6 @@ class LinearConstantPropagationProblem /* If boundaries are equal, the value is constant */ Some(intRange.lowerBound) } else if (var0.definedBy.size > 1) { - // TODO (IDE) ADD TESTS FOR THIS (CAN WE REFACTOR THIS/DOES THIS REDUCE THE - // ACCURACY) return VariableValueEdgeFunction } else { None diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala index 5509503e39..de50fe3117 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala @@ -19,17 +19,13 @@ import org.opalj.value.ValueInformation */ case class JavaStatement( method: Method, - pc: Int, + pc: Int, isReturnNode: Boolean = false, - stmts: Array[Stmt[JavaStatement.V]], + stmts: Array[Stmt[JavaStatement.V]], cfg: CFG[Stmt[JavaStatement.V], TACStmts[JavaStatement.V]] ) { - // TODO (IDE) DOES THIS GET REEVALUATED EACH CALL? DO WE ALWAYS CALL IT AT LEAST ONCE? MAYBE MAKE IT A (LAZY) - // PROPERTY def stmt: Stmt[JavaStatement.V] = stmts(pc) - // TODO (IDE) DOES THIS GET REEVALUATED EACH CALL? DO WE ALWAYS CALL IT AT LEAST ONCE? MAYBE MAKE IT A (LAZY) - // PROPERTY def basicBlock: BasicBlock = cfg.bb(pc) override def hashCode(): Int = method.hashCode() * 31 + pc From 47383b5abe4b25ad6b0e515edcef4c94a6f583d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 15 Nov 2024 15:41:23 +0100 Subject: [PATCH 101/167] Optimize second phase of IDE solver --- .../org/opalj/ide/solver/IDEAnalysis.scala | 60 +++++++------------ 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index aaacd6f452..4830ea52cd 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -96,11 +96,6 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent */ private val endSummaries = mutable.Map.empty[Node, mutable.Set[(Node, JumpFunction)]] - /** - * Collection of all callables that were visited in P1 - */ - private val seenCallables = mutable.Set.empty[Callable] - /** * Map call targets to all seen call sources (similar to a call graph but reversed; needed for endSummaries * extension) @@ -181,14 +176,6 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent endSummaries.getOrElse(start, immutable.Set.empty) } - def rememberCallable(callable: Callable): Unit = { - seenCallables.add(callable) - } - - def getAllSeenCallables: collection.Set[Callable] = { - seenCallables - } - def rememberCallEdge(path: Path): Unit = { val (source, target) = path @@ -441,8 +428,6 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent private def seedPhase1()(implicit s: State): Unit = { val callable = s.targetCallable - s.rememberCallable(callable) - // IDE P1 lines 5 - 6 icfg.getStartStatements(callable).foreach { stmt => val path = ((stmt, problem.nullFact), (stmt, problem.nullFact)) @@ -516,9 +501,6 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } } } else { - // TODO (IDE) ALSO COLLECTS JRE METHODS -> P2 part (ii) MAY GET VERY SLOW - s.rememberCallable(q) - val sqs = icfg.getStartStatements(q) sqs.foreach { sq => // IDE P1 lines 12 - 13 @@ -735,8 +717,6 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logDebug(s"seeded with ${s.getNodeWorkListSize} node(s)") } - // TODO (IDE) TO SPEEDUP THE ANALYSIS WE SHOULD ONLY CALCULATE THE VALUES, WE ARE INTERESTED IN/THE USER REQUESTED. - // ESPECIALLY THE VALUES CALCULATED BY P2 part (ii) ARE NEVER USED CURRENTLY private def computeValues()(implicit s: State): Unit = { logDebug("starting phase 2 (i)") @@ -763,26 +743,26 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // IDE P2 part (ii) // IDE P2 lines 15 - 17 - s.getAllSeenCallables.foreach { p => - val sps = icfg.getStartStatements(p) - val ns = collectReachableStmts(sps, stmt => !icfg.isCallStatement(stmt)) - - // IDE P2 line 16 - 17 - sps.foreach { sp => - ns.foreach { n => - val jumpFunctionsMatchingTarget = s.lookupJumpFunctions(source = Some(sp), target = Some(n)) - jumpFunctionsMatchingTarget.foreach { - case (((_, dPrime), (_, d)), fPrime) if !fPrime.equalTo(allTopEdgeFunction) => - val nSharp = (n, d) - val vPrime = problem.lattice.meet( - s.getValue((n, d)), - fPrime.compute(s.getValue((sp, dPrime))) - ) - - logTrace(s"setting value of nSharp=${nodeToString(nSharp)} to vPrime=$vPrime") - - s.setValue(nSharp, vPrime) - } + // Reduced to the one callable, results are created for + val p = s.targetCallable + val sps = icfg.getStartStatements(p) + val ns = collectReachableStmts(sps, stmt => !icfg.isCallStatement(stmt)) + + // IDE P2 line 16 - 17 + sps.foreach { sp => + ns.foreach { n => + val jumpFunctionsMatchingTarget = s.lookupJumpFunctions(source = Some(sp), target = Some(n)) + jumpFunctionsMatchingTarget.foreach { + case (((_, dPrime), (_, d)), fPrime) if !fPrime.equalTo(allTopEdgeFunction) => + val nSharp = (n, d) + val vPrime = problem.lattice.meet( + s.getValue((n, d)), + fPrime.compute(s.getValue((sp, dPrime))) + ) + + logTrace(s"setting value of nSharp=${nodeToString(nSharp)} to vPrime=$vPrime") + + s.setValue(nSharp, vPrime) } } } From 79bffb71e073dcec8806257e3ccc662d84ca67a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 15 Nov 2024 15:41:56 +0100 Subject: [PATCH 102/167] Update TODOs --- .../src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 4830ea52cd..583c84a287 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -344,10 +344,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent private def performPhase2()(implicit s: State): Unit = { logDebug("starting phase 2") - // TODO (IDE) PHASE 2 IS PERFORMED ALSO FOR INTERIM RESULTS TO MAKE CYCLIC ANALYSES POSSIBLE - // - PHASE 2 IS PERFORMED FROM SCRATCH ON EACH UPDATE AT THE MOMENT (THIS SHOULD ALWAYS PROCUDE AN UPPER BOUND - // OF THE FINAL VALUE) - // - DO WE NEED TO RERUN IT FROM SCRATCH EACH TIME OR IS THERE AN INCREMENTAL SOLUTION? + // TODO (IDE) PHASE 2 IS PERFORMED FROM SCRATCH ON EACH UPDATE AT THE MOMENT. CONSIDER IMPLEMENTING AN + // INCREMENTAL SOLUTION (DECIDE WHAT COULD HAVE CHANGED WHILE RUNNING PHASE 1) s.clearValues() seedPhase2() From 73470d2e66532ff744d5c7f8e8e729d7dc16f271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 2 Dec 2024 18:35:29 +0100 Subject: [PATCH 103/167] Allow custom continuations when handling an edge function result --- .../scala/org/opalj/ide/solver/IDEAnalysis.scala | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 583c84a287..d22a2c2425 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -687,6 +687,17 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent private def handleEdgeFunctionResult( edgeFunctionResult: EdgeFunctionResult[Value], path: Path + )(implicit s: State): EdgeFunction[Value] = { + handleEdgeFunctionResult(edgeFunctionResult, () => s.enqueuePath(path)) + } + + /** + * @param continuation the continuation to execute when the dependee changes (in case of an interim edge function) + * @return the (interim) edge function from the result + */ + private def handleEdgeFunctionResult( + edgeFunctionResult: EdgeFunctionResult[Value], + continuation: () => Unit )(implicit s: State): EdgeFunction[Value] = { edgeFunctionResult match { case FinalEdgeFunction(edgeFunction) => @@ -695,7 +706,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent dependees.foreach { dependee => s.addDependee( dependee, - () => s.enqueuePath(path) + continuation ) } intermediateEdgeFunction From d887c2e39281a4300beb3961aea33a51347daf8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 2 Dec 2024 18:46:56 +0100 Subject: [PATCH 104/167] Allow custom edge functions for flows starting with additional seeds --- .../opalj/ide/problem/FlowRecordingIDEProblem.scala | 6 ++++++ .../main/scala/org/opalj/ide/problem/IDEProblem.scala | 10 ++++++++++ .../main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 11 ++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index 7173b2ed6c..cef93585f1 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -72,6 +72,12 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal baseProblem.getAdditionalSeeds(stmt, callee) } + override def getAdditionalSeedsEdgeFunction(stmt: Statement, fact: Fact, callee: Callable)( + implicit propertyStore: PropertyStore + ): EdgeFunctionResult[Value] = { + baseProblem.getAdditionalSeedsEdgeFunction(stmt, fact, callee) + } + override def getNormalFlowFunction( source: Statement, sourceFact: Fact, diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index 31540d9928..dba075baac 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -48,6 +48,16 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl implicit @unused propertyStore: PropertyStore ): collection.Set[Fact] = immutable.Set.empty + /** + * Generate an edge function for a flow starting with an additional seeds + * @param stmt the start statement + * @param fact the start fact + * @param callee the analyzed callable + */ + def getAdditionalSeedsEdgeFunction(stmt: Statement, fact: Fact, callee: Callable)( + implicit @unused propertyStore: PropertyStore + ): EdgeFunctionResult[Value] = identityEdgeFunction + /** * Generate a flow function for a normal flow * @param source where the normal flow starts diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index d22a2c2425..871c711875 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -434,8 +434,17 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent problem.getAdditionalSeeds(stmt, callable).foreach { fact => val path = ((stmt, problem.nullFact), (stmt, fact)) - s.enqueuePath(path) s.setJumpFunction(path, identityEdgeFunction) + def processAdditionalSeed(): Unit = { + val edgeFunction = + handleEdgeFunctionResult( + problem.getAdditionalSeedsEdgeFunction(stmt, fact, callable), + processAdditionalSeed _ + ) + s.setJumpFunction(path, s.getJumpFunction(path).meetWith(edgeFunction)) + s.enqueuePath(path) + } + processAdditionalSeed() } } From bdc31b0ff78dc748894ae38a1b88f6942c1c8283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 4 Dec 2024 10:07:33 +0100 Subject: [PATCH 105/167] Improve LCP on fields for static fields --- .../StaticFieldImmutableExample.java | 15 +- .../StaticFieldNonImmutableExample.java | 22 +- ...ticFieldReadWriteAcrossMethodsExample.java | 39 +++ .../StaticFieldReadWriteExample.java | 37 +++ .../lcp_on_fields/StaticValues.java | 33 +++ .../lcp_on_fields/LCPOnFieldsMatcher.scala | 123 ++++++++++ .../LCPOnFieldsAnalysisScheduler.scala | 11 +- ...PropagationAnalysisSchedulerExtended.scala | 4 +- .../problem/LCPOnFieldsEdgeFunctions.scala | 50 ++++ .../problem/LCPOnFieldsFact.scala | 34 +++ .../problem/LCPOnFieldsProblem.scala | 231 +++++++++++++++++- .../problem/LCPOnFieldsValue.scala | 9 + ...arConstantPropagationProblemExtended.scala | 131 ++++------ 13 files changed, 631 insertions(+), 108 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteAcrossMethodsExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteExample.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/StaticValues.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java index 846a425fa5..8735eb14a0 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java @@ -1,6 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.lcp_on_fields; +import org.opalj.fpcf.properties.lcp_on_fields.StaticValues; import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValues; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; @@ -9,6 +10,7 @@ public final class StaticFieldImmutableExample { protected static int a = 42; static int b; private static int c; + private static int d; static { b = 23; @@ -20,16 +22,25 @@ public final class StaticFieldImmutableExample { } } + @StaticValues(constantValues = { + @ConstantValue(variable = "a", value = 42), + @ConstantValue(variable = "b", value = 23), + @ConstantValue(variable = "d", value = 0) + }, variableValues = { + @VariableValue(variable = "c") + }) @ConstantValues({ @ConstantValue(variable = "lv0", value = 42), - @ConstantValue(variable = "lv1", value = 23) + @ConstantValue(variable = "lv1", value = 23), + @ConstantValue(variable = "lv3", value = 0) }) @VariableValue(variable = "lv2") public static void main(String[] args) { int a = StaticFieldImmutableExample.a; int b = StaticFieldImmutableExample.b; int c = StaticFieldImmutableExample.c; + int d = StaticFieldImmutableExample.d; - System.out.println("a: " + a + ", b: " + b + ", c: " + c); + System.out.println("a: " + a + ", b: " + b + ", c: " + c + ", d: " + d); } } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldNonImmutableExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldNonImmutableExample.java index 1dc448b9a2..1c3907716e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldNonImmutableExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldNonImmutableExample.java @@ -1,32 +1,32 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.lcp_on_fields; +import org.opalj.fpcf.properties.lcp_on_fields.StaticValues; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; public class StaticFieldNonImmutableExample { - private static int a = 11; - static int b = 42; - protected static int c = 23; + static int a = 42; + protected static int b = 23; + @StaticValues(variableValues = { + @VariableValue(variable = "a"), + @VariableValue(variable = "b") + }) @VariableValues({ - @VariableValue(variable = "lv2"), - @VariableValue(variable = "lv3"), - @VariableValue(variable = "lv4") + @VariableValue(variable = "lv0"), + @VariableValue(variable = "lv1") }) public static void main(String[] args) { - a = 12; - int a = StaticFieldNonImmutableExample.a; int b = StaticFieldNonImmutableExample.b; - int c = StaticFieldNonImmutableExample.c; - System.out.println("a: " + a + ", b: " + b + ", c: " + c); + System.out.println("a: " + a + ", b: " + b); } } class StaticFieldNonImmutable2Example { void foobar() { - StaticFieldNonImmutableExample.b = 23; + StaticFieldNonImmutableExample.a = 23; } } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteAcrossMethodsExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteAcrossMethodsExample.java new file mode 100644 index 0000000000..e1397e08c1 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteAcrossMethodsExample.java @@ -0,0 +1,39 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.lcp_on_fields; + +import org.opalj.fpcf.properties.lcp_on_fields.StaticValues; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValues; + +public class StaticFieldReadWriteAcrossMethodsExample { + private static int a; + + public void setATo11() { + a = 11; + } + + private void setATo42() { + a = 42; + } + + @StaticValues(constantValues = { + @ConstantValue(variable = "a", value = 42) + }) + @ConstantValues({ + @ConstantValue(variable = "lv3", value = 11), + @ConstantValue(variable = "lv5", value = 42) + }) + public static void main(String[] args) { + StaticFieldReadWriteAcrossMethodsExample example = new StaticFieldReadWriteAcrossMethodsExample(); + + example.setATo11(); + + int a1 = a; + + example.setATo42(); + + int a2 = a; + + System.out.println("a1: " + a1 + ", a2: " + a2); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteExample.java new file mode 100644 index 0000000000..f8c1e6adcc --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteExample.java @@ -0,0 +1,37 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.lcp_on_fields; + +import org.opalj.fpcf.properties.lcp_on_fields.StaticValues; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValues; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; + +public class StaticFieldReadWriteExample { + private static int a; + + @StaticValues(constantValues = { + @ConstantValue(variable = "a", value = 11) + }) + @ConstantValues({ + @ConstantValue(variable = "lv5", value = 23), + @ConstantValue(variable = "lva", value = 11) + }) + @VariableValue(variable = "lv2") + public static void main(String[] args) { + StaticFieldReadWriteExample example1 = new StaticFieldReadWriteExample(); + + int a1 = a; + + a = 23; + + int a2 = example1.a; + + example1.a = 11; + + StaticFieldReadWriteExample example2 = new StaticFieldReadWriteExample(); + + int a3 = example2.a; + + System.out.println("a1: " + a1 + ", a2: " + a2 + ", a3: " + a3); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/StaticValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/StaticValues.java new file mode 100644 index 0000000000..7cefe01f0b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/StaticValues.java @@ -0,0 +1,33 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.lcp_on_fields; + +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +import org.opalj.fpcf.properties.linear_constant_propagation.UnknownValue; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; + +import java.lang.annotation.*; + +/** + * Annotation to state that an object has certain constant and non-constant static values + */ +@PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = StaticValuesMatcher.class) +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface StaticValues { + /** + * The constant static fields + */ + ConstantValue[] constantValues() default {}; + + /** + * The non-constant static fields + */ + VariableValue[] variableValues() default {}; + + /** + * The static fields with unknown value + */ + UnknownValue[] unknownValues() default {}; +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala index b28467c649..168aa5a95f 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala @@ -2,9 +2,11 @@ package org.opalj.fpcf.properties.lcp_on_fields import org.opalj.br.AnnotationLike +import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.br.analyses.Project import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractPropertyMatcher import org.opalj.fpcf.properties.AbstractRepeatablePropertyMatcher import org.opalj.ide.integration.BasicIDEProperty import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields @@ -231,6 +233,127 @@ class ArrayValueMatcher extends AbstractRepeatablePropertyMatcher { } } +/** + * Matcher for [[StaticValues]] annotations + */ +class StaticValuesMatcher extends AbstractPropertyMatcher { + private val annotationType: ObjectType = + ObjectType("org/opalj/fpcf/properties/lcp_on_fields/ObjectValue") + + private val constantValueType = ObjectType("org/opalj/fpcf/properties/linear_constant_propagation/ConstantValue") + private val variableValueType = ObjectType("org/opalj/fpcf/properties/linear_constant_propagation/VariableValue") + private val unknownValueType = ObjectType("org/opalj/fpcf/properties/linear_constant_propagation/UnknownValue") + + override def validateProperty( + p: Project[?], + as: Set[ObjectType], + entity: Any, + a: AnnotationLike, + properties: Iterable[Property] + ): Option[String] = { + val entityObjectType = entity.asInstanceOf[Method].classFile.thisType + + val expectedConstantValues = + getValue(p, annotationType, a.elementValuePairs, "constantValues").asArrayValue.values + .map { a => + val annotation = a.asAnnotationValue.annotation + val expectedFieldName = + getValue(p, constantValueType, annotation.elementValuePairs, "variable").asStringValue.value + val expectedValue = + getValue(p, constantValueType, annotation.elementValuePairs, "value").asIntValue.value + + (expectedFieldName, expectedValue) + } + + val expectedVariableValues = + getValue(p, annotationType, a.elementValuePairs, "variableValues").asArrayValue.values + .map { a => + val annotation = a.asAnnotationValue.annotation + val expectedFieldName = + getValue(p, variableValueType, annotation.elementValuePairs, "variable").asStringValue.value + + expectedFieldName + } + + val expectedUnknownValues = + getValue(p, annotationType, a.elementValuePairs, "unknownValues").asArrayValue.values + .map { a => + val annotation = a.asAnnotationValue.annotation + val expectedFieldName = + getValue(p, unknownValueType, annotation.elementValuePairs, "variable").asStringValue.value + + expectedFieldName + } + + if (properties.exists { + case property: BasicIDEProperty[?, ?] => + expectedConstantValues.forall { + case (fieldName, value) => + property.results.exists { + case ( + f: lcp_on_fields.problem.AbstractStaticFieldFact, + lcp_on_fields.problem.StaticFieldValue(v) + ) => + f.objectType == entityObjectType && f.fieldName == fieldName && + (v match { + case linear_constant_propagation.problem.ConstantValue(c) => value == c + case _ => false + }) + + case _ => false + } + } && + expectedVariableValues.forall { fieldName => + property.results.exists { + case ( + f: lcp_on_fields.problem.AbstractStaticFieldFact, + lcp_on_fields.problem.StaticFieldValue(v) + ) => + f.objectType == entityObjectType && f.fieldName == fieldName && + v == linear_constant_propagation.problem.VariableValue + + case _ => false + } + } && + expectedUnknownValues.forall { fieldName => + property.results.exists { + case ( + f: lcp_on_fields.problem.AbstractStaticFieldFact, + lcp_on_fields.problem.StaticFieldValue(v) + ) => + f.objectType == entityObjectType && f.fieldName == fieldName && + v == linear_constant_propagation.problem.UnknownValue + + case _ => false + } + } + + case _ => false + } + ) { + None + } else { + val expectedValues = + expectedConstantValues + .map { case (fieldName, c) => fieldName -> linear_constant_propagation.problem.ConstantValue(c) } + .concat(expectedVariableValues.map { fieldName => + fieldName -> linear_constant_propagation.problem.VariableValue + }) + .concat(expectedUnknownValues.map { fieldName => + fieldName -> linear_constant_propagation.problem.UnknownValue + }) + .toMap + Some( + s"Result should contain ${expectedValues.map { + case (fieldName, value) => + s"(${lcp_on_fields.problem.StaticFieldFact(entityObjectType, fieldName)}, ${lcp_on_fields.problem.StaticFieldValue(value)})" + + }}" + ) + } + } +} + /** * Matcher for [[VariableValue]] and [[VariableValues]] annotations */ diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala index f766956abf..e0fd38c295 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -3,7 +3,10 @@ package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields import scala.collection.immutable +import org.opalj.br.analyses.DeclaredFieldsKey +import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.immutability.FieldImmutability import org.opalj.fpcf.PropertyBounds import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsFact @@ -28,11 +31,17 @@ abstract class LCPOnFieldsAnalysisScheduler extends JavaIDEAnalysisScheduler[LCP LCPOnFieldsFact, LCPOnFieldsValue ] = { - new LCPOnFieldsProblem + new LCPOnFieldsProblem(project, icfg) } + override def requiredProjectInformation: ProjectInformationKeys = + super.requiredProjectInformation ++ Seq( + DeclaredFieldsKey + ) + override def uses: Set[PropertyBounds] = super.uses.union(immutable.Set( + PropertyBounds.ub(FieldImmutability), PropertyBounds.ub(LinearConstantPropagationPropertyMetaInformation) )) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala index 3cf810340a..52500db56d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala @@ -4,7 +4,6 @@ package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields import scala.collection.immutable import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties.immutability.FieldImmutability import org.opalj.fpcf.PropertyBounds import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LinearConstantPropagationProblemExtended @@ -31,12 +30,11 @@ abstract class LinearConstantPropagationAnalysisSchedulerExtended LinearConstantPropagationFact, LinearConstantPropagationValue ] = { - new LinearConstantPropagationProblemExtended(project, createICFG(project)) + new LinearConstantPropagationProblemExtended } override def uses: Set[PropertyBounds] = super.uses.union(immutable.Set( - PropertyBounds.ub(FieldImmutability), PropertyBounds.ub(LCPOnFieldsPropertyMetaInformation) )) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala index e4832c1f6c..7a172b078f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -312,6 +312,53 @@ case class PutElementEdgeFunction( }) } +/** + * Edge function modeling the effect of when a static field gets written + */ +case class PutStaticFieldEdgeFunction( + value: LinearConstantPropagationValue +) extends EdgeFunction[LCPOnFieldsValue] { + override def compute(sourceValue: LCPOnFieldsValue): LCPOnFieldsValue = { + StaticFieldValue(value) + } + + override def composeWith(secondEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = { + secondEdgeFunction match { + case PutStaticFieldEdgeFunction(_) => secondEdgeFunction + + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => secondEdgeFunction + case AllBottomEdgeFunction(_) => secondEdgeFunction + + case _ => + throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") + } + } + + override def meetWith(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = { + otherEdgeFunction match { + case PutStaticFieldEdgeFunction(value2) => + PutStaticFieldEdgeFunction( + LinearConstantPropagationLattice.meet(value, value2) + ) + + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => this + case AllBottomEdgeFunction(_) => otherEdgeFunction + + case _ => + throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + } + } + + override def equalTo(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): Boolean = + (otherEdgeFunction eq this) || + (otherEdgeFunction match { + case PutStaticFieldEdgeFunction(value2) => value == value2 + case _ => false + }) +} + /** * Edge function for cases where a value is unknown */ @@ -322,6 +369,9 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LCPOnFieldsValue](Unk this } + override def equalTo(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): Boolean = + otherEdgeFunction eq this + override def toString: String = "UnknownValueEdgeFunction()" } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala index ce534f4044..a87b982823 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala @@ -1,6 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem +import org.opalj.br.ObjectType import org.opalj.ide.problem.IDEFact /** @@ -93,3 +94,36 @@ case class NewArrayFact(name: String, definedAtIndex: Int) extends AbstractArray case class PutElementFact(name: String, definedAtIndex: Int) extends AbstractArrayFact { override def toString: String = s"PutElementFact($name)" } + +/** + * Type for facts for static fields + */ +trait AbstractStaticFieldFact extends LCPOnFieldsFact { + /** + * The object type the field belongs to + */ + val objectType: ObjectType + + /** + * The name of the field + */ + val fieldName: String + + def toStaticFieldFact: AbstractStaticFieldFact = StaticFieldFact(objectType, fieldName) +} + +/** + * Fact representing a seen static field + */ +case class StaticFieldFact(objectType: ObjectType, fieldName: String) extends AbstractStaticFieldFact { + override def toStaticFieldFact: StaticFieldFact = this + + override def toString: String = s"StaticFieldFact(${objectType.simpleName}, $fieldName)" +} + +/** + * Fact representing a seen static field and modeling that it gets written + */ +case class PutStaticFieldFact(objectType: ObjectType, fieldName: String) extends AbstractStaticFieldFact { + override def toString: String = s"PutStaticFieldFact(${objectType.simpleName}, $fieldName)" +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 34c8b5136f..a12f5b8fdc 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -2,8 +2,16 @@ package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem import scala.collection.immutable +import scala.collection.mutable +import org.opalj.ai.domain.l1.DefaultIntegerRangeValues +import org.opalj.br.Field +import org.opalj.br.IntegerType import org.opalj.br.Method +import org.opalj.br.analyses.DeclaredFieldsKey +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.immutability.FieldImmutability +import org.opalj.br.fpcf.properties.immutability.TransitivelyImmutableField import org.opalj.fpcf.FinalP import org.opalj.fpcf.InterimUBP import org.opalj.fpcf.Property @@ -19,11 +27,13 @@ import org.opalj.tac.Assignment import org.opalj.tac.New import org.opalj.tac.NewArray import org.opalj.tac.PutField +import org.opalj.tac.PutStatic import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationLattice import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem +import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall @@ -31,16 +41,136 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall * Definition of linear constant propagation on fields problem. This implementation detects basic cases of linear * constant propagation involving fields. It detects direct field assignments but fails to detect assignments done in a * method where the value is passed as an argument (e.g. a classical setter). Similar, array read accesses can only be - * resolved if the index is a constant literal. + * resolved if the index is a constant literal. There also is just minimal support for static fields. * This implementation is mainly intended to be an example of a cyclic IDE analysis. */ -class LCPOnFieldsProblem extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValue] { +class LCPOnFieldsProblem( + project: SomeProject, + icfg: JavaICFG +) extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValue] { + private val declaredFields = project.get(DeclaredFieldsKey) + override val nullFact: LCPOnFieldsFact = NullFact override val lattice: MeetLattice[LCPOnFieldsValue] = LCPOnFieldsLattice + override def getAdditionalSeeds(stmt: JavaStatement, callee: Method)( + implicit propertyStore: PropertyStore + ): collection.Set[LCPOnFieldsFact] = { + callee.classFile.fields.filter(_.isStatic).map { field => + StaticFieldFact(field.classFile.thisType, field.name) + }.toSet + } + + override def getAdditionalSeedsEdgeFunction(stmt: JavaStatement, fact: LCPOnFieldsFact, callee: Method)( + implicit propertyStore: PropertyStore + ): EdgeFunctionResult[LCPOnFieldsValue] = { + fact match { + case f @ StaticFieldFact(_, _) => getEdgeFunctionForStaticFieldFactByImmutability(f) + case _ => super.getAdditionalSeedsEdgeFunction(stmt, fact, callee) + } + } + + private def getEdgeFunctionForStaticFieldFactByImmutability(staticFieldFact: StaticFieldFact)( + implicit propertyStore: PropertyStore + ): EdgeFunctionResult[LCPOnFieldsValue] = { + val declaredField = + declaredFields( + staticFieldFact.objectType, + staticFieldFact.fieldName, + IntegerType + ) + if (!declaredField.isDefinedField) { + return identityEdgeFunction + } + val field = declaredField.definedField + + /* We enhance the analysis with immutability information. When a static field is immutable and we have knowledge + * of an assignment site, then this will always be the value of the field. This way we can make this analysis + * more precise without the need to add precise handling of static initializers. */ + val fieldImmutabilityEOptionP = propertyStore(field, FieldImmutability.key) + + fieldImmutabilityEOptionP match { + case FinalP(fieldImmutability) => + fieldImmutability match { + case TransitivelyImmutableField => + val value = getValueForGetStaticExprByStaticInitializer(field) + FinalEdgeFunction(PutStaticFieldEdgeFunction(value)) + case _ => + FinalEdgeFunction(PutStaticFieldEdgeFunction(linear_constant_propagation.problem.VariableValue)) + } + + case InterimUBP(fieldImmutability) => + fieldImmutability match { + case TransitivelyImmutableField => + val value = getValueForGetStaticExprByStaticInitializer(field) + InterimEdgeFunction(PutStaticFieldEdgeFunction(value), immutable.Set(fieldImmutabilityEOptionP)) + case _ => + FinalEdgeFunction(PutStaticFieldEdgeFunction(linear_constant_propagation.problem.VariableValue)) + } + + case _ => + InterimEdgeFunction( + PutStaticFieldEdgeFunction(linear_constant_propagation.problem.UnknownValue), + immutable.Set(fieldImmutabilityEOptionP) + ) + } + } + + private def getValueForGetStaticExprByStaticInitializer(field: Field): LinearConstantPropagationValue = { + var value: LinearConstantPropagationValue = linear_constant_propagation.problem.UnknownValue + + /* Search for statements that write the field in static initializer of the class belonging to the field. */ + field.classFile.staticInitializer match { + case Some(method) => + var workingStmts: mutable.Set[JavaStatement] = mutable.Set.from(icfg.getStartStatements(method)) + val seenStmts = mutable.Set.empty[JavaStatement] + + while (workingStmts.nonEmpty) { + workingStmts.foreach { stmt => + stmt.stmt.astID match { + case PutStatic.ASTID => + val putStatic = stmt.stmt.asPutStatic + if (field.classFile.thisType == putStatic.declaringClass && + field.name == putStatic.name + ) { + stmt.stmt.asPutStatic.value.asVar.value match { + case intRange: DefaultIntegerRangeValues#IntegerRange => + if (intRange.lowerBound == intRange.upperBound) { + value = LinearConstantPropagationLattice.meet( + value, + linear_constant_propagation.problem.ConstantValue(intRange.upperBound) + ) + } else { + return linear_constant_propagation.problem.VariableValue + } + + case _ => + return linear_constant_propagation.problem.VariableValue + } + } + + case _ => + } + } + + seenStmts.addAll(workingStmts) + workingStmts = workingStmts.foldLeft(mutable.Set.empty[JavaStatement]) { (nextStmts, stmt) => + nextStmts.addAll(icfg.getNextStatements(stmt)) + }.diff(seenStmts) + } + + case _ => + } + + value match { + case linear_constant_propagation.problem.UnknownValue => linear_constant_propagation.problem.ConstantValue(0) + case _ => value + } + } + override def getNormalFlowFunction( source: JavaStatement, sourceFact: LCPOnFieldsFact, @@ -96,6 +226,48 @@ class LCPOnFieldsProblem extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValu /* Specialized facts only live for one step and are turned back into basic ones afterwards */ immutable.Set(f.toObjectOrArrayFact) + /* Static fields are modeled such that statements that change their value can always originate from + * the null fact */ + case (PutStatic.ASTID, NullFact) => + val putStatic = source.stmt.asPutStatic + + /* Only fields of type integer */ + if (putStatic.declaredFieldType.isIntegerType) { + val declaredField = + declaredFields( + putStatic.declaringClass, + putStatic.name, + putStatic.declaredFieldType + ) + if (!declaredField.isDefinedField) { + return immutable.Set(sourceFact) + } + val field = declaredField.definedField + + /* Only private fields (as they cannot be influenced by other static initializers) */ + if (field.isPrivate) { + immutable.Set(sourceFact, PutStaticFieldFact(putStatic.declaringClass, putStatic.name)) + } else { + immutable.Set(sourceFact) + } + } else { + immutable.Set(sourceFact) + } + + case (PutStatic.ASTID, f: AbstractStaticFieldFact) => + val putStatic = source.stmt.asPutStatic + + /* Drop existing fact if for the same static field */ + if (f.objectType == putStatic.declaringClass && f.fieldName == putStatic.name) { + immutable.Set.empty + } else { + immutable.Set(f.toStaticFieldFact) + } + + case (_, f: AbstractStaticFieldFact) => + /* Specialized facts only live for one step and are turned back into basic ones afterwards */ + immutable.Set(f.toStaticFieldFact) + case _ => immutable.Set(sourceFact) } } @@ -119,6 +291,12 @@ class LCPOnFieldsProblem extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValu callee.returnType.asArrayType.componentType.isIntegerType ) { immutable.Set(callSiteFact) + } else if (callee.classFile.fields.exists { field => field.isStatic } && + !callee.classFile.fqn.startsWith("java/") && + !callee.classFile.fqn.startsWith("sun/") + ) { + /* The null fact is needed for writing static fields */ + immutable.Set(callSiteFact) } else { immutable.Set.empty } @@ -143,6 +321,14 @@ class LCPOnFieldsProblem extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValu } } .toSet + + case f: AbstractStaticFieldFact => + /* Only propagate fact if the callee can access the corresponding static field */ + if (callee.classFile.thisType == f.objectType) { + immutable.Set(f.toStaticFieldFact) + } else { + immutable.Set.empty + } } } } @@ -201,6 +387,9 @@ class LCPOnFieldsProblem extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValu case _ => immutable.Set.empty } } + + case f: AbstractStaticFieldFact => + immutable.Set(f.toStaticFieldFact) } } } @@ -229,6 +418,14 @@ class LCPOnFieldsProblem extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValu } else { immutable.Set(f.toObjectOrArrayFact) } + + case f: AbstractStaticFieldFact => + /* Propagate facts that are not propagated via the call flow */ + if (callee.classFile.thisType == f.objectType) { + immutable.Set.empty + } else { + immutable.Set(f.toStaticFieldFact) + } } } } @@ -320,6 +517,36 @@ class LCPOnFieldsProblem extends JavaIDEProblem[LCPOnFieldsFact, LCPOnFieldsValu ) } + case PutStaticFieldFact(_, _) => + val valueVar = source.stmt.asPutStatic.value.asVar + + val lcpEOptionP = + propertyStore((source.method, source), LinearConstantPropagationPropertyMetaInformation.key) + + /* Decide based on the current result of the linear constant propagation analysis */ + lcpEOptionP match { + case FinalP(property) => + val value = getVariableFromProperty(valueVar)(property) + FinalEdgeFunction(PutStaticFieldEdgeFunction(value)) + + case InterimUBP(property) => + val value = getVariableFromProperty(valueVar)(property) + value match { + case linear_constant_propagation.problem.UnknownValue => + InterimEdgeFunction(PutStaticFieldEdgeFunction(value), immutable.Set(lcpEOptionP)) + case linear_constant_propagation.problem.ConstantValue(_) => + InterimEdgeFunction(PutStaticFieldEdgeFunction(value), immutable.Set(lcpEOptionP)) + case linear_constant_propagation.problem.VariableValue => + FinalEdgeFunction(PutStaticFieldEdgeFunction(value)) + } + + case _ => + InterimEdgeFunction( + PutStaticFieldEdgeFunction(linear_constant_propagation.problem.UnknownValue), + immutable.Set(lcpEOptionP) + ) + } + case _ => FinalEdgeFunction(identityEdgeFunction) } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala index 128797531a..e6c2beaed0 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala @@ -33,6 +33,15 @@ case class ArrayValue( override def toString: String = s"ArrayValue($initValue, ${elements.mkString(", ")})" } +/** + * Value representing the value of a static field + */ +case class StaticFieldValue( + value: LinearConstantPropagationValue +) extends LCPOnFieldsValue { + override def toString: String = s"StaticFieldValue($value)" +} + /** * Value is variable (not really used currently, mainly for completeness) */ diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala index 935840254b..b633489fe8 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala @@ -2,14 +2,9 @@ package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem import scala.collection.immutable -import scala.collection.mutable import org.opalj.ai.domain.l1.DefaultIntegerRangeValues -import org.opalj.br.Field -import org.opalj.br.analyses.DeclaredFieldsKey -import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties.immutability.FieldImmutability -import org.opalj.br.fpcf.properties.immutability.TransitivelyImmutableField +import org.opalj.br.ObjectType import org.opalj.fpcf.FinalP import org.opalj.fpcf.InterimUBP import org.opalj.fpcf.Property @@ -20,7 +15,6 @@ import org.opalj.ide.problem.InterimEdgeFunction import org.opalj.tac.ArrayLoad import org.opalj.tac.GetField import org.opalj.tac.GetStatic -import org.opalj.tac.PutStatic import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.ConstantValue import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearCombinationEdgeFunction @@ -33,7 +27,6 @@ import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.pro import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.VariableFact import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.VariableValue import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.VariableValueEdgeFunction -import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.V @@ -41,10 +34,7 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.V * Extended definition of the linear constant propagation problem, trying to resolve field accesses with the LCP on * fields analysis. */ -class LinearConstantPropagationProblemExtended( - project: SomeProject, - icfg: JavaICFG -) extends LinearConstantPropagationProblem { +class LinearConstantPropagationProblemExtended extends LinearConstantPropagationProblem { override def isArrayLoadExpressionGeneratedByFact( arrayLoadExpr: ArrayLoad[V] )( @@ -213,91 +203,54 @@ class LinearConstantPropagationProblemExtended( target: JavaStatement, targetFact: LinearConstantPropagationFact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { - val field = - project.get(DeclaredFieldsKey)( - getStaticExpr.declaringClass, - getStaticExpr.name, - getStaticExpr.declaredFieldType - ).definedField + val objectType = getStaticExpr.declaringClass + val fieldName = getStaticExpr.name - /* We enhance the analysis with immutability information. When a static field is immutable and we have knowledge - * of an assignment site, then this will always be the value of the field. This way we can make this analysis - * more precise without the need to add precise handling of static initializers. */ - val fieldImmutabilityEOptionP = propertyStore(field, FieldImmutability.key) + val lcpOnFieldsEOptionP = propertyStore((source.method, source), LCPOnFieldsPropertyMetaInformation.key) - fieldImmutabilityEOptionP match { - case FinalP(fieldImmutability) => - fieldImmutability match { - case TransitivelyImmutableField => - getValueForGetStaticExpr(getStaticExpr, field) match { - case ConstantValue(c) => FinalEdgeFunction(LinearCombinationEdgeFunction(0, c, lattice.top)) - case _ => FinalEdgeFunction(VariableValueEdgeFunction) - } - case _ => FinalEdgeFunction(VariableValueEdgeFunction) - } + /* Decide based on the current result of the LCP on fields analysis */ + lcpOnFieldsEOptionP match { + case FinalP(property) => + FinalEdgeFunction(getStaticFieldFromProperty(objectType, fieldName)(property) match { + case UnknownValue => UnknownValueEdgeFunction + case ConstantValue(c) => LinearCombinationEdgeFunction(0, c, lattice.top) + case VariableValue => VariableValueEdgeFunction + }) - case InterimUBP(fieldImmutability) => - fieldImmutability match { - case TransitivelyImmutableField => - getValueForGetStaticExpr(getStaticExpr, field) match { - case ConstantValue(c) => - InterimEdgeFunction( - LinearCombinationEdgeFunction(0, c, lattice.top), - immutable.Set(fieldImmutabilityEOptionP) - ) - case _ => FinalEdgeFunction(VariableValueEdgeFunction) - } - case _ => FinalEdgeFunction(VariableValueEdgeFunction) + case InterimUBP(property) => + getStaticFieldFromProperty(objectType, fieldName)(property) match { + case UnknownValue => + InterimEdgeFunction(UnknownValueEdgeFunction, immutable.Set(lcpOnFieldsEOptionP)) + case ConstantValue(c) => + InterimEdgeFunction( + LinearCombinationEdgeFunction(0, c, lattice.top), + immutable.Set(lcpOnFieldsEOptionP) + ) + case VariableValue => + FinalEdgeFunction(VariableValueEdgeFunction) } case _ => - InterimEdgeFunction(UnknownValueEdgeFunction, immutable.Set(fieldImmutabilityEOptionP)) + InterimEdgeFunction(UnknownValueEdgeFunction, immutable.Set(lcpOnFieldsEOptionP)) } } - private def getValueForGetStaticExpr(getStaticExpr: GetStatic, field: Field): LinearConstantPropagationValue = { - var value: LinearConstantPropagationValue = UnknownValue - - /* Search for statements that write the field in static initializer of the class belonging to the field. */ - field.classFile.staticInitializer match { - case Some(method) => - var workingStmts: mutable.Set[JavaStatement] = mutable.Set.from(icfg.getStartStatements(method)) - val seenStmts = mutable.Set.empty[JavaStatement] - - while (workingStmts.nonEmpty) { - workingStmts.foreach { stmt => - stmt.stmt.astID match { - case PutStatic.ASTID => - val putStatic = stmt.stmt.asPutStatic - if (getStaticExpr.declaringClass == putStatic.declaringClass && - getStaticExpr.name == putStatic.name - ) { - stmt.stmt.asPutStatic.value.asVar.value match { - case intRange: DefaultIntegerRangeValues#IntegerRange => - if (intRange.lowerBound == intRange.upperBound) { - value = lattice.meet(value, ConstantValue(intRange.upperBound)) - } else { - return VariableValue - } - - case _ => - return VariableValue - } - } - - case _ => - } - } - - seenStmts.addAll(workingStmts) - workingStmts = workingStmts.foldLeft(mutable.Set.empty[JavaStatement]) { (nextStmts, stmt) => - nextStmts.addAll(icfg.getNextStatements(stmt)) - }.diff(seenStmts) - } - - case _ => - } - - value + private def getStaticFieldFromProperty( + objectType: ObjectType, + fieldName: String + )(property: Property): LinearConstantPropagationValue = { + property + .asInstanceOf[LCPOnFieldsPropertyMetaInformation.Self] + .results + .filter { + case (f: AbstractStaticFieldFact, StaticFieldValue(_)) => + f.objectType == objectType && f.fieldName == fieldName + case _ => false + } + .map(_._2) + .foldLeft(UnknownValue: LinearConstantPropagationValue) { + case (value, StaticFieldValue(v)) => + lattice.meet(value, v) + } } } From d5f9ad11bb509487a24828085022bed67deaead5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 4 Dec 2024 10:10:57 +0100 Subject: [PATCH 106/167] Add precomputed flow and summary function for static fields LCP --- .../lcp_on_fields/problem/LCPOnFieldsProblem.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index a12f5b8fdc..f1b624a550 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -633,6 +633,9 @@ class LCPOnFieldsProblem( } else { immutable.Set.empty } + + case _: AbstractStaticFieldFact => + immutable.Set.empty } } } @@ -656,6 +659,9 @@ class LCPOnFieldsProblem( case _: AbstractArrayFact => NewArrayEdgeFunction(linear_constant_propagation.problem.VariableValue) + case _: AbstractStaticFieldFact => + VariableValueEdgeFunction + case _ => identityEdgeFunction } } From 572f67a4fff96ebea331e88b3701e4a480d1b91f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 9 Dec 2024 14:44:21 +0100 Subject: [PATCH 107/167] Adjust analysis logging --- .../src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 2 +- .../main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 871c711875..44d330d6e3 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -290,7 +290,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent */ // TODO (IDE) WHAT HAPPENS WHEN ANALYZING MULTIPLE CALLABLES? CAN WE CACHE E.G. JUMP/SUMMARY FUNCTIONS? def performAnalysis(callable: Callable): ProperPropertyComputationResult = { - logDebug(s"performing ${getClass.getSimpleName} for $callable") + logInfo(s"performing ${getClass.getSimpleName} for $callable") implicit val state: State = new State(callable) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala index 9d3a6070b8..c6e314d1e2 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala @@ -16,6 +16,7 @@ import org.opalj.fpcf.SomeEPS import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEValue +import org.opalj.ide.util.Logging /** * A proxy for IDE analyses that accepts analysis requests for callables as well as statement-callable combinations. @@ -26,13 +27,13 @@ import org.opalj.ide.problem.IDEValue class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( val project: SomeProject, val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] -) extends FPCFAnalysis { +) extends FPCFAnalysis with Logging.ByProjectConfig { /** * @param entity either only a callable or a pair of callable and statement that should be analyzed (if no statement * is given, the result for all exit statements is calculated) */ def proxyAnalysis(entity: Entity): ProperPropertyComputationResult = { - println(s"Proxying request to ${PropertyKey.name(propertyMetaInformation.key)} for $entity") + logInfo(s"Proxying request to ${PropertyKey.name(propertyMetaInformation.key)} for $entity") val (callable, stmt) = entity match { case (c: Entity, s: Entity) => (c.asInstanceOf[Callable], Some(s.asInstanceOf[Statement])) From 8b606be4347f49d45c28806ba8edc0f40f0f8404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 9 Dec 2024 14:46:14 +0100 Subject: [PATCH 108/167] Fix corner cases of linear constant propagation and LCP on fields --- .../StaticFieldImmutableExample.java | 10 +++++++--- .../problem/LCPOnFieldsEdgeFunctions.scala | 17 +++++++++++++---- .../problem/LCPOnFieldsProblem.scala | 9 +++++++-- .../LinearConstantPropagationProblem.scala | 14 ++++++++++++++ 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java index 8735eb14a0..4839af5299 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java @@ -11,6 +11,7 @@ public final class StaticFieldImmutableExample { static int b; private static int c; private static int d; + static final int e = 2; static { b = 23; @@ -27,12 +28,14 @@ public final class StaticFieldImmutableExample { @ConstantValue(variable = "b", value = 23), @ConstantValue(variable = "d", value = 0) }, variableValues = { - @VariableValue(variable = "c") + @VariableValue(variable = "c"), + @VariableValue(variable = "e") }) @ConstantValues({ @ConstantValue(variable = "lv0", value = 42), @ConstantValue(variable = "lv1", value = 23), - @ConstantValue(variable = "lv3", value = 0) + @ConstantValue(variable = "lv3", value = 0), + @ConstantValue(variable = "lv4", value = 2) }) @VariableValue(variable = "lv2") public static void main(String[] args) { @@ -40,7 +43,8 @@ public static void main(String[] args) { int b = StaticFieldImmutableExample.b; int c = StaticFieldImmutableExample.c; int d = StaticFieldImmutableExample.d; + int e = StaticFieldImmutableExample.e; - System.out.println("a: " + a + ", b: " + b + ", c: " + c + ", d: " + d); + System.out.println("a: " + a + ", b: " + b + ", c: " + c + ", d: " + d + ", e: " + e); } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala index 7a172b078f..32d0075e81 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -117,13 +117,22 @@ case class PutFieldEdgeFunction( override def meetWith(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = { otherEdgeFunction match { - case ObjectEdgeFunction(_) => - throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + case ObjectEdgeFunction(values2) => + ObjectEdgeFunction( + (values2 - fieldName) + + (fieldName -> values2.getOrElse( + fieldName, + linear_constant_propagation.problem.VariableValue + )) + ) case PutFieldEdgeFunction(fieldName2, value2) if fieldName == fieldName2 => PutFieldEdgeFunction(fieldName, LinearConstantPropagationLattice.meet(value, value2)) - case PutFieldEdgeFunction(_, _) => - throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + case PutFieldEdgeFunction(fieldName2, _) => + ObjectEdgeFunction(immutable.Map( + fieldName -> linear_constant_propagation.problem.VariableValue, + fieldName2 -> linear_constant_propagation.problem.VariableValue + )) case IdentityEdgeFunction() => this case AllTopEdgeFunction(_) => this diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index f1b624a550..36816c526b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -166,8 +166,13 @@ class LCPOnFieldsProblem( } value match { - case linear_constant_propagation.problem.UnknownValue => linear_constant_propagation.problem.ConstantValue(0) - case _ => value + case linear_constant_propagation.problem.UnknownValue => + if (field.isFinal) { + linear_constant_propagation.problem.VariableValue + } else { + linear_constant_propagation.problem.ConstantValue(0) + } + case _ => value } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 0fc5116040..82d12bc6f1 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -427,6 +427,14 @@ class LinearConstantPropagationProblem case (None, Some(r), BinaryArithmeticOperators.Multiply) => LinearCombinationEdgeFunction(r, 0) + case (Some(l), Some(r), BinaryArithmeticOperators.Divide) => + LinearCombinationEdgeFunction(0, l / r) + case (_, _, BinaryArithmeticOperators.Divide) => + VariableValueEdgeFunction + + case (_, _, BinaryArithmeticOperators.Modulo) => + VariableValueEdgeFunction + case (None, None, _) => VariableValueEdgeFunction @@ -436,6 +444,12 @@ class LinearConstantPropagationProblem VariableValueEdgeFunction case (_, _, BinaryArithmeticOperators.XOr) => VariableValueEdgeFunction + case (_, _, BinaryArithmeticOperators.ShiftLeft) => + VariableValueEdgeFunction + case (_, _, BinaryArithmeticOperators.ShiftRight) => + VariableValueEdgeFunction + case (_, _, BinaryArithmeticOperators.UnsignedShiftRight) => + VariableValueEdgeFunction case (_, _, op) => throw new UnsupportedOperationException(s"Operator $op is not implemented!") From cd390ef81fafe6f22de036473a747acc2b384d26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 3 Jan 2025 09:58:42 +0100 Subject: [PATCH 109/167] Fix corner cases of linear constant propagation and LCP on fields --- .../problem/LCPOnFieldsEdgeFunctions.scala | 77 +++++++++++++++---- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala index 32d0075e81..e7c97afc40 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -55,8 +55,14 @@ case class ObjectEdgeFunction( .toMap ) - case PutFieldEdgeFunction(_, _) => - throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + case PutFieldEdgeFunction(fieldName, value) => + ObjectEdgeFunction( + (values - fieldName) + + (fieldName -> LinearConstantPropagationLattice.meet( + value, + values.getOrElse(fieldName, linear_constant_propagation.problem.VariableValue) + )) + ) case IdentityEdgeFunction() => this case AllTopEdgeFunction(_) => this @@ -98,7 +104,8 @@ case class PutFieldEdgeFunction( override def composeWith(secondEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = { secondEdgeFunction match { - case ObjectEdgeFunction(values) if values.contains(fieldName) => secondEdgeFunction + case ObjectEdgeFunction(values) if values.contains(fieldName) => + ObjectEdgeFunction((values - fieldName) + (fieldName -> value)) case ObjectEdgeFunction(values) => ObjectEdgeFunction(values + (fieldName -> value)) @@ -120,9 +127,12 @@ case class PutFieldEdgeFunction( case ObjectEdgeFunction(values2) => ObjectEdgeFunction( (values2 - fieldName) + - (fieldName -> values2.getOrElse( - fieldName, - linear_constant_propagation.problem.VariableValue + (fieldName -> LinearConstantPropagationLattice.meet( + value, + values2.getOrElse( + fieldName, + linear_constant_propagation.problem.VariableValue + ) )) ) @@ -175,8 +185,8 @@ class ArrayEdgeFunction( secondEdgeFunction match { case NewArrayEdgeFunction(_) => secondEdgeFunction - case ArrayEdgeFunction(_, _) => - throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") + case ArrayEdgeFunction(initValue2, elements2) => + new ArrayEdgeFunction(initValue2, (elements -- elements2.keys) ++ elements2) case PutElementEdgeFunction(index, value) => index match { @@ -213,8 +223,21 @@ class ArrayEdgeFunction( .toMap ) - case PutElementEdgeFunction(_, _) => - throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + case PutElementEdgeFunction(index, value) => + index match { + case linear_constant_propagation.problem.UnknownValue => + UnknownValueEdgeFunction + case linear_constant_propagation.problem.ConstantValue(i) => + new ArrayEdgeFunction( + initValue, + (elements - i) + (i -> LinearConstantPropagationLattice.meet( + value, + elements.getOrElse(i, initValue) + )) + ) + case linear_constant_propagation.problem.VariableValue => + new ArrayEdgeFunction(linear_constant_propagation.problem.VariableValue, immutable.Map.empty) + } case IdentityEdgeFunction() => this case AllTopEdgeFunction(_) => this @@ -276,8 +299,21 @@ case class PutElementEdgeFunction( secondEdgeFunction match { case NewArrayEdgeFunction(_) => secondEdgeFunction - case ArrayEdgeFunction(_, _) => - throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") + case ArrayEdgeFunction(initValue, elements) => + index match { + case linear_constant_propagation.problem.UnknownValue => + UnknownValueEdgeFunction + case linear_constant_propagation.problem.ConstantValue(i) => + new ArrayEdgeFunction( + initValue, + (elements - i) + (i -> LinearConstantPropagationLattice.meet( + value, + elements.getOrElse(i, initValue) + )) + ) + case linear_constant_propagation.problem.VariableValue => + new ArrayEdgeFunction(linear_constant_propagation.problem.VariableValue, immutable.Map.empty) + } case PutElementEdgeFunction(index2, _) if index == index2 => secondEdgeFunction case PutElementEdgeFunction(_, _) => @@ -295,8 +331,21 @@ case class PutElementEdgeFunction( override def meetWith(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = { otherEdgeFunction match { - case ArrayEdgeFunction(_, _) => - throw new UnsupportedOperationException(s"Meeting $this with $otherEdgeFunction is not implemented!") + case ArrayEdgeFunction(initValue, elements) => + index match { + case linear_constant_propagation.problem.UnknownValue => + UnknownValueEdgeFunction + case linear_constant_propagation.problem.ConstantValue(i) => + new ArrayEdgeFunction( + initValue, + (elements - i) + (i -> LinearConstantPropagationLattice.meet( + value, + elements.getOrElse(i, initValue) + )) + ) + case linear_constant_propagation.problem.VariableValue => + new ArrayEdgeFunction(linear_constant_propagation.problem.VariableValue, immutable.Map.empty) + } case PutElementEdgeFunction(index2, value2) => PutElementEdgeFunction( From 3f09a5b6feee0994d215a0f97d10916e826156e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 3 Jan 2025 10:01:04 +0100 Subject: [PATCH 110/167] Fix analysis of some more empty callables --- .../lcp_on_fields/problem/LCPOnFieldsProblem.scala | 6 +++--- .../problem/LinearConstantPropagationProblem.scala | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 36816c526b..8beb59920f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -603,7 +603,7 @@ class LCPOnFieldsProblem( callSiteFact: LCPOnFieldsFact, callee: Method )(implicit propertyStore: PropertyStore): Boolean = { - if (callee.isNative) { + if (callee.isNative || callee.body.isEmpty) { return true } @@ -616,7 +616,7 @@ class LCPOnFieldsProblem( callee: Method, returnSite: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { - if (callee.isNative) { + if (callee.isNative || callee.body.isEmpty) { return new FlowFunction[LCPOnFieldsFact] { override def compute(): FactsAndDependees = { val callStmt = callSite.stmt.asCall() @@ -656,7 +656,7 @@ class LCPOnFieldsProblem( returnSite: JavaStatement, returnSiteFact: LCPOnFieldsFact )(implicit propertyStore: PropertyStore): EdgeFunction[LCPOnFieldsValue] = { - if (callee.isNative) { + if (callee.isNative || callee.body.isEmpty) { return returnSiteFact match { case _: AbstractObjectFact => VariableValueEdgeFunction diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 82d12bc6f1..90d2ae5fdf 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -529,7 +529,7 @@ class LinearConstantPropagationProblem callSiteFact: LinearConstantPropagationFact, callee: Method )(implicit propertyStore: PropertyStore): Boolean = { - if (callee.isNative) { + if (callee.isNative || callee.body.isEmpty) { return true } @@ -542,7 +542,7 @@ class LinearConstantPropagationProblem callee: Method, returnSite: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { - if (callee.isNative) { + if (callee.isNative || callee.body.isEmpty) { return emptyFlowFunction } @@ -556,7 +556,7 @@ class LinearConstantPropagationProblem returnSite: JavaStatement, returnSiteFact: LinearConstantPropagationFact )(implicit propertyStore: PropertyStore): EdgeFunction[LinearConstantPropagationValue] = { - if (callee.isNative) { + if (callee.isNative || callee.body.isEmpty) { return identityEdgeFunction } From 4b64423944496f6988d7aef59bdce1f997267753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 3 Jan 2025 10:07:22 +0100 Subject: [PATCH 111/167] Fix return of special exceptions --- .../instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 8beb59920f..9215c0a7bd 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -5,6 +5,7 @@ import scala.collection.immutable import scala.collection.mutable import org.opalj.ai.domain.l1.DefaultIntegerRangeValues +import org.opalj.ai.isImplicitOrExternalException import org.opalj.br.Field import org.opalj.br.IntegerType import org.opalj.br.Method @@ -355,6 +356,10 @@ class LCPOnFieldsProblem( case f: AbstractEntityFact => val definedAtIndex = f.definedAtIndex + if (isImplicitOrExternalException(definedAtIndex)) { + return immutable.Set.empty + } + val callStmt = returnSite.stmt.asCall() val allParams = callStmt.allParams From 3206fa8ebadd745a1822e587775a4a4c8c2d52a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 3 Jan 2025 10:08:39 +0100 Subject: [PATCH 112/167] Fix processing of static callables --- .../problem/LCPOnFieldsProblem.scala | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 9215c0a7bd..34dd2baf0f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -312,6 +312,9 @@ class LCPOnFieldsProblem( /* All parameters (including the implicit 'this' reference) */ val allParams = callStmt.allParams + val staticCallIndexOffset = + if (callStmt.receiverOption.isEmpty) { 1 } + else { 0 } allParams .zipWithIndex @@ -321,9 +324,12 @@ class LCPOnFieldsProblem( param.asVar.definedBy.contains(f.definedAtIndex) } .map { case (_, index) => + val adjustedIndex = index + staticCallIndexOffset f match { - case _: AbstractObjectFact => ObjectFact(s"param$index", -(index + 1)) - case _: AbstractArrayFact => ArrayFact(s"param$index", -(index + 1)) + case _: AbstractObjectFact => + ObjectFact(s"param$adjustedIndex", -(adjustedIndex + 1)) + case _: AbstractArrayFact => + ArrayFact(s"param$adjustedIndex", -(adjustedIndex + 1)) } } .toSet @@ -363,10 +369,13 @@ class LCPOnFieldsProblem( val callStmt = returnSite.stmt.asCall() val allParams = callStmt.allParams + val staticCallIndexOffset = + if (callStmt.receiverOption.isEmpty) { 1 } + else { 0 } /* Distinguish parameters and local variables */ if (definedAtIndex < 0) { - val paramIndex = -(definedAtIndex + 1) + val paramIndex = -definedAtIndex - 1 - staticCallIndexOffset val param = allParams(paramIndex) val paramName = param.asVar.name.substring(1, param.asVar.name.length - 1) param.asVar.definedBy.map { dAI => From bdfc72cf653fb2794bd29951bc63dfe200c44f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 3 Jan 2025 14:07:50 +0100 Subject: [PATCH 113/167] Fix logging issues --- OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 3 ++- .../src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 44d330d6e3..0b463712ee 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -11,6 +11,7 @@ import org.opalj.fpcf.InterimEUBP import org.opalj.fpcf.InterimPartialResult import org.opalj.fpcf.PartialResult import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.Result import org.opalj.fpcf.Results import org.opalj.fpcf.SomeEOptionP @@ -290,7 +291,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent */ // TODO (IDE) WHAT HAPPENS WHEN ANALYZING MULTIPLE CALLABLES? CAN WE CACHE E.G. JUMP/SUMMARY FUNCTIONS? def performAnalysis(callable: Callable): ProperPropertyComputationResult = { - logInfo(s"performing ${getClass.getSimpleName} for $callable") + logInfo(s"performing ${PropertyKey.name(propertyMetaInformation.key)} for $callable") implicit val state: State = new State(callable) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala index c6e314d1e2..376682efe8 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala @@ -33,7 +33,7 @@ class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statement, Callable < * is given, the result for all exit statements is calculated) */ def proxyAnalysis(entity: Entity): ProperPropertyComputationResult = { - logInfo(s"Proxying request to ${PropertyKey.name(propertyMetaInformation.key)} for $entity") + logInfo(s"proxying request to ${PropertyKey.name(propertyMetaInformation.key)} for $entity") val (callable, stmt) = entity match { case (c: Entity, s: Entity) => (c.asInstanceOf[Callable], Some(s.asInstanceOf[Statement])) From 266743f1a7a2864a447f3c64e18485396a2887d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 6 Jan 2025 09:33:34 +0100 Subject: [PATCH 114/167] Optimize jump functions lookup --- .../org/opalj/ide/solver/IDEAnalysis.scala | 51 ++++++++++++++++--- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 0b463712ee..e4390e7cfc 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -86,6 +86,10 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent * The jump functions (incrementally calculated) in P1 */ private val jumpFunctions: JumpFunctions = mutable.Map.empty + /** + * Index-like structure for faster access of jump functions map + */ + private val jumpFunctionSFTFByST = mutable.Map.empty[(Statement, Statement), mutable.Set[(Fact, Fact)]] /** * The summary functions (incrementally calculated) in P1 @@ -137,6 +141,11 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent def setJumpFunction(path: Path, jumpFunction: JumpFunction): Unit = { jumpFunctions.put(path, jumpFunction) + + val ((source, sourceFact), (target, targetFact)) = path + jumpFunctionSFTFByST + .getOrElseUpdate((source, target), { mutable.Set.empty }) + .add((sourceFact, targetFact)) } def getJumpFunction(path: Path): JumpFunction = { @@ -149,13 +158,41 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent target: Option[Statement] = None, targetFact: Option[Fact] = None ): collection.Map[Path, JumpFunction] = { - // TODO (IDE) THIS COULD BE OPTIMIZED TO SPEEDUP THE ANALYSIS - jumpFunctions.filter { - case (((s, sf), (t, tf)), _) => - source.forall { source => s == source } && - sourceFact.forall { sourceFact => sf == sourceFact } && - target.forall { target => t == target } && - targetFact.forall { targetFact => tf == targetFact } + ((source, sourceFact), (target, targetFact)) match { + case ((Some(s), None), (Some(t), None)) => + jumpFunctionSFTFByST.getOrElse((s, t), immutable.Set.empty[(Fact, Fact)]) + .map { case (sF, tF) => + val path = ((s, sF), (t, tF)) + path -> jumpFunctions(path) + } + .toMap + + case ((Some(s), None), (Some(t), Some(tF))) => + jumpFunctionSFTFByST.getOrElse((s, t), immutable.Set.empty[(Fact, Fact)]) + .filter { case (_, tF2) => tF2 == tF } + .map { case (sF, _) => + val path = ((s, sF), (t, tF)) + path -> jumpFunctions(path) + } + .toMap + + case ((Some(s), Some(sF)), (Some(t), None)) => + jumpFunctionSFTFByST.getOrElse((s, t), immutable.Set.empty[(Fact, Fact)]) + .filter { case (sF2, _) => sF2 == sF } + .map { case (_, tF) => + val path = ((s, sF), (t, tF)) + path -> jumpFunctions(path) + } + .toMap + + case _ => + jumpFunctions.filter { + case (((s, sf), (t, tf)), _) => + source.forall { source => s == source } && + sourceFact.forall { sourceFact => sf == sourceFact } && + target.forall { target => t == target } && + targetFact.forall { targetFact => tf == targetFact } + } } } From 932d52f314269006c5394176dc351f44d8b712a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 6 Jan 2025 09:34:43 +0100 Subject: [PATCH 115/167] Fix pattern matching exception in linear constant propagation --- .../problem/LinearConstantPropagationProblemExtended.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala index b633489fe8..bffa884a26 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala @@ -72,6 +72,8 @@ class LinearConstantPropagationProblemExtended extends LinearConstantPropagation } else { None } + case _ => + None } val lcpOnFieldsEOptionP = From 9ccdcb6ecd269b42b649700e7aa54fbd307893a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 8 Jan 2025 13:15:46 +0100 Subject: [PATCH 116/167] Fix termination issues due to faulty problem definition --- .../problem/LinearConstantPropagationEdgeFunctions.scala | 4 ---- 1 file changed, 4 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala index 7b76a6147a..ebe75f8089 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala @@ -133,10 +133,6 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LinearConstantPropaga } } - override def meetWith( - otherEdgeFunction: EdgeFunction[LinearConstantPropagationValue] - ): EdgeFunction[LinearConstantPropagationValue] = this - override def equalTo(otherEdgeFunction: EdgeFunction[LinearConstantPropagationValue]): Boolean = otherEdgeFunction eq this From 84d1e639443d0b7c0e22e747daf17be548a2124d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 8 Jan 2025 14:24:37 +0100 Subject: [PATCH 117/167] Increase performance impact of raw results --- .../IFDSPropertyMetaInformation.scala | 3 +- .../BaseIDEAnalysisProxyScheduler.scala | 2 +- .../EagerIDEAnalysisProxyScheduler.scala | 2 +- .../FlowRecordingAnalysisScheduler.scala | 2 +- .../integration/IDEAnalysisScheduler.scala | 2 +- .../IDEPropertyMetaInformation.scala | 6 +- .../ide/integration/IDERawProperty.scala | 27 +++-- .../IDERawPropertyMetaInformation.scala | 10 +- .../LazyIDEAnalysisProxyScheduler.scala | 2 +- .../org/opalj/ide/solver/IDEAnalysis.scala | 50 ++------- .../opalj/ide/solver/IDEAnalysisProxy.scala | 101 +++++++----------- .../JavaIFDSAnalysisScheduler.scala | 3 +- .../JavaIFDSPropertyMetaInformation.scala | 11 ++ .../LCPOnFieldsAnalysisScheduler.scala | 4 +- .../LCPOnFieldsPropertyMetaInformation.scala | 4 +- ...PropagationAnalysisSchedulerExtended.scala | 4 +- ...ConstantPropagationAnalysisScheduler.scala | 4 +- ...ntPropagationPropertyMetaInformation.scala | 4 +- .../JavaIDEPropertyMetaInformation.scala | 13 +++ 19 files changed, 117 insertions(+), 137 deletions(-) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala index 04fb3a1b71..df5574f3f0 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala @@ -8,4 +8,5 @@ import org.opalj.ide.problem.IDEFact /** * Interface for property meta information for IFDS problems based on an IDE problem */ -trait IFDSPropertyMetaInformation[Fact <: IDEFact] extends IDEPropertyMetaInformation[Fact, IFDSValue] +trait IFDSPropertyMetaInformation[Statement, Fact <: IDEFact] + extends IDEPropertyMetaInformation[Statement, Fact, IFDSValue] diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala index 5c02035a6a..1e108acb0a 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala @@ -20,7 +20,7 @@ import org.opalj.ide.solver.IDEAnalysisProxy */ trait BaseIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity] extends FPCFAnalysisScheduler { - val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] + val propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] override type InitializationData = IDEAnalysisProxy[Fact, Value, Statement, Callable] diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala index 5bdf84c932..3ac42a7a61 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala @@ -17,7 +17,7 @@ import org.opalj.ide.solver.IDEAnalysisProxy * @param methodProvider for which methods the results should be computed eagerly */ class EagerIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( - val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value], + val propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value], methodProvider: SomeProject => Iterable[Method] = { project => project.allMethodsWithBody } ) extends BaseIDEAnalysisProxyScheduler[Fact, Value, Statement, Callable] with FPCFEagerAnalysisScheduler { def this(ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable, ?]) = { diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala index 8ae93260dc..f0cf01fa28 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala @@ -51,7 +51,7 @@ class FlowRecordingAnalysisScheduler[ recordEdgeFunctions: Boolean = true ) extends IDEAnalysisScheduler[Fact, Value, Statement, Callable, _ICFG] with Logging.EnableAll with Logging.GlobalLogContext { - override def propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] = { + override def propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] = { ideAnalysisScheduler.propertyMetaInformation } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala index 69ad80d5b0..2ce96cfda6 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala @@ -29,7 +29,7 @@ abstract class IDEAnalysisScheduler[ ] extends FPCFLazyAnalysisScheduler { override final type InitializationData = IDEAnalysis[Fact, Value, Statement, Callable] - def propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] + def propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] def createProblem(project: SomeProject, icfg: _ICFG): IDEProblem[Fact, Value, Statement, Callable] diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala index 05686b29de..77b35fcd5c 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala @@ -8,14 +8,14 @@ import org.opalj.ide.problem.IDEValue /** * Base interface of property meta information of IDE analyses. Creates [[BasicIDEProperty]] by default. */ -trait IDEPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue] extends PropertyMetaInformation { +trait IDEPropertyMetaInformation[Statement, Fact <: IDEFact, Value <: IDEValue] extends PropertyMetaInformation { override type Self = BasicIDEProperty[Fact, Value] /** * A property meta information corresponding to this one but used for the actual/underlying IDE analysis */ - private[ide] val backingPropertyMetaInformation: IDERawPropertyMetaInformation[Fact, Value] = - new IDERawPropertyMetaInformation[Fact, Value](this) + private[ide] val backingPropertyMetaInformation: IDERawPropertyMetaInformation[Statement, Fact, Value] = + new IDERawPropertyMetaInformation[Statement, Fact, Value](this) /** * Create a property diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala index e1f07a508c..9e3f5be085 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala @@ -9,29 +9,38 @@ import org.opalj.ide.problem.IDEValue /** * Class representing a property that is directly created by an IDE analysis. * @param key the property key (very likely taken from an [[IDERawPropertyMetaInformation]] instance) - * @param results the raw results produced by the analysis + * @param stmtResults the raw statement results produced by the analysis + * @param callableResults the raw callable results produced by the analysis */ -class IDERawProperty[Fact <: IDEFact, Value <: IDEValue]( - val key: PropertyKey[IDERawProperty[Fact, Value]], - val results: collection.Set[(Fact, Value)] +class IDERawProperty[Statement, Fact <: IDEFact, Value <: IDEValue]( + val key: PropertyKey[IDERawProperty[Statement, Fact, Value]], + val stmtResults: collection.Map[Statement, collection.Set[(Fact, Value)]], + val callableResults: collection.Set[(Fact, Value)] ) extends Property { - override type Self = IDERawProperty[Fact, Value] + override type Self = IDERawProperty[Statement, Fact, Value] override def toString: String = { s"IDERawProperty(${PropertyKey.name(key)}, {\n${ - results.map { case (fact, value) => s"\t($fact,$value)" }.mkString("\n") + stmtResults.map { case (stmt, results) => + s"\t$stmt\n${ + results.map { case (fact, value) => s"\t\t($fact,$value)" }.mkString("\n") + }" + }.mkString("\n") + }\n}, {\n${ + callableResults.map { case (fact, value) => s"\t($fact,$value)" }.mkString("\n") }\n})" } override def equals(other: Any): Boolean = { other match { - case ideRawProperty: IDERawProperty[?, ?] => - key == ideRawProperty.key && results == ideRawProperty.results + case ideRawProperty: IDERawProperty[?, ?, ?] => + key == ideRawProperty.key && stmtResults == ideRawProperty.stmtResults && + callableResults == ideRawProperty.callableResults case _ => false } } override def hashCode(): Int = { - key.hashCode() * 31 + results.hashCode() + (key.hashCode() * 31 + stmtResults.hashCode()) * 31 + callableResults.hashCode() } } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala index 0715693fad..8e9c380d45 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala @@ -11,17 +11,17 @@ import org.opalj.ide.problem.IDEValue * The property type is fixed to [[IDERawProperty]]. * @param propertyMetaInformation the property meta information this object should be backing */ -final class IDERawPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue]( - propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] +final class IDERawPropertyMetaInformation[Statement, Fact <: IDEFact, Value <: IDEValue]( + propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] ) extends PropertyMetaInformation { - override type Self = IDERawProperty[Fact, Value] + override type Self = IDERawProperty[Statement, Fact, Value] /** * The used property key, based on [[propertyMetaInformation]] */ - private lazy val backingPropertyKey: PropertyKey[IDERawProperty[Fact, Value]] = { + private lazy val backingPropertyKey: PropertyKey[IDERawProperty[Statement, Fact, Value]] = { PropertyKey.create(s"${PropertyKey.name(propertyMetaInformation.key)}_Raw") } - override def key: PropertyKey[IDERawProperty[Fact, Value]] = backingPropertyKey + override def key: PropertyKey[IDERawProperty[Statement, Fact, Value]] = backingPropertyKey } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala index 258982fe64..69b564272f 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala @@ -15,7 +15,7 @@ import org.opalj.ide.solver.IDEAnalysisProxy * A scheduler to (lazily) schedule the proxy analysis that is used to access the IDE analysis results */ class LazyIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( - val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] + val propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] ) extends BaseIDEAnalysisProxyScheduler[Fact, Value, Statement, Callable] with FPCFLazyAnalysisScheduler { def this(ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable, ?]) = { this(ideAnalysisScheduler.propertyMetaInformation) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index e4390e7cfc..46ccfa80e8 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -7,17 +7,13 @@ import scala.collection.mutable import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.fpcf.Entity -import org.opalj.fpcf.InterimEUBP -import org.opalj.fpcf.InterimPartialResult -import org.opalj.fpcf.PartialResult +import org.opalj.fpcf.InterimResult import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.Result -import org.opalj.fpcf.Results import org.opalj.fpcf.SomeEOptionP import org.opalj.fpcf.SomeEPK import org.opalj.fpcf.SomeEPS -import org.opalj.fpcf.SomePartialResult import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.ide.integration.IDERawProperty import org.opalj.ide.problem.AllTopEdgeFunction @@ -40,7 +36,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val project: SomeProject, val problem: IDEProblem[Fact, Value, Statement, Callable], val icfg: ICFG[Statement, Callable], - val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] + val propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] ) extends FPCFAnalysis with Logging.ByProjectConfig { private type Node = (Statement, Fact) /** @@ -400,34 +396,20 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val callable = s.targetCallable val (resultsByStatement, resultsForExit) = s.collectResults(callable) - val propertiesByStatement = resultsByStatement.map { case (stmt, results) => - (stmt, new IDERawProperty(propertyMetaInformation.backingPropertyMetaInformation.key, results)) - } - val propertyForExit = - new IDERawProperty(propertyMetaInformation.backingPropertyMetaInformation.key, resultsForExit) + val ideRawProperty = new IDERawProperty( + propertyMetaInformation.backingPropertyMetaInformation.key, + resultsByStatement, + resultsForExit + ) logDebug("finished creation of properties") if (s.areDependeesEmpty) { logDebug("creating final results") - Results( - propertiesByStatement.map { case (stmt, property) => - Result((callable, stmt), property) - } ++ Iterable( - Result(callable, propertyForExit) - ) - ) + Result(callable, ideRawProperty) } else { logDebug("creating interim results") - InterimPartialResult( - propertiesByStatement.map { - case (stmt, property) => createPartialResult((callable, stmt), property) - } ++ Iterable( - createPartialResult(callable, propertyForExit) - ), - s.getDependees.toSet, - onDependeeUpdateContinuation - ) + InterimResult.forUB(callable, ideRawProperty, s.getDependees.toSet, onDependeeUpdateContinuation) } } @@ -447,20 +429,6 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent createResult() } - private def createPartialResult(entity: Entity, property: IDERawProperty[Fact, Value]): SomePartialResult = { - PartialResult( - entity, - propertyMetaInformation.backingPropertyMetaInformation.key, - { (eOptionP: SomeEOptionP) => - if (eOptionP.hasUBP && eOptionP.ub == property) { - None - } else { - Some(InterimEUBP(entity, property)) - } - } - ) - } - private def seedPhase1()(implicit s: State): Unit = { val callable = s.targetCallable diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala index 376682efe8..26fc111897 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala @@ -6,7 +6,6 @@ import scala.collection.immutable import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.fpcf.Entity -import org.opalj.fpcf.EPK import org.opalj.fpcf.InterimPartialResult import org.opalj.fpcf.InterimResult import org.opalj.fpcf.ProperPropertyComputationResult @@ -26,7 +25,7 @@ import org.opalj.ide.util.Logging */ class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( val project: SomeProject, - val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value] + val propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] ) extends FPCFAnalysis with Logging.ByProjectConfig { /** * @param entity either only a callable or a pair of callable and statement that should be analyzed (if no statement @@ -35,82 +34,62 @@ class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statement, Callable < def proxyAnalysis(entity: Entity): ProperPropertyComputationResult = { logInfo(s"proxying request to ${PropertyKey.name(propertyMetaInformation.key)} for $entity") - val (callable, stmt) = entity match { + val (callable, stmtOption) = entity match { case (c: Entity, s: Entity) => (c.asInstanceOf[Callable], Some(s.asInstanceOf[Statement])) case c => (c.asInstanceOf[Callable], None) } - createCoarseResult(callable, stmt) + createResult(callable, stmtOption) } - private def createCoarseResult(callable: Callable, stmt: Option[Statement]): ProperPropertyComputationResult = { - val eOptionP = propertyStore(callable, propertyMetaInformation.backingPropertyMetaInformation.key) - eOptionP match { - case _: EPK[Callable, ?] => - // In this case, the analysis has not been called yet - InterimPartialResult( - immutable.Set(eOptionP), - onDependeeUpdateContinuationCoarse(callable, stmt) - ) - case _ => - if (propertyStore.hasProperty( - stmt match { - case Some(statement) => (callable, statement) - case None => callable - }, - propertyMetaInformation.backingPropertyMetaInformation.key - ) - ) { - // In this case, some kind of result is present (for the callable, as well as for each statement) - createFineResult(callable, stmt) - } else { - // Otherwise, the algorithm did not reach the statement (yet) - InterimPartialResult( - immutable.Set(eOptionP), - onDependeeUpdateContinuationCoarse(callable, stmt) - ) - } - } - } - - private def onDependeeUpdateContinuationCoarse( - callable: Callable, - stmt: Option[Statement] - )(eps: SomeEPS): ProperPropertyComputationResult = { - createCoarseResult(callable, stmt) - } - - private def createFineResult(callable: Callable, stmt: Option[Statement]): ProperPropertyComputationResult = { - val eOptionP = propertyStore( - stmt match { - case Some(statement) => (callable, statement) - case None => callable - }, + private def createResult(callable: Callable, stmtOption: Option[Statement]): ProperPropertyComputationResult = { + val backingEOptionP = propertyStore( + callable, propertyMetaInformation.backingPropertyMetaInformation.key ) - if (eOptionP.isEPK) { + + val entity = stmtOption match { + case Some(statement) => (callable, statement) + case None => callable + } + + if (backingEOptionP.isEPK) { + // In this case, the analysis has not been called yet InterimPartialResult( - immutable.Set(eOptionP), - onDependeeUpdateContinuationFine(callable, stmt) + immutable.Set(backingEOptionP), + onDependeeUpdateContinuation(callable, stmtOption) + ) + } else if (backingEOptionP.isFinal) { + Result( + entity, + propertyMetaInformation.createProperty( + stmtOption match { + case Some(statement) => backingEOptionP.ub.stmtResults.getOrElse(statement, immutable.Set.empty) + case None => backingEOptionP.ub.callableResults + } + ) ) - } else if (eOptionP.isFinal) { - Result(eOptionP.e, propertyMetaInformation.createProperty(eOptionP.ub.results)) - } else if (eOptionP.hasUBP) { + } else if (backingEOptionP.hasUBP) { InterimResult.forUB( - eOptionP.e, - propertyMetaInformation.createProperty(eOptionP.ub.results), - immutable.Set(eOptionP), - onDependeeUpdateContinuationFine(callable, stmt) + entity, + propertyMetaInformation.createProperty( + stmtOption match { + case Some(statement) => backingEOptionP.ub.stmtResults.getOrElse(statement, immutable.Set.empty) + case None => backingEOptionP.ub.callableResults + } + ), + immutable.Set(backingEOptionP), + onDependeeUpdateContinuation(callable, stmtOption) ) } else { - throw new IllegalStateException(s"Expected a final or interim EPS but got $eOptionP!") + throw new IllegalStateException(s"Expected a final or interim EPS but got $backingEOptionP!") } } - private def onDependeeUpdateContinuationFine( + private def onDependeeUpdateContinuation( callable: Callable, - stmt: Option[Statement] + stmtOption: Option[Statement] )(eps: SomeEPS): ProperPropertyComputationResult = { - createFineResult(callable, stmt) + createResult(callable, stmtOption) } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala index 877dac4d26..f99baa6a91 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala @@ -2,7 +2,6 @@ package org.opalj.tac.fpcf.analyses.ide.ifds.integration import org.opalj.br.analyses.SomeProject -import org.opalj.ide.ifds.integration.IFDSPropertyMetaInformation import org.opalj.ide.ifds.problem.IFDSValue import org.opalj.ide.problem.IDEFact import org.opalj.tac.fpcf.analyses.ide.ifds.problem.JavaIFDSProblem @@ -13,7 +12,7 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG * Specialized IDE analysis scheduler for IFDS problems with Java programs */ abstract class JavaIFDSAnalysisScheduler[Fact <: IDEFact] extends JavaIDEAnalysisSchedulerBase[Fact, IFDSValue] { - override def propertyMetaInformation: IFDSPropertyMetaInformation[Fact] + override def propertyMetaInformation: JavaIFDSPropertyMetaInformation[Fact] override def createProblem(project: SomeProject, icfg: JavaICFG): JavaIFDSProblem[Fact] } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala new file mode 100644 index 0000000000..f762500752 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala @@ -0,0 +1,11 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.ifds.integration + +import org.opalj.ide.ifds.integration.IFDSPropertyMetaInformation +import org.opalj.ide.problem.IDEFact +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement + +/** + * Specialized property meta information for IFDS problems with Java programs + */ +trait JavaIFDSPropertyMetaInformation[Fact <: IDEFact] extends IFDSPropertyMetaInformation[JavaStatement, Fact] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala index e0fd38c295..56ea5d362a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -8,13 +8,13 @@ import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.immutability.FieldImmutability import org.opalj.fpcf.PropertyBounds -import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsFact import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsProblem import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsValue import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG @@ -24,7 +24,7 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG */ abstract class LCPOnFieldsAnalysisScheduler extends JavaIDEAnalysisScheduler[LCPOnFieldsFact, LCPOnFieldsValue] with JavaIDEAnalysisSchedulerBase.ForwardICFG { - override def propertyMetaInformation: IDEPropertyMetaInformation[LCPOnFieldsFact, LCPOnFieldsValue] = + override def propertyMetaInformation: JavaIDEPropertyMetaInformation[LCPOnFieldsFact, LCPOnFieldsValue] = LCPOnFieldsPropertyMetaInformation override def createProblem(project: SomeProject, icfg: JavaICFG): JavaIDEProblem[ diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala index ca805983cf..6fef15e7f4 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala @@ -2,14 +2,14 @@ package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields import org.opalj.fpcf.PropertyKey -import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsFact import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsValue +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEPropertyMetaInformation /** * Meta information for linear constant propagation on fields */ object LCPOnFieldsPropertyMetaInformation - extends IDEPropertyMetaInformation[LCPOnFieldsFact, LCPOnFieldsValue] { + extends JavaIDEPropertyMetaInformation[LCPOnFieldsFact, LCPOnFieldsValue] { final val key: PropertyKey[Self] = PropertyKey.create("opalj.ide.LinearConstantPropagationOnFields") } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala index 52500db56d..3616162a79 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala @@ -5,13 +5,13 @@ import scala.collection.immutable import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.PropertyBounds -import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LinearConstantPropagationProblemExtended import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG @@ -21,7 +21,7 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG abstract class LinearConstantPropagationAnalysisSchedulerExtended extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] with JavaIDEAnalysisSchedulerBase.ForwardICFG { - override def propertyMetaInformation: IDEPropertyMetaInformation[ + override def propertyMetaInformation: JavaIDEPropertyMetaInformation[ LinearConstantPropagationFact, LinearConstantPropagationValue ] = LinearConstantPropagationPropertyMetaInformation diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala index 33c55aa82d..3c5408a43b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala @@ -2,12 +2,12 @@ package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation import org.opalj.br.analyses.SomeProject -import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationProblem import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisScheduler import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG @@ -17,7 +17,7 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG abstract class LinearConstantPropagationAnalysisScheduler extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] with JavaIDEAnalysisSchedulerBase.ForwardICFG { - override def propertyMetaInformation: IDEPropertyMetaInformation[ + override def propertyMetaInformation: JavaIDEPropertyMetaInformation[ LinearConstantPropagationFact, LinearConstantPropagationValue ] = LinearConstantPropagationPropertyMetaInformation diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala index 49e9973700..b875ceeabb 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala @@ -2,14 +2,14 @@ package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation import org.opalj.fpcf.PropertyKey -import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue +import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEPropertyMetaInformation /** * Meta information for linear constant propagation */ object LinearConstantPropagationPropertyMetaInformation - extends IDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] { + extends JavaIDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] { final val key: PropertyKey[Self] = PropertyKey.create("opalj.ide.LinearConstantPropagation") } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala new file mode 100644 index 0000000000..9b6cd01791 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala @@ -0,0 +1,13 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ide.integration + +import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.ide.problem.IDEFact +import org.opalj.ide.problem.IDEValue +import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement + +/** + * Specialized property meta information for IDE problems with Java programs + */ +trait JavaIDEPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue] + extends IDEPropertyMetaInformation[JavaStatement, Fact, Value] From 57743a50de739ce77373fad284d08c985dff18a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 10 Jan 2025 12:53:03 +0100 Subject: [PATCH 118/167] Extend IDE precomputation extension to be able to deal with call statements where call targets are unknown --- .../opalj/ide/ifds/problem/IFDSProblem.scala | 13 ++ .../ide/problem/FlowRecordingIDEProblem.scala | 26 +++ .../org/opalj/ide/problem/IDEProblem.scala | 48 ++++- .../scala/org/opalj/ide/solver/ICFG.scala | 14 -- .../org/opalj/ide/solver/IDEAnalysis.scala | 178 ++++++++++-------- 5 files changed, 188 insertions(+), 91 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala index 4c7e2ea60d..bccb664ff4 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala @@ -105,4 +105,17 @@ abstract class IFDSProblem[Fact <: IDEFact, Statement, Callable <: Entity] identityEdgeFunction } } + + override final def getPrecomputedSummaryFunction( + callSite: Statement, + callSiteFact: Fact, + returnSite: Statement, + returnSiteFact: Fact + )(implicit propertyStore: PropertyStore): EdgeFunction[IFDSValue] = { + if (callSiteFact == nullFact) { + AllBottomEdgeFunction + } else { + identityEdgeFunction + } + } } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index cef93585f1..f67504d408 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -238,6 +238,32 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal edgeFunction } + override def getPrecomputedFlowFunction(callSite: Statement, callSiteFact: Fact, returnSite: Statement)( + implicit propertyStore: PropertyStore + ): FlowFunction[Fact] = { + new RecordingFlowFunction( + baseProblem.getPrecomputedFlowFunction(callSite, callSiteFact, returnSite), + callSite, + callSiteFact, + returnSite, + "precomputed flow" + ) + } + + override def getPrecomputedSummaryFunction( + callSite: Statement, + callSiteFact: Fact, + returnSite: Statement, + returnSiteFact: Fact + )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { + val edgeFunction = baseProblem.getPrecomputedSummaryFunction(callSite, callSiteFact, returnSite, returnSiteFact) + collectedEdgeFunctions.put( + createDotEdge(callSite, callSiteFact, returnSite, returnSiteFact, "precomputed flow"), + edgeFunction + ) + edgeFunction + } + private def createDotEdge( source: Statement, sourceFact: Fact, diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index dba075baac..20a702bf5c 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -184,12 +184,15 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl * @param callSiteFact the fact the flow starts with * @param callee the callable this flow is about * @param returnSite where the flow ends (e.g. the next statement after the call) + * @note In this type of precomputed flow the callable is known. Thus, the call-to-return flow can be applied + * normally and does not need to be integrated in this flow. */ def getPrecomputedFlowFunction(callSite: Statement, callSiteFact: Fact, callee: Callable, returnSite: Statement)( implicit propertyStore: PropertyStore ): FlowFunction[Fact] = { throw new IllegalArgumentException( - s"No precomputed flow function for callSite=$callSite, callee=$callee and returnSite=$returnSite exists!" + s"No precomputed flow function for callSite=$callSite, callSiteFact=$callSiteFact, callee=$callee and " + + s"returnSite=$returnSite exists!" ) } @@ -201,6 +204,8 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl * @param callee the callable the flow is about * @param returnSite where the flow ends (e.g. the next statement after the call) * @param returnSiteFact the fact the flow ends with + * @note In this type of precomputed flow the callable is known. Thus, the call-to-return flow can be applied + * normally and does not need to be integrated in this flow. */ def getPrecomputedSummaryFunction( callSite: Statement, @@ -214,4 +219,45 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl s"callee=$callee, returnSite=$returnSite and returnSiteFact=$returnSiteFact exists!" ) } + + /** + * Generate a flow function that yields the facts that are valid when going through the unknown callable and + * reaching the return site. Similar to a call-to-return flow (cfg. [[getCallToReturnFlowFunction]]) but capturing + * the effects that flow through the possible callables. + * @param callSite where the flow starts (always a call statement) + * @param callSiteFact the fact the flow starts with + * @param returnSite where the flow ends (e.g. the next statement after the call) + * @note In this type of precomputed flow the callable is unknown. Thus, the call-to-return flow is not applied and + * needs to be integrated into this flow. + */ + def getPrecomputedFlowFunction(callSite: Statement, callSiteFact: Fact, returnSite: Statement)( + implicit propertyStore: PropertyStore + ): FlowFunction[Fact] = { + throw new IllegalArgumentException( + s"No precomputed flow function for callSite=$callSite, callSiteFact=$callSiteFact and " + + s"returnSite=$returnSite exists!" + ) + } + + /** + * Generate a summary function from a call-site node up to a return-site node (just what summary functions are in + * the foundation paper, but in one step and for all callables that are possible call targets). + * @param callSite where the flow starts (always a call statement) + * @param callSiteFact the fact the flow starts with + * @param returnSite where the flow ends (e.g. the next statement after the call) + * @param returnSiteFact the fact the flow ends with + * @note In this type of precomputed flow the callable is unknown. Thus, the call-to-return flow is not applied and + * needs to be integrated into this flow. + */ + def getPrecomputedSummaryFunction( + callSite: Statement, + callSiteFact: Fact, + returnSite: Statement, + returnSiteFact: Fact + )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { + throw new IllegalArgumentException( + s"No precomputed summary function for callSite=$callSite, callSiteFact=$callSiteFact, " + + s"returnSite=$returnSite and returnSiteFact=$returnSiteFact exists!" + ) + } } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala index 901864ddc1..9668ee3b3d 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala @@ -37,20 +37,6 @@ trait ICFG[Statement, Callable <: Entity] { */ def getCallees(stmt: Statement): collection.Set[Callable] - /** - * Get all possible callees a call statement could call. Throws an exception if no callees could be found. - */ - def getCalleesNonEmpty(stmt: Statement): collection.Set[Callable] = { - val callees = getCallees(stmt) - if (callees.isEmpty) { - throw new IllegalStateException( - s"Statement ${stringifyStatement(stmt)} is detected as call statement but no callees were found!" - ) - } else { - callees - } - } - /** * Get the callable a statement belongs to */ diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 46ccfa80e8..4dc05bd6a8 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -468,7 +468,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logTrace(s"current jumpFunction=$f") if (icfg.isCallStatement(n)) { // IDE P1 line 11 - processCallFlow(path, f, icfg.getCalleesNonEmpty(n)) + processCallFlow(path, f, icfg.getCallees(n)) } else if (icfg.isNormalExitStatement(n)) { // IDE P1 line 19 processExitFlow(path, f) } else { // IDE P1 line 30 @@ -488,99 +488,125 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val rs = icfg.getNextStatements(n) // IDE P1 line 14 - qs.foreach { q => - logDebug(s"handling call target q=$q") - - if (problem.hasPrecomputedFlowAndSummaryFunction(n, d2, q)) { - logDebug(s"handling path with precomputed information") + if (qs.isEmpty) { + logDebug(s"handling path with precomputed information as qs=$qs") - /* Handling for precomputed summaries */ - rs.foreach { r => - val d5s = handleFlowFunctionResult(problem.getPrecomputedFlowFunction(n, d2, q, r).compute(), path) - - logTrace(s"generated the following d5s=$d5s for return statement r=${icfg.stringifyStatement(r)}") + rs.foreach { r => + val d5s = handleFlowFunctionResult(problem.getPrecomputedFlowFunction(n, d2, r).compute(), path) - d5s.foreach { d5 => - val summaryFunction = problem.getPrecomputedSummaryFunction(n, d2, q, r, d5) - val callToReturnPath = ((n, d2), (r, d5)) - val oldSummaryFunction = s.getSummaryFunction(callToReturnPath) - val fPrime = summaryFunction.meetWith(oldSummaryFunction) + logTrace(s"generated the following d5s=$d5s for return statement r=${icfg.stringifyStatement(r)}") - if (!fPrime.equalTo(oldSummaryFunction)) { - s.setSummaryFunction(callToReturnPath, fPrime) - } + d5s.foreach { d5 => + val summaryFunction = problem.getPrecomputedSummaryFunction(n, d2, r, d5) + val callToReturnPath = ((n, d2), (r, d5)) + val oldSummaryFunction = s.getSummaryFunction(callToReturnPath) + val fPrime = summaryFunction.meetWith(oldSummaryFunction) - propagate(((sp, d1), (r, d5)), f.composeWith(fPrime)) + if (!fPrime.equalTo(oldSummaryFunction)) { + s.setSummaryFunction(callToReturnPath, fPrime) } + + propagate(((sp, d1), (r, d5)), f.composeWith(fPrime)) } - } else { - val sqs = icfg.getStartStatements(q) - sqs.foreach { sq => - // IDE P1 lines 12 - 13 - val d3s = handleFlowFunctionResult(problem.getCallFlowFunction(n, d2, sq, q).compute(), path) + } + } else { - logTrace(s"generated the following d3s=$d3s for start statement sq=${icfg.stringifyStatement(sq)}") + qs.foreach { q => + logDebug(s"handling call target q=$q") - d3s.foreach { d3 => - s.rememberCallEdge(((n, d2), (sq, d3))) - - val endSummaries = s.getEndSummaries((sq, d3)) - // Handling for end summaries extension - if (endSummaries.nonEmpty) { - endSummaries.foreach { case ((eq, d4), fEndSummary) => - val f4 = handleEdgeFunctionResult(problem.getCallEdgeFunction(n, d2, sq, d3, q), path) - rs.foreach { r => - val d5s = handleFlowFunctionResult( - problem.getReturnFlowFunction(eq, d4, q, r).compute(), - path - ) - d5s.foreach { d5 => - val f5 = handleEdgeFunctionResult( - problem.getReturnEdgeFunction(eq, d4, q, r, d5), + if (problem.hasPrecomputedFlowAndSummaryFunction(n, d2, q)) { + logDebug(s"handling path with precomputed information") + + /* Handling for precomputed summaries */ + rs.foreach { r => + val d5s = + handleFlowFunctionResult(problem.getPrecomputedFlowFunction(n, d2, q, r).compute(), path) + + logTrace(s"generated the following d5s=$d5s for return statement r=${icfg.stringifyStatement(r)}") + + d5s.foreach { d5 => + val summaryFunction = problem.getPrecomputedSummaryFunction(n, d2, q, r, d5) + val callToReturnPath = ((n, d2), (r, d5)) + val oldSummaryFunction = s.getSummaryFunction(callToReturnPath) + val fPrime = summaryFunction.meetWith(oldSummaryFunction) + + if (!fPrime.equalTo(oldSummaryFunction)) { + s.setSummaryFunction(callToReturnPath, fPrime) + } + + propagate(((sp, d1), (r, d5)), f.composeWith(fPrime)) + } + } + } else { + val sqs = icfg.getStartStatements(q) + sqs.foreach { sq => + // IDE P1 lines 12 - 13 + val d3s = handleFlowFunctionResult(problem.getCallFlowFunction(n, d2, sq, q).compute(), path) + + logTrace(s"generated the following d3s=$d3s for start statement sq=${icfg.stringifyStatement(sq)}") + + d3s.foreach { d3 => + s.rememberCallEdge(((n, d2), (sq, d3))) + + val endSummaries = s.getEndSummaries((sq, d3)) + // Handling for end summaries extension + if (endSummaries.nonEmpty) { + endSummaries.foreach { case ((eq, d4), fEndSummary) => + val f4 = + handleEdgeFunctionResult(problem.getCallEdgeFunction(n, d2, sq, d3, q), path) + rs.foreach { r => + val d5s = handleFlowFunctionResult( + problem.getReturnFlowFunction(eq, d4, q, r).compute(), path ) - val callToReturnPath = ((n, d2), (r, d5)) - val oldSummaryFunction = s.getSummaryFunction(callToReturnPath) - val fPrime = - f4.composeWith(fEndSummary).composeWith(f5).meetWith(oldSummaryFunction) - - if (!fPrime.equalTo(oldSummaryFunction)) { - s.setSummaryFunction(callToReturnPath, fPrime) + d5s.foreach { d5 => + val f5 = handleEdgeFunctionResult( + problem.getReturnEdgeFunction(eq, d4, q, r, d5), + path + ) + val callToReturnPath = ((n, d2), (r, d5)) + val oldSummaryFunction = s.getSummaryFunction(callToReturnPath) + val fPrime = + f4.composeWith(fEndSummary).composeWith(f5).meetWith(oldSummaryFunction) + + if (!fPrime.equalTo(oldSummaryFunction)) { + s.setSummaryFunction(callToReturnPath, fPrime) + } + + propagate(((sp, d1), (r, d5)), f.composeWith(fPrime)) } - - propagate(((sp, d1), (r, d5)), f.composeWith(fPrime)) } } + } else { + // Default algorithm behavior + propagate(((sq, d3), (sq, d3)), identityEdgeFunction) } - } else { - // Default algorithm behavior - propagate(((sq, d3), (sq, d3)), identityEdgeFunction) } } } - } - rs.foreach { r => - val d3s = handleFlowFunctionResult(problem.getCallToReturnFlowFunction(n, d2, q, r).compute(), path) - - logTrace(s"generated the following d3s=$d3s for return-site statement r=${icfg.stringifyStatement(r)}") - - // IDE P1 lines 15 - 16 - d3s.foreach { d3 => - propagate( - ((sp, d1), (r, d3)), - f.composeWith(handleEdgeFunctionResult( - problem.getCallToReturnEdgeFunction(n, d2, q, r, d3), - path - )) - ) - } + rs.foreach { r => + val d3s = handleFlowFunctionResult(problem.getCallToReturnFlowFunction(n, d2, q, r).compute(), path) + + logTrace(s"generated the following d3s=$d3s for return-site statement r=${icfg.stringifyStatement(r)}") - // IDE P1 lines 17 - 18 - d3s.foreach { d3 => - val f3 = s.getSummaryFunction(((n, d2), (r, d3))) - if (!f3.equalTo(allTopEdgeFunction)) { - propagate(((sp, d1), (r, d3)), f.composeWith(f3)) + // IDE P1 lines 15 - 16 + d3s.foreach { d3 => + propagate( + ((sp, d1), (r, d3)), + f.composeWith(handleEdgeFunctionResult( + problem.getCallToReturnEdgeFunction(n, d2, q, r, d3), + path + )) + ) + } + + // IDE P1 lines 17 - 18 + d3s.foreach { d3 => + val f3 = s.getSummaryFunction(((n, d2), (r, d3))) + if (!f3.equalTo(allTopEdgeFunction)) { + propagate(((sp, d1), (r, d3)), f.composeWith(f3)) + } } } } @@ -754,7 +780,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val (n, _) = node if (icfg.isCallStatement(n)) { // IDE P2 line 11 - processCallNode(node, icfg.getCalleesNonEmpty(n)) + processCallNode(node, icfg.getCallees(n)) } else { // IDE P2 line 7 processStartNode(node) } From 99f3902a3f9fc9fbff637cf38b85f34fbdccaa7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 10 Jan 2025 12:59:09 +0100 Subject: [PATCH 119/167] Enhance precomputation for known callable in linear constant propagation --- .../problem/LCPOnFieldsProblem.scala | 22 ++++++++++++++----- .../LinearConstantPropagationProblem.scala | 18 +++++++++++++-- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 34dd2baf0f..dea1d11cd8 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -633,19 +633,27 @@ class LCPOnFieldsProblem( if (callee.isNative || callee.body.isEmpty) { return new FlowFunction[LCPOnFieldsFact] { override def compute(): FactsAndDependees = { - val callStmt = callSite.stmt.asCall() - callSiteFact match { case NullFact => returnSite.stmt.astID match { case Assignment.ASTID => val assignment = returnSite.stmt.asAssignment - immutable.Set(NewObjectFact(assignment.targetVar.name, returnSite.pc)) + if (callee.returnType.isObjectType) { + immutable.Set(NewObjectFact(assignment.targetVar.name, returnSite.pc)) + } else if (callee.returnType.isArrayType && + callee.returnType.asArrayType.componentType.isIntegerType + ) { + immutable.Set(NewArrayFact(assignment.targetVar.name, returnSite.pc)) + } else { + immutable.Set.empty + } case _ => immutable.Set.empty } case f: AbstractEntityFact => + val callStmt = callSite.stmt.asCall() + /* Check whether fact corresponds to one of the parameters */ if (callStmt.allParams.exists { param => param.asVar.definedBy.contains(f.definedAtIndex) }) { immutable.Set(f.toObjectOrArrayFact) @@ -653,8 +661,12 @@ class LCPOnFieldsProblem( immutable.Set.empty } - case _: AbstractStaticFieldFact => - immutable.Set.empty + case f: AbstractStaticFieldFact => + if (callee.classFile.thisType == f.objectType) { + immutable.Set(f.toStaticFieldFact) + } else { + immutable.Set.empty + } } } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 90d2ae5fdf..a121e9d5c2 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -543,7 +543,21 @@ class LinearConstantPropagationProblem returnSite: JavaStatement )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { if (callee.isNative || callee.body.isEmpty) { - return emptyFlowFunction + return new FlowFunction[LinearConstantPropagationFact] { + override def compute(): FactsAndDependees = { + if (callee.returnType.isIntegerType) { + returnSite.stmt.astID match { + case Assignment.ASTID => + val assignment = returnSite.stmt.asAssignment + immutable.Set(VariableFact(assignment.targetVar.name, returnSite.pc)) + + case _ => immutable.Set.empty + } + } else { + immutable.Set.empty + } + } + } } super.getPrecomputedFlowFunction(callSite, callSiteFact, callee, returnSite) @@ -557,7 +571,7 @@ class LinearConstantPropagationProblem returnSiteFact: LinearConstantPropagationFact )(implicit propertyStore: PropertyStore): EdgeFunction[LinearConstantPropagationValue] = { if (callee.isNative || callee.body.isEmpty) { - return identityEdgeFunction + return VariableValueEdgeFunction } super.getPrecomputedSummaryFunction(callSite, callSiteFact, callee, returnSite, returnSiteFact) From f1fc5802b6f341c91eb406524759882cb9ff6b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 10 Jan 2025 13:42:55 +0100 Subject: [PATCH 120/167] Implement precomputation for unknown callable in linear constant propagation --- .../problem/LCPOnFieldsProblem.scala | 67 +++++++++++++++++++ .../LinearConstantPropagationProblem.scala | 42 ++++++++++++ 2 files changed, 109 insertions(+) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index dea1d11cd8..ed15968c7c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -699,4 +699,71 @@ class LCPOnFieldsProblem( super.getPrecomputedSummaryFunction(callSite, callSiteFact, callee, returnSite, returnSiteFact) } + + override def getPrecomputedFlowFunction( + callSite: JavaStatement, + callSiteFact: LCPOnFieldsFact, + returnSite: JavaStatement + )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { + new FlowFunction[LCPOnFieldsFact] { + override def compute(): FactsAndDependees = { + callSiteFact match { + case NullFact => + returnSite.stmt.astID match { + case Assignment.ASTID => + val callStmt = callSite.stmt.asCall() + val assignment = returnSite.stmt.asAssignment + + if (callStmt.descriptor.returnType.isObjectType) { + immutable.Set(callSiteFact, NewObjectFact(assignment.targetVar.name, returnSite.pc)) + } else if (callStmt.descriptor.returnType.isArrayType && + callStmt.descriptor.returnType.asArrayType.componentType.isIntegerType + ) { + immutable.Set(callSiteFact, NewArrayFact(assignment.targetVar.name, returnSite.pc)) + } else { + immutable.Set(callSiteFact) + } + + case _ => immutable.Set(callSiteFact) + } + + case f: AbstractEntityFact => + immutable.Set(f.toObjectOrArrayFact) + + case f: AbstractStaticFieldFact => + immutable.Set(f.toStaticFieldFact) + } + } + } + } + + override def getPrecomputedSummaryFunction( + callSite: JavaStatement, + callSiteFact: LCPOnFieldsFact, + returnSite: JavaStatement, + returnSiteFact: LCPOnFieldsFact + )(implicit propertyStore: PropertyStore): EdgeFunction[LCPOnFieldsValue] = { + (callSiteFact, returnSiteFact) match { + case (NullFact, _: AbstractObjectFact) => + VariableValueEdgeFunction + + case (NullFact, _: AbstractArrayFact) => + NewArrayEdgeFunction(linear_constant_propagation.problem.VariableValue) + + case (_: AbstractEntityFact, f: AbstractEntityFact) => + val callStmt = callSite.stmt.asCall() + + /* Check whether fact corresponds to one of the parameters */ + if (callStmt.allParams.exists { param => param.asVar.definedBy.contains(f.definedAtIndex) }) { + VariableValueEdgeFunction + } else { + identityEdgeFunction + } + + case (_, _: AbstractStaticFieldFact) => + VariableValueEdgeFunction + + case _ => identityEdgeFunction + } + } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index a121e9d5c2..b966b363f4 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -576,4 +576,46 @@ class LinearConstantPropagationProblem super.getPrecomputedSummaryFunction(callSite, callSiteFact, callee, returnSite, returnSiteFact) } + + override def getPrecomputedFlowFunction( + callSite: JavaStatement, + callSiteFact: LinearConstantPropagationFact, + returnSite: JavaStatement + )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { + new FlowFunction[LinearConstantPropagationFact] { + override def compute(): FactsAndDependees = { + if (!callSite.stmt.asCall().descriptor.returnType.isIntegerType) { + callSiteFact match { + case NullFact => + returnSite.stmt.astID match { + case Assignment.ASTID => + val assignment = returnSite.stmt.asAssignment + immutable.Set(callSiteFact, VariableFact(assignment.targetVar.name, returnSite.pc)) + + case _ => immutable.Set(callSiteFact) + } + + case VariableFact(_, _) => immutable.Set(callSiteFact) + } + } else { + immutable.Set(callSiteFact) + } + } + } + } + + override def getPrecomputedSummaryFunction( + callSite: JavaStatement, + callSiteFact: LinearConstantPropagationFact, + returnSite: JavaStatement, + returnSiteFact: LinearConstantPropagationFact + )(implicit propertyStore: PropertyStore): EdgeFunction[LinearConstantPropagationValue] = { + (callSiteFact, returnSiteFact) match { + case (NullFact, VariableFact(_, _)) => + VariableValueEdgeFunction + + case _ => + identityEdgeFunction + } + } } From b16c37f31a98c251fb4cada5d05668eff58a4d01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 13 Jan 2025 17:09:12 +0100 Subject: [PATCH 121/167] Fix hashCode implementation --- .../opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala index de50fe3117..e8c90cbabf 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala @@ -28,7 +28,12 @@ case class JavaStatement( def basicBlock: BasicBlock = cfg.bb(pc) - override def hashCode(): Int = method.hashCode() * 31 + pc + override def hashCode(): Int = { + (method.hashCode() * 31 + pc) * 31 + ( + if (isReturnNode) { 1 } + else { 0 } + ) + } override def equals(obj: Any): Boolean = obj match { case JavaStatement(method2, pc2, isReturnNode2, _, _) => From b697e7eb1521883176022a4dea10c90fb9987a04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 17 Jan 2025 15:55:31 +0100 Subject: [PATCH 122/167] Allow precomputed summary functions to be intermediate edge functions --- .../opalj/ide/ifds/problem/IFDSProblem.scala | 5 ++--- .../ide/problem/FlowRecordingIDEProblem.scala | 17 +++++++++-------- .../org/opalj/ide/problem/IDEProblem.scala | 4 ++-- .../org/opalj/ide/solver/IDEAnalysis.scala | 6 ++++-- .../problem/LCPOnFieldsProblem.scala | 5 ++--- .../LinearConstantPropagationProblem.scala | 5 ++--- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala index bccb664ff4..31ef6c3ecf 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala @@ -3,7 +3,6 @@ package org.opalj.ide.ifds.problem import org.opalj.fpcf.Entity import org.opalj.fpcf.PropertyStore -import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEProblem @@ -98,7 +97,7 @@ abstract class IFDSProblem[Fact <: IDEFact, Statement, Callable <: Entity] callee: Callable, returnSite: Statement, returnSiteFact: Fact - )(implicit propertyStore: PropertyStore): EdgeFunction[IFDSValue] = { + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[IFDSValue] = { if (callSiteFact == nullFact) { AllBottomEdgeFunction } else { @@ -111,7 +110,7 @@ abstract class IFDSProblem[Fact <: IDEFact, Statement, Callable <: Entity] callSiteFact: Fact, returnSite: Statement, returnSiteFact: Fact - )(implicit propertyStore: PropertyStore): EdgeFunction[IFDSValue] = { + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[IFDSValue] = { if (callSiteFact == nullFact) { AllBottomEdgeFunction } else { diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index f67504d408..6c91b5b03f 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -228,14 +228,14 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal callee: Callable, returnSite: Statement, returnSiteFact: Fact - )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { - val edgeFunction = + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] = { + val edgeFunctionResult = baseProblem.getPrecomputedSummaryFunction(callSite, callSiteFact, callee, returnSite, returnSiteFact) collectedEdgeFunctions.put( createDotEdge(callSite, callSiteFact, returnSite, returnSiteFact, "precomputed flow"), - edgeFunction + getEdgeFunctionFromEdgeFunctionResult(edgeFunctionResult) ) - edgeFunction + edgeFunctionResult } override def getPrecomputedFlowFunction(callSite: Statement, callSiteFact: Fact, returnSite: Statement)( @@ -255,13 +255,14 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal callSiteFact: Fact, returnSite: Statement, returnSiteFact: Fact - )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { - val edgeFunction = baseProblem.getPrecomputedSummaryFunction(callSite, callSiteFact, returnSite, returnSiteFact) + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] = { + val edgeFunctionResult = + baseProblem.getPrecomputedSummaryFunction(callSite, callSiteFact, returnSite, returnSiteFact) collectedEdgeFunctions.put( createDotEdge(callSite, callSiteFact, returnSite, returnSiteFact, "precomputed flow"), - edgeFunction + getEdgeFunctionFromEdgeFunctionResult(edgeFunctionResult) ) - edgeFunction + edgeFunctionResult } private def createDotEdge( diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index 20a702bf5c..591295f2a1 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -213,7 +213,7 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl callee: Callable, returnSite: Statement, returnSiteFact: Fact - )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] = { throw new IllegalArgumentException( s"No precomputed summary function for callSite=$callSite, callSiteFact=$callSiteFact, " + s"callee=$callee, returnSite=$returnSite and returnSiteFact=$returnSiteFact exists!" @@ -254,7 +254,7 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl callSiteFact: Fact, returnSite: Statement, returnSiteFact: Fact - )(implicit propertyStore: PropertyStore): EdgeFunction[Value] = { + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] = { throw new IllegalArgumentException( s"No precomputed summary function for callSite=$callSite, callSiteFact=$callSiteFact, " + s"returnSite=$returnSite and returnSiteFact=$returnSiteFact exists!" diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 4dc05bd6a8..de45682ef1 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -497,7 +497,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logTrace(s"generated the following d5s=$d5s for return statement r=${icfg.stringifyStatement(r)}") d5s.foreach { d5 => - val summaryFunction = problem.getPrecomputedSummaryFunction(n, d2, r, d5) + val summaryFunction = + handleEdgeFunctionResult(problem.getPrecomputedSummaryFunction(n, d2, r, d5), path) val callToReturnPath = ((n, d2), (r, d5)) val oldSummaryFunction = s.getSummaryFunction(callToReturnPath) val fPrime = summaryFunction.meetWith(oldSummaryFunction) @@ -525,7 +526,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logTrace(s"generated the following d5s=$d5s for return statement r=${icfg.stringifyStatement(r)}") d5s.foreach { d5 => - val summaryFunction = problem.getPrecomputedSummaryFunction(n, d2, q, r, d5) + val summaryFunction = + handleEdgeFunctionResult(problem.getPrecomputedSummaryFunction(n, d2, q, r, d5), path) val callToReturnPath = ((n, d2), (r, d5)) val oldSummaryFunction = s.getSummaryFunction(callToReturnPath) val fPrime = summaryFunction.meetWith(oldSummaryFunction) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index ed15968c7c..fbc8f9111c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -17,7 +17,6 @@ import org.opalj.fpcf.FinalP import org.opalj.fpcf.InterimUBP import org.opalj.fpcf.Property import org.opalj.fpcf.PropertyStore -import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.FinalEdgeFunction import org.opalj.ide.problem.FlowFunction @@ -681,7 +680,7 @@ class LCPOnFieldsProblem( callee: Method, returnSite: JavaStatement, returnSiteFact: LCPOnFieldsFact - )(implicit propertyStore: PropertyStore): EdgeFunction[LCPOnFieldsValue] = { + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LCPOnFieldsValue] = { if (callee.isNative || callee.body.isEmpty) { return returnSiteFact match { case _: AbstractObjectFact => @@ -742,7 +741,7 @@ class LCPOnFieldsProblem( callSiteFact: LCPOnFieldsFact, returnSite: JavaStatement, returnSiteFact: LCPOnFieldsFact - )(implicit propertyStore: PropertyStore): EdgeFunction[LCPOnFieldsValue] = { + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LCPOnFieldsValue] = { (callSiteFact, returnSiteFact) match { case (NullFact, _: AbstractObjectFact) => VariableValueEdgeFunction diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index b966b363f4..1b5dd55c1b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -9,7 +9,6 @@ import org.opalj.BinaryArithmeticOperators import org.opalj.ai.domain.l1.DefaultIntegerRangeValues import org.opalj.br.Method import org.opalj.fpcf.PropertyStore -import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.FlowFunction import org.opalj.ide.problem.IdentityFlowFunction @@ -569,7 +568,7 @@ class LinearConstantPropagationProblem callee: Method, returnSite: JavaStatement, returnSiteFact: LinearConstantPropagationFact - )(implicit propertyStore: PropertyStore): EdgeFunction[LinearConstantPropagationValue] = { + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { if (callee.isNative || callee.body.isEmpty) { return VariableValueEdgeFunction } @@ -609,7 +608,7 @@ class LinearConstantPropagationProblem callSiteFact: LinearConstantPropagationFact, returnSite: JavaStatement, returnSiteFact: LinearConstantPropagationFact - )(implicit propertyStore: PropertyStore): EdgeFunction[LinearConstantPropagationValue] = { + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { (callSiteFact, returnSiteFact) match { case (NullFact, VariableFact(_, _)) => VariableValueEdgeFunction From 0c7e9bff0913ba72e3f7cc2aeeb7dd57c5b373d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 17 Jan 2025 16:00:02 +0100 Subject: [PATCH 123/167] More conservative handling of static fields in LCP on fields --- .../problem/LCPOnFieldsProblem.scala | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index fbc8f9111c..9cd19feab6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -76,14 +76,9 @@ class LCPOnFieldsProblem( private def getEdgeFunctionForStaticFieldFactByImmutability(staticFieldFact: StaticFieldFact)( implicit propertyStore: PropertyStore ): EdgeFunctionResult[LCPOnFieldsValue] = { - val declaredField = - declaredFields( - staticFieldFact.objectType, - staticFieldFact.fieldName, - IntegerType - ) + val declaredField = declaredFields(staticFieldFact.objectType, staticFieldFact.fieldName, IntegerType) if (!declaredField.isDefinedField) { - return identityEdgeFunction + return PutStaticFieldEdgeFunction(linear_constant_propagation.problem.VariableValue) } val field = declaredField.definedField @@ -238,12 +233,7 @@ class LCPOnFieldsProblem( /* Only fields of type integer */ if (putStatic.declaredFieldType.isIntegerType) { - val declaredField = - declaredFields( - putStatic.declaringClass, - putStatic.name, - putStatic.declaredFieldType - ) + val declaredField = declaredFields(putStatic.declaringClass, putStatic.name, IntegerType) if (!declaredField.isDefinedField) { return immutable.Set(sourceFact) } @@ -683,6 +673,9 @@ class LCPOnFieldsProblem( )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LCPOnFieldsValue] = { if (callee.isNative || callee.body.isEmpty) { return returnSiteFact match { + case NullFact => + identityEdgeFunction + case _: AbstractObjectFact => VariableValueEdgeFunction @@ -690,9 +683,7 @@ class LCPOnFieldsProblem( NewArrayEdgeFunction(linear_constant_propagation.problem.VariableValue) case _: AbstractStaticFieldFact => - VariableValueEdgeFunction - - case _ => identityEdgeFunction + PutStaticFieldEdgeFunction(linear_constant_propagation.problem.VariableValue) } } From e59dcfd14806b623d1cd1a6bc8e56d3dd0034d93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 17 Jan 2025 16:07:15 +0100 Subject: [PATCH 124/167] More precise summaries for object and array facts in LCP on fields --- .../problem/LCPOnFieldsProblem.scala | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 9cd19feab6..f504d23db4 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -733,6 +733,8 @@ class LCPOnFieldsProblem( returnSite: JavaStatement, returnSiteFact: LCPOnFieldsFact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LCPOnFieldsValue] = { + val callStmt = callSite.stmt.asCall() + (callSiteFact, returnSiteFact) match { case (NullFact, _: AbstractObjectFact) => VariableValueEdgeFunction @@ -741,11 +743,19 @@ class LCPOnFieldsProblem( NewArrayEdgeFunction(linear_constant_propagation.problem.VariableValue) case (_: AbstractEntityFact, f: AbstractEntityFact) => - val callStmt = callSite.stmt.asCall() - + /* Constructor of object class doesn't modify the object */ + if (callStmt.declaringClass.isObjectType && + callStmt.declaringClass.asObjectType.fqn == "java/lang/Object" && callStmt.name == "" + ) { + identityEdgeFunction + } /* Check whether fact corresponds to one of the parameters */ - if (callStmt.allParams.exists { param => param.asVar.definedBy.contains(f.definedAtIndex) }) { - VariableValueEdgeFunction + else if (callStmt.allParams.exists { param => param.asVar.definedBy.contains(f.definedAtIndex) }) { + f match { + case _: AbstractObjectFact => VariableValueEdgeFunction + case _: AbstractEntityFact => + NewArrayEdgeFunction(linear_constant_propagation.problem.VariableValue) + } } else { identityEdgeFunction } From 9497afa9fd7dab799e2f5b1426028cf26ad505b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 17 Jan 2025 16:09:03 +0100 Subject: [PATCH 125/167] More precise summaries for static facts in LCP on fields --- .../problem/LCPOnFieldsProblem.scala | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index f504d23db4..2528a151be 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -73,7 +73,7 @@ class LCPOnFieldsProblem( } } - private def getEdgeFunctionForStaticFieldFactByImmutability(staticFieldFact: StaticFieldFact)( + private def getEdgeFunctionForStaticFieldFactByImmutability(staticFieldFact: AbstractStaticFieldFact)( implicit propertyStore: PropertyStore ): EdgeFunctionResult[LCPOnFieldsValue] = { val declaredField = declaredFields(staticFieldFact.objectType, staticFieldFact.fieldName, IntegerType) @@ -760,8 +760,18 @@ class LCPOnFieldsProblem( identityEdgeFunction } - case (_, _: AbstractStaticFieldFact) => - VariableValueEdgeFunction + case (_, f: AbstractStaticFieldFact) => + val declaredField = declaredFields(f.objectType, f.fieldName, IntegerType) + if (!declaredField.isDefinedField) { + return PutStaticFieldEdgeFunction(linear_constant_propagation.problem.VariableValue) + } + val field = declaredField.definedField + + if (callStmt.declaringClass != f.objectType && field.isPrivate) { + identityEdgeFunction + } else { + getEdgeFunctionForStaticFieldFactByImmutability(f) + } case _ => identityEdgeFunction } From afe0d8146c812f83426eca92514fcfbde4850fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 17 Jan 2025 16:10:13 +0100 Subject: [PATCH 126/167] Fix precomputed flow function generation for linear constant propagation --- .../problem/LinearConstantPropagationProblem.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 1b5dd55c1b..07d8d23aa5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -583,7 +583,7 @@ class LinearConstantPropagationProblem )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { new FlowFunction[LinearConstantPropagationFact] { override def compute(): FactsAndDependees = { - if (!callSite.stmt.asCall().descriptor.returnType.isIntegerType) { + if (callSite.stmt.asCall().descriptor.returnType.isIntegerType) { callSiteFact match { case NullFact => returnSite.stmt.astID match { From 3a0fb6a0fb1ba4a0a39e745f8e56c0b73bbeafb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 17 Jan 2025 16:11:37 +0100 Subject: [PATCH 127/167] Fix linting issues --- .../problem/LinearConstantPropagationProblem.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 07d8d23aa5..b8f5c66284 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -372,8 +372,8 @@ class LinearConstantPropagationProblem val leftExpr = binaryExpr.left val rightExpr = binaryExpr.right - if (leftExpr.astID != Var.ASTID && leftExpr.astID != IntConst.ASTID - || rightExpr.astID != Var.ASTID && rightExpr.astID != IntConst.ASTID + if (leftExpr.astID != Var.ASTID && leftExpr.astID != IntConst.ASTID || + rightExpr.astID != Var.ASTID && rightExpr.astID != IntConst.ASTID ) { throw new IllegalArgumentException(s"Combination ($leftExpr, $rightExpr) should not occur here!") } From 57a5bc17d503ac2e904d2d2380d7c0ba746baa32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 17 Jan 2025 16:18:35 +0100 Subject: [PATCH 128/167] Do not include runtime in IDE tests by default --- .../src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala index 4baae9822b..6187fa732d 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala @@ -16,7 +16,7 @@ import org.opalj.ide.ConfigKeyTraceLog import org.opalj.tac.cg.RTACallGraphKey class IDEPropertiesTest extends PropertiesTest { - override def withRT: Boolean = true + override def withRT: Boolean = false override def createConfig(): Config = { super.createConfig() From 498958d463102b71458711e831da59251e594e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 20 Jan 2025 12:03:39 +0100 Subject: [PATCH 129/167] More precise summaries for object facts in LCP on fields --- .../lcp_on_fields/problem/LCPOnFieldsProblem.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 2528a151be..311c3385b1 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -677,7 +677,15 @@ class LCPOnFieldsProblem( identityEdgeFunction case _: AbstractObjectFact => - VariableValueEdgeFunction + val callStmt = callSite.stmt.asCall() + + if (callStmt.declaringClass.isObjectType && + callStmt.declaringClass.asObjectType.fqn == "java/lang/Object" && callStmt.name == "" + ) { + identityEdgeFunction + } else { + VariableValueEdgeFunction + } case _: AbstractArrayFact => NewArrayEdgeFunction(linear_constant_propagation.problem.VariableValue) From bd6e10ef3063a4f54f0a90bf5038768bf33ee1de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 29 Jan 2025 19:02:09 +0100 Subject: [PATCH 130/167] Improve precision of linear constant propagation --- .../PropagationAcrossMethodsExample.java | 6 ++++-- .../problem/LinearConstantPropagationProblem.scala | 7 ++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java index a98c07c245..0a658acb00 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java @@ -7,8 +7,10 @@ import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; public class PropagationAcrossMethodsExample { - @VariableValue(variable = "param2") - @VariableValue(variable = "param3") + @VariableValues({ + @VariableValue(variable = "param2"), + @VariableValue(variable = "param3") + }) public int linearCalculation1(String msg, int a, int b) { System.out.println(msg + ": " + a); return 42 - 5 * b; diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index b8f5c66284..5ae6289367 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -505,7 +505,12 @@ class LinearConstantPropagationProblem calleeEntry: JavaStatement, calleeEntryFact: LinearConstantPropagationFact, callee: Method - )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = identityEdgeFunction + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { + callSiteFact match { + case NullFact => UnknownValueEdgeFunction + case _ => identityEdgeFunction + } + } override def getReturnEdgeFunction( calleeExit: JavaStatement, From f9eb5657a46e37de5b16048491117e1b09d7e217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 29 Jan 2025 19:04:42 +0100 Subject: [PATCH 131/167] Consequent AllTopEdgeFunction implementations --- .../main/scala/org/opalj/ide/problem/EdgeFunction.scala | 8 ++++++-- .../src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 3 +++ .../lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala | 3 --- .../problem/LinearConstantPropagationEdgeFunctions.scala | 5 +---- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala index 5370969165..d38d0ec13e 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala @@ -62,8 +62,12 @@ abstract case class AllTopEdgeFunction[Value <: IDEValue](private val top: Value override def compute(sourceValue: Value): Value = top - override def meetWith(otherEdgeFunction: EdgeFunction[Value]): EdgeFunction[Value] = - otherEdgeFunction + override def meetWith(otherEdgeFunction: EdgeFunction[Value]): EdgeFunction[Value] = { + otherEdgeFunction match { + case AllTopEdgeFunction(_) => this + case _ => otherEdgeFunction + } + } override def equalTo(otherEdgeFunction: EdgeFunction[Value]): Boolean = (otherEdgeFunction eq this) || diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index de45682ef1..b38567bd00 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -63,6 +63,9 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") } + override def meetWith(otherEdgeFunction: EdgeFunction[Value]): EdgeFunction[Value] = + otherEdgeFunction + override def equalTo(otherEdgeFunction: EdgeFunction[Value]): Boolean = otherEdgeFunction eq this } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala index e7c97afc40..fa4ddf9c5f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -427,9 +427,6 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LCPOnFieldsValue](Unk this } - override def equalTo(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): Boolean = - otherEdgeFunction eq this - override def toString: String = "UnknownValueEdgeFunction()" } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala index ebe75f8089..872a7c0074 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala @@ -126,15 +126,12 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LinearConstantPropaga case UnknownValueEdgeFunction => secondEdgeFunction case IdentityEdgeFunction() => this - case AllTopEdgeFunction(_) => secondEdgeFunction + case AllTopEdgeFunction(_) => this case _ => throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") } } - override def equalTo(otherEdgeFunction: EdgeFunction[LinearConstantPropagationValue]): Boolean = - otherEdgeFunction eq this - override def toString: String = "UnknownValueEdgeFunction()" } From 365ee586c7d3efec419af552557101e2e8c49a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Thu, 30 Jan 2025 18:32:26 +0100 Subject: [PATCH 132/167] Implement return flow functions extension --- .../opalj/ide/ifds/problem/IFDSProblem.scala | 4 +++- .../ide/problem/FlowRecordingIDEProblem.scala | 20 +++++++++++++++---- .../org/opalj/ide/problem/IDEProblem.scala | 17 ++++++++++++++-- .../org/opalj/ide/solver/IDEAnalysis.scala | 8 ++++---- .../problem/LCPOnFieldsProblem.scala | 8 ++++++-- .../LinearConstantPropagationProblem.scala | 8 ++++++-- 6 files changed, 50 insertions(+), 15 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala index 31ef6c3ecf..bde1f65a0b 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala @@ -47,7 +47,9 @@ abstract class IFDSProblem[Fact <: IDEFact, Statement, Callable <: Entity] calleeExitFact: Fact, callee: Callable, returnSite: Statement, - returnSiteFact: Fact + returnSiteFact: Fact, + callSite: Statement, + callSiteFact: Fact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[IFDSValue] = { if (calleeExitFact == nullFact) { AllBottomEdgeFunction diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index 6c91b5b03f..4d26849986 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -111,10 +111,12 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal calleeExit: Statement, calleeExitFact: Fact, callee: Callable, - returnSite: Statement + returnSite: Statement, + callSite: Statement, + callSiteFact: Fact )(implicit propertyStore: PropertyStore): FlowFunction[Fact] = { new RecordingFlowFunction( - baseProblem.getReturnFlowFunction(calleeExit, calleeExitFact, callee, returnSite), + baseProblem.getReturnFlowFunction(calleeExit, calleeExitFact, callee, returnSite, callSite, callSiteFact), calleeExit, calleeExitFact, returnSite, @@ -172,10 +174,20 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal calleeExitFact: Fact, callee: Callable, returnSite: Statement, - returnSiteFact: Fact + returnSiteFact: Fact, + callSite: Statement, + callSiteFact: Fact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] = { val edgeFunctionResult = - baseProblem.getReturnEdgeFunction(calleeExit, calleeExitFact, callee, returnSite, returnSiteFact) + baseProblem.getReturnEdgeFunction( + calleeExit, + calleeExitFact, + callee, + returnSite, + returnSiteFact, + callSite, + callSiteFact + ) collectedEdgeFunctions.put( createDotEdge(calleeExit, calleeExitFact, returnSite, returnSiteFact, "return flow"), getEdgeFunctionFromEdgeFunctionResult(edgeFunctionResult) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index 591295f2a1..2affd89864 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -85,8 +85,17 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl * @param calleeExitFact the fact the flow starts with * @param callee the callable that is returned from * @param returnSite where the return flow ends (e.g. the next statement after the call in the callers code) + * @param callSite corresponding to the return flow + * @param callSiteFact corresponding to the return flow */ - def getReturnFlowFunction(calleeExit: Statement, calleeExitFact: Fact, callee: Callable, returnSite: Statement)( + def getReturnFlowFunction( + calleeExit: Statement, + calleeExitFact: Fact, + callee: Callable, + returnSite: Statement, + callSite: Statement, + callSiteFact: Fact + )( implicit propertyStore: PropertyStore ): FlowFunction[Fact] @@ -138,13 +147,17 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl * @param callee the callable that is returned from * @param returnSite where the return flow ends (e.g. the next statement after the call in the callers code) * @param returnSiteFact the fact the flow ends with + * @param callSite corresponding to the return flow + * @param callSiteFact corresponding to the return flow */ def getReturnEdgeFunction( calleeExit: Statement, calleeExitFact: Fact, callee: Callable, returnSite: Statement, - returnSiteFact: Fact + returnSiteFact: Fact, + callSite: Statement, + callSiteFact: Fact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] /** diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index b38567bd00..713defcf91 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -561,12 +561,12 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent handleEdgeFunctionResult(problem.getCallEdgeFunction(n, d2, sq, d3, q), path) rs.foreach { r => val d5s = handleFlowFunctionResult( - problem.getReturnFlowFunction(eq, d4, q, r).compute(), + problem.getReturnFlowFunction(eq, d4, q, r, n, d2).compute(), path ) d5s.foreach { d5 => val f5 = handleEdgeFunctionResult( - problem.getReturnEdgeFunction(eq, d4, q, r, d5), + problem.getReturnEdgeFunction(eq, d4, q, r, d5, n, d2), path ) val callToReturnPath = ((n, d2), (r, d5)) @@ -636,14 +636,14 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logDebug(s"handling calling statement c=${icfg.stringifyStatement(c)}, d4=$d4 and return-site statement r=${icfg.stringifyStatement(r)}") // IDE P1 line 21 - val d5s = handleFlowFunctionResult(problem.getReturnFlowFunction(n, d2, p, r).compute(), path) + val d5s = handleFlowFunctionResult(problem.getReturnFlowFunction(n, d2, p, r, c, d4).compute(), path) logDebug(s"generated the following d5s=$d5s") d5s.foreach { d5 => // IDE P1 lines 22 - 23 val f4 = handleEdgeFunctionResult(problem.getCallEdgeFunction(c, d4, sp, d1, p), path) - val f5 = handleEdgeFunctionResult(problem.getReturnEdgeFunction(n, d2, p, r, d5), path) + val f5 = handleEdgeFunctionResult(problem.getReturnEdgeFunction(n, d2, p, r, d5, c, d4), path) // IDE P1 line 24 val callToReturnPath = ((c, d4), (r, d5)) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 311c3385b1..9144706a1c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -339,7 +339,9 @@ class LCPOnFieldsProblem( calleeExit: JavaStatement, calleeExitFact: LCPOnFieldsFact, callee: Method, - returnSite: JavaStatement + returnSite: JavaStatement, + callSite: JavaStatement, + callSiteFact: LCPOnFieldsFact )(implicit propertyStore: PropertyStore): FlowFunction[LCPOnFieldsFact] = { new FlowFunction[LCPOnFieldsFact] { override def compute(): FactsAndDependees = { @@ -590,7 +592,9 @@ class LCPOnFieldsProblem( calleeExitFact: LCPOnFieldsFact, callee: Method, returnSite: JavaStatement, - returnSiteFact: LCPOnFieldsFact + returnSiteFact: LCPOnFieldsFact, + callSite: JavaStatement, + callSiteFact: LCPOnFieldsFact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LCPOnFieldsValue] = identityEdgeFunction override def getCallToReturnEdgeFunction( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 5ae6289367..333585ba4b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -258,7 +258,9 @@ class LinearConstantPropagationProblem calleeExit: JavaStatement, calleeExitFact: LinearConstantPropagationFact, callee: Method, - returnSite: JavaStatement + returnSite: JavaStatement, + callSite: JavaStatement, + callSiteFact: LinearConstantPropagationFact )(implicit propertyStore: PropertyStore): FlowFunction[LinearConstantPropagationFact] = { new FlowFunction[LinearConstantPropagationFact] { override def compute(): FactsAndDependees = { @@ -517,7 +519,9 @@ class LinearConstantPropagationProblem calleeExitFact: LinearConstantPropagationFact, callee: Method, returnSite: JavaStatement, - returnSiteFact: LinearConstantPropagationFact + returnSiteFact: LinearConstantPropagationFact, + callSite: JavaStatement, + callSiteFact: LinearConstantPropagationFact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = identityEdgeFunction override def getCallToReturnEdgeFunction( From fdc1f208d6803c63b4c01f695116c6a166f3329f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Thu, 30 Jan 2025 18:49:00 +0100 Subject: [PATCH 133/167] Improve precision of LCP on fields --- .../problem/LCPOnFieldsEdgeFunctions.scala | 17 ++++++++++++++++- .../problem/LCPOnFieldsProblem.scala | 7 ++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala index fa4ddf9c5f..2a8da06d38 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -424,7 +424,22 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LCPOnFieldsValue](Unk override def composeWith( secondEdgeFunction: EdgeFunction[LCPOnFieldsValue] ): EdgeFunction[LCPOnFieldsValue] = { - this + secondEdgeFunction match { + case ObjectEdgeFunction(_) => secondEdgeFunction + case PutFieldEdgeFunction(_, _) => secondEdgeFunction + case ArrayEdgeFunction(_, _) => secondEdgeFunction + case PutElementEdgeFunction(_, _) => secondEdgeFunction + case PutStaticFieldEdgeFunction(_) => secondEdgeFunction + + case VariableValueEdgeFunction => secondEdgeFunction + case UnknownValueEdgeFunction => secondEdgeFunction + + case IdentityEdgeFunction() => this + case AllTopEdgeFunction(_) => this + + case _ => + throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") + } } override def toString: String = "UnknownValueEdgeFunction()" diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 9144706a1c..66a1329810 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -585,7 +585,12 @@ class LCPOnFieldsProblem( calleeEntry: JavaStatement, calleeEntryFact: LCPOnFieldsFact, callee: Method - )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LCPOnFieldsValue] = identityEdgeFunction + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LCPOnFieldsValue] = { + callSiteFact match { + case NullFact => UnknownValueEdgeFunction + case _ => identityEdgeFunction + } + } override def getReturnEdgeFunction( calleeExit: JavaStatement, From 7a844b95f7c57ff4323006295d1254e94e154717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Thu, 30 Jan 2025 18:49:35 +0100 Subject: [PATCH 134/167] fixup! Improve precision of LCP on fields --- .../problem/LCPOnFieldsEdgeFunctions.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala index 2a8da06d38..0527ca942c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -425,20 +425,20 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LCPOnFieldsValue](Unk secondEdgeFunction: EdgeFunction[LCPOnFieldsValue] ): EdgeFunction[LCPOnFieldsValue] = { secondEdgeFunction match { - case ObjectEdgeFunction(_) => secondEdgeFunction - case PutFieldEdgeFunction(_, _) => secondEdgeFunction - case ArrayEdgeFunction(_, _) => secondEdgeFunction - case PutElementEdgeFunction(_, _) => secondEdgeFunction + case ObjectEdgeFunction(_) => secondEdgeFunction + case PutFieldEdgeFunction(_, _) => secondEdgeFunction + case ArrayEdgeFunction(_, _) => secondEdgeFunction + case PutElementEdgeFunction(_, _) => secondEdgeFunction case PutStaticFieldEdgeFunction(_) => secondEdgeFunction case VariableValueEdgeFunction => secondEdgeFunction - case UnknownValueEdgeFunction => secondEdgeFunction + case UnknownValueEdgeFunction => secondEdgeFunction case IdentityEdgeFunction() => this - case AllTopEdgeFunction(_) => this + case AllTopEdgeFunction(_) => this case _ => - throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") + throw new UnsupportedOperationException(s"Composing $this with $secondEdgeFunction is not implemented!") } } From 0e2d1395bfb7f126ef9ea52e8418468a55e4301a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 31 Jan 2025 09:36:16 +0100 Subject: [PATCH 135/167] Reorder and extend generics --- .../ifds/integration/IFDSPropertyMetaInformation.scala | 5 +++-- .../integration/BaseIDEAnalysisProxyScheduler.scala | 2 +- .../integration/EagerIDEAnalysisProxyScheduler.scala | 2 +- .../integration/FlowRecordingAnalysisScheduler.scala | 2 +- .../opalj/ide/integration/IDEAnalysisScheduler.scala | 2 +- .../ide/integration/IDEPropertyMetaInformation.scala | 8 +++++--- .../org/opalj/ide/integration/IDERawProperty.scala | 6 +++--- .../integration/IDERawPropertyMetaInformation.scala | 10 +++++----- .../integration/LazyIDEAnalysisProxyScheduler.scala | 2 +- .../main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 2 +- .../scala/org/opalj/ide/solver/IDEAnalysisProxy.scala | 2 +- .../integration/JavaIFDSPropertyMetaInformation.scala | 3 ++- .../integration/JavaIDEPropertyMetaInformation.scala | 3 ++- 13 files changed, 27 insertions(+), 22 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala index df5574f3f0..3e2d7b2282 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala @@ -1,6 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ide.ifds.integration +import org.opalj.fpcf.Entity import org.opalj.ide.ifds.problem.IFDSValue import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.ide.problem.IDEFact @@ -8,5 +9,5 @@ import org.opalj.ide.problem.IDEFact /** * Interface for property meta information for IFDS problems based on an IDE problem */ -trait IFDSPropertyMetaInformation[Statement, Fact <: IDEFact] - extends IDEPropertyMetaInformation[Statement, Fact, IFDSValue] +trait IFDSPropertyMetaInformation[Fact <: IDEFact, Statement, Callable <: Entity] + extends IDEPropertyMetaInformation[Fact, IFDSValue, Statement, Callable] diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala index 1e108acb0a..5c7d8786fc 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala @@ -20,7 +20,7 @@ import org.opalj.ide.solver.IDEAnalysisProxy */ trait BaseIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity] extends FPCFAnalysisScheduler { - val propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] + val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value, Statement, Callable] override type InitializationData = IDEAnalysisProxy[Fact, Value, Statement, Callable] diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala index 3ac42a7a61..ecc438bd63 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala @@ -17,7 +17,7 @@ import org.opalj.ide.solver.IDEAnalysisProxy * @param methodProvider for which methods the results should be computed eagerly */ class EagerIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( - val propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value], + val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value, Statement, Callable], methodProvider: SomeProject => Iterable[Method] = { project => project.allMethodsWithBody } ) extends BaseIDEAnalysisProxyScheduler[Fact, Value, Statement, Callable] with FPCFEagerAnalysisScheduler { def this(ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable, ?]) = { diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala index f0cf01fa28..483afa895a 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala @@ -51,7 +51,7 @@ class FlowRecordingAnalysisScheduler[ recordEdgeFunctions: Boolean = true ) extends IDEAnalysisScheduler[Fact, Value, Statement, Callable, _ICFG] with Logging.EnableAll with Logging.GlobalLogContext { - override def propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] = { + override def propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value, Statement, Callable] = { ideAnalysisScheduler.propertyMetaInformation } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala index 2ce96cfda6..4096083ee9 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala @@ -29,7 +29,7 @@ abstract class IDEAnalysisScheduler[ ] extends FPCFLazyAnalysisScheduler { override final type InitializationData = IDEAnalysis[Fact, Value, Statement, Callable] - def propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] + def propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value, Statement, Callable] def createProblem(project: SomeProject, icfg: _ICFG): IDEProblem[Fact, Value, Statement, Callable] diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala index 77b35fcd5c..7b675d079f 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala @@ -1,6 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ide.integration +import org.opalj.fpcf.Entity import org.opalj.fpcf.PropertyMetaInformation import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEValue @@ -8,14 +9,15 @@ import org.opalj.ide.problem.IDEValue /** * Base interface of property meta information of IDE analyses. Creates [[BasicIDEProperty]] by default. */ -trait IDEPropertyMetaInformation[Statement, Fact <: IDEFact, Value <: IDEValue] extends PropertyMetaInformation { +trait IDEPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity] + extends PropertyMetaInformation { override type Self = BasicIDEProperty[Fact, Value] /** * A property meta information corresponding to this one but used for the actual/underlying IDE analysis */ - private[ide] val backingPropertyMetaInformation: IDERawPropertyMetaInformation[Statement, Fact, Value] = - new IDERawPropertyMetaInformation[Statement, Fact, Value](this) + private[ide] val backingPropertyMetaInformation: IDERawPropertyMetaInformation[Fact, Value, Statement] = + new IDERawPropertyMetaInformation[Fact, Value, Statement](this) /** * Create a property diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala index 9e3f5be085..d03f689afe 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala @@ -12,12 +12,12 @@ import org.opalj.ide.problem.IDEValue * @param stmtResults the raw statement results produced by the analysis * @param callableResults the raw callable results produced by the analysis */ -class IDERawProperty[Statement, Fact <: IDEFact, Value <: IDEValue]( - val key: PropertyKey[IDERawProperty[Statement, Fact, Value]], +class IDERawProperty[Fact <: IDEFact, Value <: IDEValue, Statement]( + val key: PropertyKey[IDERawProperty[Fact, Value, Statement]], val stmtResults: collection.Map[Statement, collection.Set[(Fact, Value)]], val callableResults: collection.Set[(Fact, Value)] ) extends Property { - override type Self = IDERawProperty[Statement, Fact, Value] + override type Self = IDERawProperty[Fact, Value, Statement] override def toString: String = { s"IDERawProperty(${PropertyKey.name(key)}, {\n${ diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala index 8e9c380d45..f31845ebc2 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala @@ -11,17 +11,17 @@ import org.opalj.ide.problem.IDEValue * The property type is fixed to [[IDERawProperty]]. * @param propertyMetaInformation the property meta information this object should be backing */ -final class IDERawPropertyMetaInformation[Statement, Fact <: IDEFact, Value <: IDEValue]( - propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] +final class IDERawPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue, Statement]( + propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value, Statement, ?] ) extends PropertyMetaInformation { - override type Self = IDERawProperty[Statement, Fact, Value] + override type Self = IDERawProperty[Fact, Value, Statement] /** * The used property key, based on [[propertyMetaInformation]] */ - private lazy val backingPropertyKey: PropertyKey[IDERawProperty[Statement, Fact, Value]] = { + private lazy val propertyKey: PropertyKey[IDERawProperty[Fact, Value, Statement]] = { PropertyKey.create(s"${PropertyKey.name(propertyMetaInformation.key)}_Raw") } - override def key: PropertyKey[IDERawProperty[Statement, Fact, Value]] = backingPropertyKey + override def key: PropertyKey[IDERawProperty[Fact, Value, Statement]] = propertyKey } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala index 69b564272f..e40010e03d 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala @@ -15,7 +15,7 @@ import org.opalj.ide.solver.IDEAnalysisProxy * A scheduler to (lazily) schedule the proxy analysis that is used to access the IDE analysis results */ class LazyIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( - val propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] + val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value, Statement, Callable] ) extends BaseIDEAnalysisProxyScheduler[Fact, Value, Statement, Callable] with FPCFLazyAnalysisScheduler { def this(ideAnalysisScheduler: IDEAnalysisScheduler[Fact, Value, Statement, Callable, ?]) = { this(ideAnalysisScheduler.propertyMetaInformation) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 713defcf91..087fe3034b 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -36,7 +36,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val project: SomeProject, val problem: IDEProblem[Fact, Value, Statement, Callable], val icfg: ICFG[Statement, Callable], - val propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] + val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value, Statement, Callable] ) extends FPCFAnalysis with Logging.ByProjectConfig { private type Node = (Statement, Fact) /** diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala index 26fc111897..79102193fa 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala @@ -25,7 +25,7 @@ import org.opalj.ide.util.Logging */ class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( val project: SomeProject, - val propertyMetaInformation: IDEPropertyMetaInformation[Statement, Fact, Value] + val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value, Statement, Callable] ) extends FPCFAnalysis with Logging.ByProjectConfig { /** * @param entity either only a callable or a pair of callable and statement that should be analyzed (if no statement diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala index f762500752..dff3ff931e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala @@ -1,6 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ide.ifds.integration +import org.opalj.br.Method import org.opalj.ide.ifds.integration.IFDSPropertyMetaInformation import org.opalj.ide.problem.IDEFact import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement @@ -8,4 +9,4 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement /** * Specialized property meta information for IFDS problems with Java programs */ -trait JavaIFDSPropertyMetaInformation[Fact <: IDEFact] extends IFDSPropertyMetaInformation[JavaStatement, Fact] +trait JavaIFDSPropertyMetaInformation[Fact <: IDEFact] extends IFDSPropertyMetaInformation[Fact, JavaStatement, Method] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala index 9b6cd01791..ca0039467f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala @@ -1,6 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ide.integration +import org.opalj.br.Method import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEValue @@ -10,4 +11,4 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement * Specialized property meta information for IDE problems with Java programs */ trait JavaIDEPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue] - extends IDEPropertyMetaInformation[JavaStatement, Fact, Value] + extends IDEPropertyMetaInformation[Fact, Value, JavaStatement, Method] From 4dc2bdb859353b16879a6ec724756454afdd544a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 31 Jan 2025 09:38:17 +0100 Subject: [PATCH 136/167] Prepare to track target callables --- .../IDEPropertyMetaInformation.scala | 6 ++++ .../IDETargetCallablesProperty.scala | 31 +++++++++++++++++++ ...rgetCallablesPropertyMetaInformation.scala | 24 ++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesProperty.scala create mode 100644 OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesPropertyMetaInformation.scala diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala index 7b675d079f..6cedba1236 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala @@ -19,6 +19,12 @@ trait IDEPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue, Statement, private[ide] val backingPropertyMetaInformation: IDERawPropertyMetaInformation[Fact, Value, Statement] = new IDERawPropertyMetaInformation[Fact, Value, Statement](this) + /** + * A property meta information corresponding to this one but used for target callables + */ + private[ide] val targetCallablesPropertyMetaInformation: IDETargetCallablesPropertyMetaInformation[Callable] = + new IDETargetCallablesPropertyMetaInformation[Callable](this) + /** * Create a property * @param results the results the property should represent diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesProperty.scala new file mode 100644 index 0000000000..735eccb31a --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesProperty.scala @@ -0,0 +1,31 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.integration + +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyKey + +/** + * Property for the target callables that should be analysed by an IDE analysis. + */ +class IDETargetCallablesProperty[Callable <: Entity]( + val key: PropertyKey[IDETargetCallablesProperty[Callable]], + val targetCallables: collection.Set[Callable] +) extends Property { + override type Self = IDETargetCallablesProperty[Callable] + + override def toString: String = + s"IDETargetCallablesProperty(\n${targetCallables.map { callable => s"\t$callable" }.mkString("\n")}\n)" + + override def equals(other: Any): Boolean = { + other match { + case ideTargetCallablesProperty: IDETargetCallablesProperty[?] => + key == ideTargetCallablesProperty.key && targetCallables == ideTargetCallablesProperty.targetCallables + case _ => false + } + } + + override def hashCode(): Int = { + key.hashCode() * 31 + targetCallables.hashCode() + } +} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesPropertyMetaInformation.scala new file mode 100644 index 0000000000..17914fb042 --- /dev/null +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesPropertyMetaInformation.scala @@ -0,0 +1,24 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ide.integration + +import org.opalj.fpcf.Entity +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.PropertyMetaInformation + +/** + * Class for property meta information for properties carrying the target callables. + */ +final class IDETargetCallablesPropertyMetaInformation[Callable <: Entity]( + propertyMetaInformation: IDEPropertyMetaInformation[?, ?, ?, Callable] +) extends PropertyMetaInformation { + override type Self = IDETargetCallablesProperty[Callable] + + /** + * The used property key, based on [[propertyMetaInformation]] + */ + private lazy val propertyKey: PropertyKey[IDETargetCallablesProperty[Callable]] = { + PropertyKey.create(s"${PropertyKey.name(propertyMetaInformation.key)}_TargetCallables") + } + + override def key: PropertyKey[IDETargetCallablesProperty[Callable]] = propertyKey +} From 4e9e7799b4c38b629d318792d3b68e040160e8d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 31 Jan 2025 11:04:54 +0100 Subject: [PATCH 137/167] Fix linting issues --- .../BaseIDEAnalysisProxyScheduler.scala | 15 +++++++++++---- .../ide/integration/IDEAnalysisScheduler.scala | 15 +++++++++++---- .../org/opalj/ide/solver/IDEAnalysisProxy.scala | 4 ++-- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala index 5c7d8786fc..a6f3ca94aa 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala @@ -28,16 +28,23 @@ trait BaseIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statemen override def requiredProjectInformation: ProjectInformationKeys = Seq(PropertyStoreKey) - override def init(project: SomeProject, ps: PropertyStore): IDEAnalysisProxy[Fact, Value, Statement, Callable] = { + override def init( + project: SomeProject, + propertyStore: PropertyStore + ): IDEAnalysisProxy[Fact, Value, Statement, Callable] = { new IDEAnalysisProxy[Fact, Value, Statement, Callable](project, propertyMetaInformation) } override def uses: Set[PropertyBounds] = immutable.Set(PropertyBounds.ub(propertyMetaInformation.backingPropertyMetaInformation)) - override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + override def beforeSchedule(project: SomeProject, propertyStore: PropertyStore): Unit = {} - override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} + override def afterPhaseScheduling(propertyStore: PropertyStore, analysis: FPCFAnalysis): Unit = {} - override def afterPhaseCompletion(p: SomeProject, ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} + override def afterPhaseCompletion( + project: SomeProject, + propertyStore: PropertyStore, + analysis: FPCFAnalysis + ): Unit = {} } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala index 4096083ee9..d8431078f8 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala @@ -44,9 +44,12 @@ abstract class IDEAnalysisScheduler[ override def uses: immutable.Set[PropertyBounds] = immutable.Set.empty - override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + override def beforeSchedule(project: SomeProject, propertyStore: PropertyStore): Unit = {} - override final def init(project: SomeProject, ps: PropertyStore): IDEAnalysis[Fact, Value, Statement, Callable] = { + override final def init( + project: SomeProject, + propertyStore: PropertyStore + ): IDEAnalysis[Fact, Value, Statement, Callable] = { val icfg = createICFG(project) val problem = createProblem(project, icfg) new IDEAnalysis(project, problem, icfg, propertyMetaInformation) @@ -64,7 +67,11 @@ abstract class IDEAnalysisScheduler[ analysis } - override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} + override def afterPhaseScheduling(propertyStore: PropertyStore, analysis: FPCFAnalysis): Unit = {} - override def afterPhaseCompletion(p: SomeProject, ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} + override def afterPhaseCompletion( + project: SomeProject, + propertyStore: PropertyStore, + analysis: FPCFAnalysis + ): Unit = {} } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala index 79102193fa..bbec89c9ea 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala @@ -87,8 +87,8 @@ class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statement, Callable < } private def onDependeeUpdateContinuation( - callable: Callable, - stmtOption: Option[Statement] + callable: Callable, + stmtOption: Option[Statement] )(eps: SomeEPS): ProperPropertyComputationResult = { createResult(callable, stmtOption) } From 8c0248a1f5613687e452aef2cf0451c2b4acc664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 31 Jan 2025 15:28:21 +0100 Subject: [PATCH 138/167] Extend IDE solver framework to analyse multiple callables efficiently --- .../BaseIDEAnalysisProxyScheduler.scala | 13 +- .../org/opalj/ide/solver/IDEAnalysis.scala | 277 +++++++++++++----- .../opalj/ide/solver/IDEAnalysisProxy.scala | 47 ++- 3 files changed, 251 insertions(+), 86 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala index a6f3ca94aa..01fa8790b5 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala @@ -9,8 +9,10 @@ import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.fpcf.Entity +import org.opalj.fpcf.PartialResult import org.opalj.fpcf.PropertyBounds import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.SomeEOptionP import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEValue import org.opalj.ide.solver.IDEAnalysisProxy @@ -38,7 +40,16 @@ trait BaseIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statemen override def uses: Set[PropertyBounds] = immutable.Set(PropertyBounds.ub(propertyMetaInformation.backingPropertyMetaInformation)) - override def beforeSchedule(project: SomeProject, propertyStore: PropertyStore): Unit = {} + override def beforeSchedule(project: SomeProject, propertyStore: PropertyStore): Unit = { + /* Add initial result for target callables */ + propertyStore.handleResult( + PartialResult( + propertyMetaInformation, + propertyMetaInformation.targetCallablesPropertyMetaInformation.key, + { (_: SomeEOptionP) => None } + ) + ) + } override def afterPhaseScheduling(propertyStore: PropertyStore, analysis: FPCFAnalysis): Unit = {} diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 087fe3034b..8ea3a0407c 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -7,15 +7,21 @@ import scala.collection.mutable import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.fpcf.Entity -import org.opalj.fpcf.InterimResult +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.EPS +import org.opalj.fpcf.InterimEUBP +import org.opalj.fpcf.InterimPartialResult +import org.opalj.fpcf.PartialResult import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.Result +import org.opalj.fpcf.Results import org.opalj.fpcf.SomeEOptionP import org.opalj.fpcf.SomeEPK import org.opalj.fpcf.SomeEPS import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.ide.integration.IDERawProperty +import org.opalj.ide.integration.IDETargetCallablesProperty import org.opalj.ide.problem.AllTopEdgeFunction import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.EdgeFunctionResult @@ -74,8 +80,25 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent * Container class for simpler interaction and passing of the 'shared' data */ private class State( - val targetCallable: Callable + initialTargetCallablesEOptionP: EOptionP[ + IDEPropertyMetaInformation[Fact, Value, Statement, Callable], + IDETargetCallablesProperty[Callable] + ] ) { + private val targetCallables = mutable.Set.empty[Callable] + + private var targetCallablesEOptionP: EOptionP[ + IDEPropertyMetaInformation[Fact, Value, Statement, Callable], + IDETargetCallablesProperty[Callable] + ] = initialTargetCallablesEOptionP + + processTargetCallablesEOptionP(initialTargetCallablesEOptionP) + + /** + * Remember the callables where a final result has already been produced + */ + private val callablesWithFinalResults = mutable.Set.empty[Callable] + /** * The work list for paths used in P1 */ @@ -122,6 +145,35 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent */ private val dependees = mutable.Map.empty[SomeEPK, (SomeEOptionP, mutable.Set[() => Unit])] + def getTargetCallables: collection.Set[Callable] = { + targetCallables + } + + def getTargetCallablesWithoutFinalResults: collection.Set[Callable] = { + targetCallables.diff(callablesWithFinalResults) + } + + def rememberCallableWithFinalResult(callable: Callable): Unit = { + callablesWithFinalResults.add(callable) + } + + def getTargetCallablesEOptionP: EOptionP[ + IDEPropertyMetaInformation[Fact, Value, Statement, Callable], + IDETargetCallablesProperty[Callable] + ] = { + targetCallablesEOptionP + } + + def processTargetCallablesEOptionP(newTargetCallablesEOptionP: EOptionP[ + IDEPropertyMetaInformation[Fact, Value, Statement, Callable], + IDETargetCallablesProperty[Callable] + ]): Unit = { + targetCallablesEOptionP = newTargetCallablesEOptionP + if (targetCallablesEOptionP.hasUBP) { + targetCallables.addAll(targetCallablesEOptionP.ub.targetCallables) + } + } + def enqueuePath(path: Path): Unit = { pathWorkList.enqueue(path) } @@ -253,38 +305,41 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } /** - * @return a map from statements to results and the results for the callable in total + * @return a map from statements to results and the results for the callable in total (both per requested + * callable) */ - def collectResults(callable: Callable): ( - collection.Map[Statement, collection.Set[(Fact, Value)]], - collection.Set[(Fact, Value)] - ) = { - val relevantValues = values + def collectResults(callables: collection.Set[Callable]): Map[ + Callable, + (collection.Map[Statement, collection.Set[(Fact, Value)]], collection.Set[(Fact, Value)]) + ] = { + val relevantValuesByCallable = values .filter { case ((n, d), _) => - icfg.getCallable(n) == callable && d != problem.nullFact - } - - val resultsByStatement = relevantValues - .groupMap(_._1._1) { case ((_, d), value) => (d, value) } - .map { case (n, dValuePairs) => - ( - n, - dValuePairs.groupMapReduce(_._1)(_._2) { (value1, value2) => - problem.lattice.meet(value1, value2) - }.toSet - ) - } + callables.contains(icfg.getCallable(n)) && d != problem.nullFact + }.groupBy { case ((n, _), _) => icfg.getCallable(n) } + + relevantValuesByCallable.map { case (callable, relevantValues) => + val resultsByStatement = relevantValues + .groupMap(_._1._1) { case ((_, d), value) => (d, value) } + .map { case (n, dValuePairs) => + ( + n, + dValuePairs.groupMapReduce(_._1)(_._2) { (value1, value2) => + problem.lattice.meet(value1, value2) + }.toSet + ) + } - val resultsForExit = resultsByStatement - .filter { case (n, _) => icfg.isNormalExitStatement(n) } - .map(_._2.toList) - .flatten - .groupMapReduce(_._1)(_._2) { - (value1, value2) => problem.lattice.meet(value1, value2) - } - .toSet + val resultsForExit = resultsByStatement + .filter { case (n, _) => icfg.isNormalExitStatement(n) } + .map(_._2.toList) + .flatten + .groupMapReduce(_._1)(_._2) { + (value1, value2) => problem.lattice.meet(value1, value2) + } + .toSet - (resultsByStatement, resultsForExit) + callable -> (resultsByStatement, resultsForExit) + } } def addDependee(eOptionP: SomeEOptionP, c: () => Unit): Unit = { @@ -320,16 +375,30 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } /** - * Run the IDE solver and calculate (and return) the result - * @param callable the callable that should be analyzed - * @return a result for each statement of the callable plus a result for the callable itself (combining the results - * of all exit statements) + * Run the IDE solver and calculate (and return) the result. This method should only be triggered in combination + * with the IDE proxy! + * @param entity Expected to be `None`. Other values do not cause errors but will only return empty (temporary) + * results. + * @return a result for each statement of the target callables plus one result for each target callable itself + * (combining the results of all exit statements) */ - // TODO (IDE) WHAT HAPPENS WHEN ANALYZING MULTIPLE CALLABLES? CAN WE CACHE E.G. JUMP/SUMMARY FUNCTIONS? - def performAnalysis(callable: Callable): ProperPropertyComputationResult = { - logInfo(s"performing ${PropertyKey.name(propertyMetaInformation.key)} for $callable") + def performAnalysis(entity: Entity): ProperPropertyComputationResult = { + /* If an actual entity reaches here, there was a concrete request to the property store that was faster than + * this analysis could answer when being called with `entity == None`. Returning an 'empty' result in this case, + * which of course will be updated if the right analysis request completes. */ + if (entity != None) { + return PartialResult( + entity, + propertyMetaInformation.backingPropertyMetaInformation.key, + { (_: SomeEOptionP) => None } + ) + } + + val targetCallablesEOptionP = + propertyStore(propertyMetaInformation, propertyMetaInformation.targetCallablesPropertyMetaInformation.key) + implicit val state: State = new State(targetCallablesEOptionP) - implicit val state: State = new State(callable) + logInfo(s"performing ${PropertyKey.name(propertyMetaInformation.key)} for ${state.getTargetCallables.size} callables") performPhase1() performPhase2() @@ -394,26 +463,68 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent private def createResult()( implicit s: State ): ProperPropertyComputationResult = { - logDebug("starting creation of properties") - - val callable = s.targetCallable + logDebug("starting creation of properties and results") + + val callables = s.getTargetCallablesWithoutFinalResults + val collectedResults = s.collectResults(callables) + val callableResults = collectedResults.map { case (callable, (resultsByStatement, resultsForExit)) => + val ideRawProperty = new IDERawProperty( + propertyMetaInformation.backingPropertyMetaInformation.key, + resultsByStatement, + resultsForExit + ) + + if (s.areDependeesEmpty) { + s.rememberCallableWithFinalResult(callable) + Result(callable, ideRawProperty) + } else { + PartialResult( + callable, + propertyMetaInformation.backingPropertyMetaInformation.key, + { (eOptionP: SomeEOptionP) => + if (eOptionP.hasUBP && eOptionP.ub == ideRawProperty) { + None + } else { + Some(InterimEUBP(callable, ideRawProperty)) + } + } + ) + } + } - val (resultsByStatement, resultsForExit) = s.collectResults(callable) - val ideRawProperty = new IDERawProperty( - propertyMetaInformation.backingPropertyMetaInformation.key, - resultsByStatement, - resultsForExit + logDebug("finished creation of properties and results") + + Results( + Seq( + InterimPartialResult( + None, + s.getDependees.toSet ++ immutable.Set(s.getTargetCallablesEOptionP), + { (eps: SomeEPS) => + if (eps.toEPK == s.getTargetCallablesEOptionP.toEPK) { + onTargetCallablesUpdateContinuation(eps.asInstanceOf[EPS[ + IDEPropertyMetaInformation[Fact, Value, Statement, Callable], + IDETargetCallablesProperty[Callable] + ]]) + } else { + onDependeeUpdateContinuation(eps) + } + } + ) + ) ++ callableResults ) + } - logDebug("finished creation of properties") + private def onTargetCallablesUpdateContinuation(eps: EPS[ + IDEPropertyMetaInformation[Fact, Value, Statement, Callable], + IDETargetCallablesProperty[Callable] + ])(implicit s: State): ProperPropertyComputationResult = { + s.processTargetCallablesEOptionP(eps) - if (s.areDependeesEmpty) { - logDebug("creating final results") - Result(callable, ideRawProperty) - } else { - logDebug("creating interim results") - InterimResult.forUB(callable, ideRawProperty, s.getDependees.toSet, onDependeeUpdateContinuation) - } + seedPhase1() + continuePhase1() + performPhase2() + + createResult() } private def onDependeeUpdateContinuation(eps: SomeEPS)( @@ -433,27 +544,30 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } private def seedPhase1()(implicit s: State): Unit = { - val callable = s.targetCallable + val callables = s.getTargetCallablesWithoutFinalResults + callables.foreach { callable => + // IDE P1 lines 5 - 6 + icfg.getStartStatements(callable).foreach { stmt => + val path = ((stmt, problem.nullFact), (stmt, problem.nullFact)) + s.enqueuePath(path) + s.setJumpFunction(path, identityEdgeFunction) - // IDE P1 lines 5 - 6 - icfg.getStartStatements(callable).foreach { stmt => - val path = ((stmt, problem.nullFact), (stmt, problem.nullFact)) - s.enqueuePath(path) - s.setJumpFunction(path, identityEdgeFunction) + problem.getAdditionalSeeds(stmt, callable).foreach { fact => + val path = ((stmt, problem.nullFact), (stmt, fact)) + s.setJumpFunction(path, identityEdgeFunction) + + def processAdditionalSeed(): Unit = { + val edgeFunction = + handleEdgeFunctionResult( + problem.getAdditionalSeedsEdgeFunction(stmt, fact, callable), + processAdditionalSeed _ + ) + s.setJumpFunction(path, s.getJumpFunction(path).meetWith(edgeFunction)) + s.enqueuePath(path) + } - problem.getAdditionalSeeds(stmt, callable).foreach { fact => - val path = ((stmt, problem.nullFact), (stmt, fact)) - s.setJumpFunction(path, identityEdgeFunction) - def processAdditionalSeed(): Unit = { - val edgeFunction = - handleEdgeFunctionResult( - problem.getAdditionalSeedsEdgeFunction(stmt, fact, callable), - processAdditionalSeed _ - ) - s.setJumpFunction(path, s.getJumpFunction(path).meetWith(edgeFunction)) - s.enqueuePath(path) + processAdditionalSeed() } - processAdditionalSeed() } } @@ -760,13 +874,14 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } private def seedPhase2()(implicit s: State): Unit = { - val callable = s.targetCallable - - // IDE P2 lines 2 - 3 - icfg.getStartStatements(callable).foreach { stmt => - val node = (stmt, problem.nullFact) - s.enqueueNode(node) - s.setValue(node, problem.lattice.bottom) + val callables = s.getTargetCallables + callables.foreach { callable => + // IDE P2 lines 2 - 3 + icfg.getStartStatements(callable).foreach { stmt => + val node = (stmt, problem.nullFact) + s.enqueueNode(node) + s.setValue(node, problem.lattice.bottom) + } } logDebug(s"seeded with ${s.getNodeWorkListSize} node(s)") @@ -799,8 +914,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // IDE P2 part (ii) // IDE P2 lines 15 - 17 // Reduced to the one callable, results are created for - val p = s.targetCallable - val sps = icfg.getStartStatements(p) + val ps = s.getTargetCallablesWithoutFinalResults + val sps = ps.flatMap { p => icfg.getStartStatements(p) } val ns = collectReachableStmts(sps, stmt => !icfg.isCallStatement(stmt)) // IDE P2 line 16 - 17 diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala index bbec89c9ea..db9b604c85 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala @@ -6,13 +6,18 @@ import scala.collection.immutable import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.fpcf.Entity +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.InterimEUBP import org.opalj.fpcf.InterimPartialResult import org.opalj.fpcf.InterimResult +import org.opalj.fpcf.PartialResult import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.Result +import org.opalj.fpcf.Results import org.opalj.fpcf.SomeEPS import org.opalj.ide.integration.IDEPropertyMetaInformation +import org.opalj.ide.integration.IDETargetCallablesProperty import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEValue import org.opalj.ide.util.Logging @@ -39,6 +44,9 @@ class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statement, Callable < case c => (c.asInstanceOf[Callable], None) } + /* Request to trigger IDE solver such that it listens for changes to set of target callables */ + propertyStore(None, propertyMetaInformation.backingPropertyMetaInformation.key) + createResult(callable, stmtOption) } @@ -54,10 +62,41 @@ class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statement, Callable < } if (backingEOptionP.isEPK) { - // In this case, the analysis has not been called yet - InterimPartialResult( - immutable.Set(backingEOptionP), - onDependeeUpdateContinuation(callable, stmtOption) + /* In this case, the analysis has not been called yet */ + Results( + PartialResult( + propertyMetaInformation, + propertyMetaInformation.targetCallablesPropertyMetaInformation.key, + { + (eOptionP: EOptionP[ + IDEPropertyMetaInformation[Fact, Value, Statement, Callable], + IDETargetCallablesProperty[Callable] + ]) => + if (!eOptionP.hasUBP) { + Some(InterimEUBP( + propertyMetaInformation, + new IDETargetCallablesProperty( + propertyMetaInformation.targetCallablesPropertyMetaInformation.key, + immutable.Set(callable) + ) + )) + } else if (eOptionP.ub.targetCallables.contains(callable)) { + None + } else { + Some(InterimEUBP( + propertyMetaInformation, + new IDETargetCallablesProperty( + propertyMetaInformation.targetCallablesPropertyMetaInformation.key, + eOptionP.ub.targetCallables ++ immutable.Set(callable) + ) + )) + } + } + ), + InterimPartialResult( + immutable.Set(backingEOptionP), + onDependeeUpdateContinuation(callable, stmtOption) + ) ) } else if (backingEOptionP.isFinal) { Result( From 1a9da62ded706e9213a35c2d92c0173bb98af4d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 31 Jan 2025 15:29:45 +0100 Subject: [PATCH 139/167] Fix LCP on fields tests --- .../fixtures/lcp_on_fields/CreateObjectInMethodExample.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java index 9e6e9192be..20d528b283 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java @@ -4,7 +4,6 @@ import org.opalj.fpcf.properties.lcp_on_fields.ObjectValue; import org.opalj.fpcf.properties.lcp_on_fields.ObjectValues; import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; -import org.opalj.fpcf.properties.linear_constant_propagation.UnknownValue; public class CreateObjectInMethodExample { private int a = 42; @@ -24,7 +23,7 @@ private CreateObjectInMethodExample createNew2() { @ObjectValues({ @ObjectValue(variable = "lv0", constantValues = {@ConstantValue(variable = "a", value = 42)}), @ObjectValue(variable = "lv2", constantValues = {@ConstantValue(variable = "a", value = 31)}), - @ObjectValue(variable = "lv3", unknownValues = {@UnknownValue(variable = "a")}) + @ObjectValue(variable = "lv3", constantValues = {@ConstantValue(variable = "a", value = 33)}) }) public static void main(String[] args) { CreateObjectInMethodExample example1 = new CreateObjectInMethodExample(); From 5dcc0d4dde97157cb4fa2d01c4e41c5d9cff34e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 3 Feb 2025 14:00:09 +0100 Subject: [PATCH 140/167] Fix issue about missing results --- .../main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 8ea3a0407c..94b7cec2af 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -467,7 +467,16 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val callables = s.getTargetCallablesWithoutFinalResults val collectedResults = s.collectResults(callables) - val callableResults = collectedResults.map { case (callable, (resultsByStatement, resultsForExit)) => + val callableResults = callables.map { callable => + val (resultsByStatement, resultsForExit) = + collectedResults.getOrElse( + callable, { + ( + immutable.Map.empty[Statement, collection.Set[(Fact, Value)]], + immutable.Set.empty[(Fact, Value)] + ) + } + ) val ideRawProperty = new IDERawProperty( propertyMetaInformation.backingPropertyMetaInformation.key, resultsByStatement, From 1e589187fbc1a9eab3b28a8ef9ef168561235be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 3 Feb 2025 14:00:53 +0100 Subject: [PATCH 141/167] Fix pattern match exception --- .../ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 94b7cec2af..bce678866d 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -883,7 +883,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } private def seedPhase2()(implicit s: State): Unit = { - val callables = s.getTargetCallables + val callables = s.getTargetCallablesWithoutFinalResults callables.foreach { callable => // IDE P2 lines 2 - 3 icfg.getStartStatements(callable).foreach { stmt => @@ -942,6 +942,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logTrace(s"setting value of nSharp=${nodeToString(nSharp)} to vPrime=$vPrime") s.setValue(nSharp, vPrime) + + case _ => } } } From ce44d3674943f5d7a534d5012ff10aaef180ca5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 3 Feb 2025 16:04:38 +0100 Subject: [PATCH 142/167] Fix inconsistent results for static fields --- .../instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala index 6ebb16e72b..1ebc4e6aee 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala @@ -42,6 +42,9 @@ object LCPOnFieldsLattice extends MeetLattice[LCPOnFieldsValue] { elements ) + case (StaticFieldValue(xValue), StaticFieldValue(yValue)) => + StaticFieldValue(LinearConstantPropagationLattice.meet(xValue, yValue)) + case _ => VariableValue } } From 04961fbbe4fbf471aba38c115bf5a9789b388e11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 5 Feb 2025 13:10:35 +0100 Subject: [PATCH 143/167] Fix inconsistent seeds --- .../CreateObjectInMethodExample.java | 4 ++- .../problem/LCPOnFieldsProblem.scala | 32 +++++++++++++++---- ...arConstantPropagationProblemExtended.scala | 28 ++++++++++------ .../LinearConstantPropagationProblem.scala | 11 +++++++ 4 files changed, 59 insertions(+), 16 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java index 20d528b283..2341075dd9 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java @@ -4,6 +4,7 @@ import org.opalj.fpcf.properties.lcp_on_fields.ObjectValue; import org.opalj.fpcf.properties.lcp_on_fields.ObjectValues; import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; public class CreateObjectInMethodExample { private int a = 42; @@ -14,6 +15,7 @@ private CreateObjectInMethodExample createNew1() { return example; } + @ObjectValue(variable = "lv0", variableValues = {@VariableValue(variable = "a")}) private CreateObjectInMethodExample createNew2() { CreateObjectInMethodExample example = new CreateObjectInMethodExample(); example.a = a + 2; @@ -23,7 +25,7 @@ private CreateObjectInMethodExample createNew2() { @ObjectValues({ @ObjectValue(variable = "lv0", constantValues = {@ConstantValue(variable = "a", value = 42)}), @ObjectValue(variable = "lv2", constantValues = {@ConstantValue(variable = "a", value = 31)}), - @ObjectValue(variable = "lv3", constantValues = {@ConstantValue(variable = "a", value = 33)}) + @ObjectValue(variable = "lv3", variableValues = {@VariableValue(variable = "a")}) }) public static void main(String[] args) { CreateObjectInMethodExample example1 = new CreateObjectInMethodExample(); diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 66a1329810..becf17ae4f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -15,7 +15,6 @@ import org.opalj.br.fpcf.properties.immutability.FieldImmutability import org.opalj.br.fpcf.properties.immutability.TransitivelyImmutableField import org.opalj.fpcf.FinalP import org.opalj.fpcf.InterimUBP -import org.opalj.fpcf.Property import org.opalj.fpcf.PropertyStore import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.FinalEdgeFunction @@ -59,15 +58,35 @@ class LCPOnFieldsProblem( override def getAdditionalSeeds(stmt: JavaStatement, callee: Method)( implicit propertyStore: PropertyStore ): collection.Set[LCPOnFieldsFact] = { - callee.classFile.fields.filter(_.isStatic).map { field => - StaticFieldFact(field.classFile.thisType, field.name) - }.toSet + (if (callee.isStatic) { + immutable.Set.empty + } else { + immutable.Set(ObjectFact("param0", -1)) + }) ++ + callee.parameterTypes + .zipWithIndex + .filter { case (paramType, _) => paramType.isObjectType || paramType.isArrayType } + .map { case (paramType, index) => + if (paramType.isObjectType) { + ObjectFact(s"param${index + 1}", -(index + 2)) + } else { + ArrayFact(s"param${index + 1}", -(index + 2)) + } + } + .toSet ++ + callee.classFile + .fields + .filter(_.isStatic) + .map { field => StaticFieldFact(field.classFile.thisType, field.name) } + .toSet } override def getAdditionalSeedsEdgeFunction(stmt: JavaStatement, fact: LCPOnFieldsFact, callee: Method)( implicit propertyStore: PropertyStore ): EdgeFunctionResult[LCPOnFieldsValue] = { fact match { + case ObjectFact(_, _) => UnknownValueEdgeFunction + case ArrayFact(_, _) => UnknownValueEdgeFunction case f @ StaticFieldFact(_, _) => getEdgeFunctionForStaticFieldFactByImmutability(f) case _ => super.getAdditionalSeedsEdgeFunction(stmt, fact, callee) } @@ -561,9 +580,10 @@ class LCPOnFieldsProblem( } } - private def getVariableFromProperty(var0: JavaStatement.V)(property: Property): LinearConstantPropagationValue = { + private def getVariableFromProperty(var0: JavaStatement.V)( + property: LinearConstantPropagationPropertyMetaInformation.Self + ): LinearConstantPropagationValue = { property - .asInstanceOf[LinearConstantPropagationPropertyMetaInformation.Self] .results .filter { case (linear_constant_propagation.problem.VariableFact(_, definedAtIndex), _) => diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala index bffa884a26..c6a5bedb93 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala @@ -1,13 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem -import scala.collection.immutable - import org.opalj.ai.domain.l1.DefaultIntegerRangeValues import org.opalj.br.ObjectType import org.opalj.fpcf.FinalP import org.opalj.fpcf.InterimUBP -import org.opalj.fpcf.Property import org.opalj.fpcf.PropertyStore import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.FinalEdgeFunction @@ -16,6 +13,7 @@ import org.opalj.tac.ArrayLoad import org.opalj.tac.GetField import org.opalj.tac.GetStatic import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.VariableValue as LCPVariableValue import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.ConstantValue import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearCombinationEdgeFunction import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact @@ -30,6 +28,8 @@ import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.pro import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.V +import scala.collection.immutable + /** * Extended definition of the linear constant propagation problem, trying to resolve field accesses with the LCP on * fields analysis. @@ -110,13 +110,14 @@ class LinearConstantPropagationProblemExtended extends LinearConstantPropagation private def getArrayElementFromProperty( arrayVar: JavaStatement.V, index: Option[Int] - )(property: Property): LinearConstantPropagationValue = { + )(property: LCPOnFieldsPropertyMetaInformation.Self): LinearConstantPropagationValue = { property - .asInstanceOf[LCPOnFieldsPropertyMetaInformation.Self] .results .filter { case (f: AbstractArrayFact, ArrayValue(_, _)) => arrayVar.definedBy.contains(f.definedAtIndex) + case (f: AbstractArrayFact, LCPVariableValue) => + arrayVar.definedBy.contains(f.definedAtIndex) case _ => false } .map(_._2) @@ -132,6 +133,9 @@ class LinearConstantPropagationProblemExtended extends LinearConstantPropagation } } lattice.meet(value, arrayValue) + + case (_, LCPVariableValue) => + VariableValue } } @@ -181,19 +185,22 @@ class LinearConstantPropagationProblemExtended extends LinearConstantPropagation private def getObjectFieldFromProperty( objectVar: JavaStatement.V, fieldName: String - )(property: Property): LinearConstantPropagationValue = { + )(property: LCPOnFieldsPropertyMetaInformation.Self): LinearConstantPropagationValue = { property - .asInstanceOf[LCPOnFieldsPropertyMetaInformation.Self] .results .filter { case (f: AbstractObjectFact, ObjectValue(values)) => objectVar.definedBy.contains(f.definedAtIndex) && values.contains(fieldName) + case (f: AbstractObjectFact, LCPVariableValue) => + objectVar.definedBy.contains(f.definedAtIndex) case _ => false } .map(_._2) .foldLeft(UnknownValue: LinearConstantPropagationValue) { case (value, ObjectValue(values)) => lattice.meet(value, values(fieldName)) + case (_, LCPVariableValue) => + VariableValue } } @@ -240,19 +247,22 @@ class LinearConstantPropagationProblemExtended extends LinearConstantPropagation private def getStaticFieldFromProperty( objectType: ObjectType, fieldName: String - )(property: Property): LinearConstantPropagationValue = { + )(property: LCPOnFieldsPropertyMetaInformation.Self): LinearConstantPropagationValue = { property - .asInstanceOf[LCPOnFieldsPropertyMetaInformation.Self] .results .filter { case (f: AbstractStaticFieldFact, StaticFieldValue(_)) => f.objectType == objectType && f.fieldName == fieldName + case (f: AbstractStaticFieldFact, LCPVariableValue) => + f.objectType == objectType && f.fieldName == fieldName case _ => false } .map(_._2) .foldLeft(UnknownValue: LinearConstantPropagationValue) { case (value, StaticFieldValue(v)) => lattice.meet(value, v) + case (_, LCPVariableValue) => + VariableValue } } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 333585ba4b..06d54f2895 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -47,6 +47,17 @@ class LinearConstantPropagationProblem .toSet } + override def getAdditionalSeedsEdgeFunction( + stmt: JavaStatement, + fact: LinearConstantPropagationFact, + callee: Method + )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { + fact match { + case VariableFact(_, _) => UnknownValueEdgeFunction + case _ => super.getAdditionalSeedsEdgeFunction(stmt, fact, callee) + } + } + override def getNormalFlowFunction( source: JavaStatement, sourceFact: LinearConstantPropagationFact, From 8dfdb33ac53a033509a982c59d26b14535a7be20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 5 Feb 2025 16:48:28 +0100 Subject: [PATCH 144/167] Fix problem definitions --- .../ArrayReadWriteAcrossMethodsExample.java | 15 ++--- .../ArrayReadWriteConstantExample.java | 9 ++- .../CreateObjectInMethodExample.java | 18 +++++- .../FieldReadWriteAcrossMethodsExample.java | 4 +- .../PropagationAcrossMethodsExample.java | 24 ++++++-- .../org/opalj/ide/solver/IDEAnalysis.scala | 2 +- .../problem/LCPOnFieldsEdgeFunctions.scala | 57 +++++++++++++------ .../problem/LCPOnFieldsProblem.scala | 2 +- ...nearConstantPropagationEdgeFunctions.scala | 48 ++++++++++------ 9 files changed, 121 insertions(+), 58 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteAcrossMethodsExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteAcrossMethodsExample.java index 03b411c78c..b3197a75be 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteAcrossMethodsExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteAcrossMethodsExample.java @@ -1,10 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.lcp_on_fields; -import org.opalj.fpcf.properties.lcp_on_fields.ArrayValue; -import org.opalj.fpcf.properties.lcp_on_fields.ArrayValues; -import org.opalj.fpcf.properties.lcp_on_fields.ConstantArrayElement; -import org.opalj.fpcf.properties.lcp_on_fields.VariableArrayElement; +import org.opalj.fpcf.properties.lcp_on_fields.*; public class ArrayReadWriteAcrossMethodsExample { public void setIndexTo23(int[] arr, int index) { @@ -16,11 +13,11 @@ public void set11To42(int[] arr) { } @ArrayValues({ - @ArrayValue(variable = "lv3", variableElements = { - @VariableArrayElement(index = 0), - @VariableArrayElement(index = 1), - @VariableArrayElement(index = 2), - @VariableArrayElement(index = 3) + @ArrayValue(variable = "lv3", unknownElements = { + @UnknownArrayElement(index = 0), + @UnknownArrayElement(index = 1), + @UnknownArrayElement(index = 2), + @UnknownArrayElement(index = 3) }), @ArrayValue(variable = "lv5", constantElements = { @ConstantArrayElement(index = 11, value = 42) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteConstantExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteConstantExample.java index 53695abc86..c256ccb143 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteConstantExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteConstantExample.java @@ -19,17 +19,24 @@ public class ArrayReadWriteConstantExample { @ConstantArrayElement(index = 1, value = 2), @ConstantArrayElement(index = 2, value = 3), @ConstantArrayElement(index = 3, value = 4) + }), + @ArrayValue(variable = "lv11", constantElements = { + @ConstantArrayElement(index = 0, value = 11), + @ConstantArrayElement(index = 1, value = 12), + @ConstantArrayElement(index = 2, value = 13) }) }) public static void main(String[] args) { int[] arr1 = new int[5]; int[] arr2 = new int[]{1, 2, 3, 4}; + int[] arr3 = new int[]{11, 12, 13}; arr1[2] = 42; arr1[3] = arr2[3]; arr2[0] = arr1[4]; System.out.println("arr1: {" + arr1[0] + ", " + arr1[1] + ", " + arr1[2] + ", " + arr1[3] + ", " + arr1[4] + - "}; arr2: {" + arr2[0] + ", " + arr2[1] + ", " + arr2[2] + ", " + arr2[3] + "}"); + "}; arr2: {" + arr2[0] + ", " + arr2[1] + ", " + arr2[2] + ", " + arr2[3] + "}; arr3: {" + arr3[0] + + ", " + arr3[1] + ", " + arr3[2] + "}"); } } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java index 2341075dd9..0b6866f7d9 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java @@ -15,23 +15,35 @@ private CreateObjectInMethodExample createNew1() { return example; } - @ObjectValue(variable = "lv0", variableValues = {@VariableValue(variable = "a")}) + @ObjectValue(variable = "lv0", constantValues = {@ConstantValue(variable = "a", value = 33)}) private CreateObjectInMethodExample createNew2() { CreateObjectInMethodExample example = new CreateObjectInMethodExample(); example.a = a + 2; return example; } + @ObjectValue(variable = "lv0", variableValues = {@VariableValue(variable = "a")}) + private CreateObjectInMethodExample createNew3() { + CreateObjectInMethodExample example = new CreateObjectInMethodExample(); + example.a = a + 2; + return example; + } + @ObjectValues({ @ObjectValue(variable = "lv0", constantValues = {@ConstantValue(variable = "a", value = 42)}), @ObjectValue(variable = "lv2", constantValues = {@ConstantValue(variable = "a", value = 31)}), - @ObjectValue(variable = "lv3", variableValues = {@VariableValue(variable = "a")}) + @ObjectValue(variable = "lv3", constantValues = {@ConstantValue(variable = "a", value = 33)}), + @ObjectValue(variable = "lv4", variableValues = {@VariableValue(variable = "a")}), + @ObjectValue(variable = "lv5", variableValues = {@VariableValue(variable = "a")}) }) public static void main(String[] args) { CreateObjectInMethodExample example1 = new CreateObjectInMethodExample(); CreateObjectInMethodExample example2 = example1.createNew1(); CreateObjectInMethodExample example3 = example2.createNew2(); + CreateObjectInMethodExample example4 = example3.createNew3(); + CreateObjectInMethodExample example5 = example4.createNew3(); - System.out.println("e1: " + example1.a + ", e2: " + example2.a + ", e3: " + example3.a); + System.out.println("e1: " + example1.a + ", e2: " + example2.a + ", e3: " + example3.a + ", e4: " + + example4.a + ", e5: " + example5.a); } } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java index 88c0d0d3f6..c90f9ab459 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java @@ -4,7 +4,7 @@ import org.opalj.fpcf.properties.lcp_on_fields.ObjectValue; import org.opalj.fpcf.properties.lcp_on_fields.ObjectValues; import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; -import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; +import org.opalj.fpcf.properties.linear_constant_propagation.UnknownValue; public class FieldReadWriteAcrossMethodsExample { private int a = -2; @@ -22,7 +22,7 @@ private int getA() { } @ObjectValues({ - @ObjectValue(variable = "lv0", variableValues = {@VariableValue(variable = "a")}), + @ObjectValue(variable = "lv0", unknownValues = {@UnknownValue(variable = "a")}), @ObjectValue(variable = "lv2", constantValues = {@ConstantValue(variable = "a", value = 42)}), @ObjectValue(variable = "lv4", constantValues = {@ConstantValue(variable = "a", value = -2)}) }) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java index 0a658acb00..bd3f69d1a7 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java @@ -1,10 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.linear_constant_propagation; -import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; -import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValues; -import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; -import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; +import org.opalj.fpcf.properties.linear_constant_propagation.*; public class PropagationAcrossMethodsExample { @VariableValues({ @@ -21,10 +18,23 @@ public static int linearCalculation2(String msg, int a) { return 12 - 4 * 4 + a; } + @ConstantValue(variable = "lv3", value = 139) + public static int linearCalculation3(String msg, int a) { + System.out.println(msg); + return a + 11; + } + + @UnknownValue(variable = "lv3") + public static int linearCalculation4(String msg, int a) { + System.out.println(msg); + return 3 * a; + } + @ConstantValues({ @ConstantValue(variable = "lv5", value = -18), @ConstantValue(variable = "lv8", value = 132), - @ConstantValue(variable = "lva", value = 128) + @ConstantValue(variable = "lva", value = 128), + @ConstantValue(variable = "lv12", value = 139) }) @VariableValues({ @VariableValue(variable = "lvd"), @@ -41,6 +51,8 @@ public static void main(String[] args) { int m = example.linearCalculation1("Fifth call", 12, l); - System.out.println("i: " + i + ", j: " + j + ", k:" + k + ", l: " + l + ", m: " + m); + int n = linearCalculation3("Sixth call", k); + + System.out.println("i: " + i + ", j: " + j + ", k:" + k + ", l: " + l + ", m: " + m + ", n: " + n); } } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index bce678866d..373b9c8199 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -571,7 +571,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent problem.getAdditionalSeedsEdgeFunction(stmt, fact, callable), processAdditionalSeed _ ) - s.setJumpFunction(path, s.getJumpFunction(path).meetWith(edgeFunction)) + s.setJumpFunction(path, edgeFunction.meetWith(s.getJumpFunction(path))) s.enqueuePath(path) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala index 0527ca942c..ecb74cbb2d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -186,18 +186,18 @@ class ArrayEdgeFunction( case NewArrayEdgeFunction(_) => secondEdgeFunction case ArrayEdgeFunction(initValue2, elements2) => - new ArrayEdgeFunction(initValue2, (elements -- elements2.keys) ++ elements2) + ArrayEdgeFunction(initValue2, (elements -- elements2.keys) ++ elements2) case PutElementEdgeFunction(index, value) => index match { case linear_constant_propagation.problem.UnknownValue => /* In this case it is unknown which indices will be affected */ - UnknownValueEdgeFunction + ArrayEdgeFunction(linear_constant_propagation.problem.UnknownValue) case linear_constant_propagation.problem.ConstantValue(i) => - new ArrayEdgeFunction(initValue, (elements - i) + (i -> value)) + ArrayEdgeFunction(initValue, (elements - i) + (i -> value)) case linear_constant_propagation.problem.VariableValue => /* In this case any index could be affected */ - new ArrayEdgeFunction(linear_constant_propagation.problem.VariableValue, immutable.Map.empty) + ArrayEdgeFunction(linear_constant_propagation.problem.VariableValue) } case IdentityEdgeFunction() => this @@ -211,7 +211,7 @@ class ArrayEdgeFunction( override def meetWith(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = otherEdgeFunction match { case ArrayEdgeFunction(initValue2, elements2) => - new ArrayEdgeFunction( + ArrayEdgeFunction( LinearConstantPropagationLattice.meet(initValue, initValue2), elements.keySet.union(elements2.keySet) .map { index => @@ -226,9 +226,9 @@ class ArrayEdgeFunction( case PutElementEdgeFunction(index, value) => index match { case linear_constant_propagation.problem.UnknownValue => - UnknownValueEdgeFunction + ArrayEdgeFunction(linear_constant_propagation.problem.UnknownValue) case linear_constant_propagation.problem.ConstantValue(i) => - new ArrayEdgeFunction( + ArrayEdgeFunction( initValue, (elements - i) + (i -> LinearConstantPropagationLattice.meet( value, @@ -236,7 +236,7 @@ class ArrayEdgeFunction( )) ) case linear_constant_propagation.problem.VariableValue => - new ArrayEdgeFunction(linear_constant_propagation.problem.VariableValue, immutable.Map.empty) + ArrayEdgeFunction(linear_constant_propagation.problem.VariableValue) } case IdentityEdgeFunction() => this @@ -256,6 +256,13 @@ class ArrayEdgeFunction( } object ArrayEdgeFunction { + def apply( + initValue: LinearConstantPropagationValue, + elements: immutable.Map[Int, LinearConstantPropagationValue] = immutable.Map.empty + ): ArrayEdgeFunction = { + new ArrayEdgeFunction(initValue, elements) + } + def unapply(arrayEdgeFunction: ArrayEdgeFunction): Some[( LinearConstantPropagationValue, immutable.Map[Int, LinearConstantPropagationValue] @@ -283,9 +290,11 @@ case class PutElementEdgeFunction( case UnknownValue => UnknownValue case ArrayValue(initValue, elements) => index match { + case linear_constant_propagation.problem.UnknownValue => + ArrayValue(linear_constant_propagation.problem.UnknownValue, immutable.Map.empty) case linear_constant_propagation.problem.ConstantValue(i) => ArrayValue(initValue, (elements - i) + (i -> value)) - case _ => + case linear_constant_propagation.problem.VariableValue => ArrayValue(linear_constant_propagation.problem.VariableValue, immutable.Map.empty) } case VariableValue => VariableValue @@ -302,9 +311,9 @@ case class PutElementEdgeFunction( case ArrayEdgeFunction(initValue, elements) => index match { case linear_constant_propagation.problem.UnknownValue => - UnknownValueEdgeFunction + ArrayEdgeFunction(linear_constant_propagation.problem.UnknownValue) case linear_constant_propagation.problem.ConstantValue(i) => - new ArrayEdgeFunction( + ArrayEdgeFunction( initValue, (elements - i) + (i -> LinearConstantPropagationLattice.meet( value, @@ -312,12 +321,12 @@ case class PutElementEdgeFunction( )) ) case linear_constant_propagation.problem.VariableValue => - new ArrayEdgeFunction(linear_constant_propagation.problem.VariableValue, immutable.Map.empty) + ArrayEdgeFunction(linear_constant_propagation.problem.VariableValue) } case PutElementEdgeFunction(index2, _) if index == index2 => secondEdgeFunction case PutElementEdgeFunction(_, _) => - new ArrayEdgeFunction(linear_constant_propagation.problem.UnknownValue, immutable.Map.empty) + ArrayEdgeFunction(linear_constant_propagation.problem.UnknownValue) .composeWith(this).composeWith(secondEdgeFunction) case IdentityEdgeFunction() => this @@ -334,9 +343,9 @@ case class PutElementEdgeFunction( case ArrayEdgeFunction(initValue, elements) => index match { case linear_constant_propagation.problem.UnknownValue => - UnknownValueEdgeFunction + ArrayEdgeFunction(linear_constant_propagation.problem.UnknownValue) case linear_constant_propagation.problem.ConstantValue(i) => - new ArrayEdgeFunction( + ArrayEdgeFunction( initValue, (elements - i) + (i -> LinearConstantPropagationLattice.meet( value, @@ -344,7 +353,7 @@ case class PutElementEdgeFunction( )) ) case linear_constant_propagation.problem.VariableValue => - new ArrayEdgeFunction(linear_constant_propagation.problem.VariableValue, immutable.Map.empty) + ArrayEdgeFunction(linear_constant_propagation.problem.VariableValue) } case PutElementEdgeFunction(index2, value2) => @@ -426,9 +435,9 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LCPOnFieldsValue](Unk ): EdgeFunction[LCPOnFieldsValue] = { secondEdgeFunction match { case ObjectEdgeFunction(_) => secondEdgeFunction - case PutFieldEdgeFunction(_, _) => secondEdgeFunction + case PutFieldEdgeFunction(_, _) => this case ArrayEdgeFunction(_, _) => secondEdgeFunction - case PutElementEdgeFunction(_, _) => secondEdgeFunction + case PutElementEdgeFunction(_, _) => ArrayEdgeFunction(linear_constant_propagation.problem.UnknownValue) case PutStaticFieldEdgeFunction(_) => secondEdgeFunction case VariableValueEdgeFunction => secondEdgeFunction @@ -442,6 +451,18 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LCPOnFieldsValue](Unk } } + override def meetWith(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = { + otherEdgeFunction match { + case AllTopEdgeFunction(_) => this + case IdentityEdgeFunction() => this + case _ => otherEdgeFunction + } + } + + override def equalTo(otherEdgeFunction: EdgeFunction[LCPOnFieldsValue]): Boolean = { + otherEdgeFunction eq this + } + override def toString: String = "UnknownValueEdgeFunction()" } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index becf17ae4f..060cc6ceb0 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -86,7 +86,7 @@ class LCPOnFieldsProblem( ): EdgeFunctionResult[LCPOnFieldsValue] = { fact match { case ObjectFact(_, _) => UnknownValueEdgeFunction - case ArrayFact(_, _) => UnknownValueEdgeFunction + case ArrayFact(_, _) => ArrayEdgeFunction(linear_constant_propagation.problem.UnknownValue) case f @ StaticFieldFact(_, _) => getEdgeFunctionForStaticFieldFactByImmutability(f) case _ => super.getAdditionalSeedsEdgeFunction(stmt, fact, callee) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala index 872a7c0074..57743c5819 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala @@ -93,23 +93,6 @@ case class LinearCombinationEdgeFunction( } } -/** - * Edge function for a variable that is definitely not constant - */ -object VariableValueEdgeFunction extends AllBottomEdgeFunction[LinearConstantPropagationValue](VariableValue) { - override def composeWith( - secondEdgeFunction: EdgeFunction[LinearConstantPropagationValue] - ): EdgeFunction[LinearConstantPropagationValue] = { - secondEdgeFunction match { - case LinearCombinationEdgeFunction(0, _, _) => secondEdgeFunction - case LinearCombinationEdgeFunction(_, _, _) => this - case _ => this - } - } - - override def toString: String = "VariableValueEdgeFunction()" -} - /** * Edge function for variables whose value is unknown */ @@ -133,5 +116,36 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LinearConstantPropaga } } + override def meetWith( + otherEdgeFunction: EdgeFunction[LinearConstantPropagationValue] + ): EdgeFunction[LinearConstantPropagationValue] = { + otherEdgeFunction match { + case AllTopEdgeFunction(_) => this + case IdentityEdgeFunction() => this + case _ => otherEdgeFunction + } + } + + override def equalTo(otherEdgeFunction: EdgeFunction[LinearConstantPropagationValue]): Boolean = { + otherEdgeFunction eq this + } + override def toString: String = "UnknownValueEdgeFunction()" } + +/** + * Edge function for a variable that is definitely not constant + */ +object VariableValueEdgeFunction extends AllBottomEdgeFunction[LinearConstantPropagationValue](VariableValue) { + override def composeWith( + secondEdgeFunction: EdgeFunction[LinearConstantPropagationValue] + ): EdgeFunction[LinearConstantPropagationValue] = { + secondEdgeFunction match { + case LinearCombinationEdgeFunction(0, _, _) => secondEdgeFunction + case LinearCombinationEdgeFunction(_, _, _) => this + case _ => this + } + } + + override def toString: String = "VariableValueEdgeFunction()" +} From a6a8752dc95931b0c5b8911c5a7296a72b418480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 5 Feb 2025 17:18:53 +0100 Subject: [PATCH 145/167] Fix rare pattern matching bug --- .../src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 373b9c8199..d95cbebd34 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -784,6 +784,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent jumpFunctionsMatchingTarget.foreach { case (((_, d3), (_, _)), f3) if !f3.equalTo(allTopEdgeFunction) => propagate(((sq, d3), (r, d5)), f3.composeWith(fPrime)) + case _ => } } } @@ -922,7 +923,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // IDE P2 part (ii) // IDE P2 lines 15 - 17 - // Reduced to the one callable, results are created for + // Reduced to the callables, results are created for val ps = s.getTargetCallablesWithoutFinalResults val sps = ps.flatMap { p => icfg.getStartStatements(p) } val ns = collectReachableStmts(sps, stmt => !icfg.isCallStatement(stmt)) @@ -935,8 +936,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent case (((_, dPrime), (_, d)), fPrime) if !fPrime.equalTo(allTopEdgeFunction) => val nSharp = (n, d) val vPrime = problem.lattice.meet( - s.getValue((n, d)), fPrime.compute(s.getValue((sp, dPrime))) + s.getValue(nSharp), ) logTrace(s"setting value of nSharp=${nodeToString(nSharp)} to vPrime=$vPrime") @@ -990,6 +991,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent jumpFunctionsMatchingTarget.foreach { case (((_, _), (_, dPrime)), fPrime) if !fPrime.equalTo(allTopEdgeFunction) => propagateValue((c, dPrime), fPrime.compute(s.getValue((n, d)))) + case _ => } } } From 686fa2e8e82f2fcbbdfaf8b56a8e6eab2a0cb0b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 5 Feb 2025 18:12:52 +0100 Subject: [PATCH 146/167] Fix invalid edge function --- .../src/main/scala/org/opalj/ide/problem/EdgeFunction.scala | 4 +--- .../ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 4 +++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala index d38d0ec13e..27e65d85be 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala @@ -41,10 +41,8 @@ case class IdentityEdgeFunction[Value <: IDEValue]() extends EdgeFunction[Value] secondEdgeFunction override def meetWith(otherEdgeFunction: EdgeFunction[Value]): EdgeFunction[Value] = { - if (otherEdgeFunction.equalTo(this) || otherEdgeFunction.isInstanceOf[AllTopEdgeFunction[Value]]) { + if (otherEdgeFunction.equalTo(this)) { this - } else if (otherEdgeFunction.isInstanceOf[AllBottomEdgeFunction[Value]]) { - otherEdgeFunction } else { otherEdgeFunction.meetWith(this) } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index d95cbebd34..92d1c43a04 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -529,6 +529,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent ])(implicit s: State): ProperPropertyComputationResult = { s.processTargetCallablesEOptionP(eps) + logInfo(s"performing ${PropertyKey.name(propertyMetaInformation.key)} for ${s.getTargetCallables.size} callables") + seedPhase1() continuePhase1() performPhase2() @@ -936,8 +938,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent case (((_, dPrime), (_, d)), fPrime) if !fPrime.equalTo(allTopEdgeFunction) => val nSharp = (n, d) val vPrime = problem.lattice.meet( - fPrime.compute(s.getValue((sp, dPrime))) s.getValue(nSharp), + fPrime.compute(s.getValue((sp, dPrime))) ) logTrace(s"setting value of nSharp=${nodeToString(nSharp)} to vPrime=$vPrime") From b7ba93d97f2a6e2d5dd3235502974d8bfdc216a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 5 Feb 2025 18:26:22 +0100 Subject: [PATCH 147/167] Do not produce final results too early --- .../org/opalj/ide/solver/IDEAnalysis.scala | 47 ++++++------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 92d1c43a04..3e04a82739 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -14,7 +14,6 @@ import org.opalj.fpcf.InterimPartialResult import org.opalj.fpcf.PartialResult import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.PropertyKey -import org.opalj.fpcf.Result import org.opalj.fpcf.Results import org.opalj.fpcf.SomeEOptionP import org.opalj.fpcf.SomeEPK @@ -94,11 +93,6 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent processTargetCallablesEOptionP(initialTargetCallablesEOptionP) - /** - * Remember the callables where a final result has already been produced - */ - private val callablesWithFinalResults = mutable.Set.empty[Callable] - /** * The work list for paths used in P1 */ @@ -149,14 +143,6 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent targetCallables } - def getTargetCallablesWithoutFinalResults: collection.Set[Callable] = { - targetCallables.diff(callablesWithFinalResults) - } - - def rememberCallableWithFinalResult(callable: Callable): Unit = { - callablesWithFinalResults.add(callable) - } - def getTargetCallablesEOptionP: EOptionP[ IDEPropertyMetaInformation[Fact, Value, Statement, Callable], IDETargetCallablesProperty[Callable] @@ -465,7 +451,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent ): ProperPropertyComputationResult = { logDebug("starting creation of properties and results") - val callables = s.getTargetCallablesWithoutFinalResults + val callables = s.getTargetCallables val collectedResults = s.collectResults(callables) val callableResults = callables.map { callable => val (resultsByStatement, resultsForExit) = @@ -483,22 +469,17 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent resultsForExit ) - if (s.areDependeesEmpty) { - s.rememberCallableWithFinalResult(callable) - Result(callable, ideRawProperty) - } else { - PartialResult( - callable, - propertyMetaInformation.backingPropertyMetaInformation.key, - { (eOptionP: SomeEOptionP) => - if (eOptionP.hasUBP && eOptionP.ub == ideRawProperty) { - None - } else { - Some(InterimEUBP(callable, ideRawProperty)) - } + PartialResult( + callable, + propertyMetaInformation.backingPropertyMetaInformation.key, + { (eOptionP: SomeEOptionP) => + if (eOptionP.hasUBP && eOptionP.ub == ideRawProperty) { + None + } else { + Some(InterimEUBP(callable, ideRawProperty)) } - ) - } + } + ) } logDebug("finished creation of properties and results") @@ -555,7 +536,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } private def seedPhase1()(implicit s: State): Unit = { - val callables = s.getTargetCallablesWithoutFinalResults + val callables = s.getTargetCallables callables.foreach { callable => // IDE P1 lines 5 - 6 icfg.getStartStatements(callable).foreach { stmt => @@ -886,7 +867,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } private def seedPhase2()(implicit s: State): Unit = { - val callables = s.getTargetCallablesWithoutFinalResults + val callables = s.getTargetCallables callables.foreach { callable => // IDE P2 lines 2 - 3 icfg.getStartStatements(callable).foreach { stmt => @@ -926,7 +907,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // IDE P2 part (ii) // IDE P2 lines 15 - 17 // Reduced to the callables, results are created for - val ps = s.getTargetCallablesWithoutFinalResults + val ps = s.getTargetCallables val sps = ps.flatMap { p => icfg.getStartStatements(p) } val ns = collectReachableStmts(sps, stmt => !icfg.isCallStatement(stmt)) From 5759a8ff6af48c0b9e4cf78caf44a048a3e5afef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 5 Feb 2025 18:30:12 +0100 Subject: [PATCH 148/167] Use smarter seeding algorithm for phase 1 --- .../org/opalj/ide/solver/IDEAnalysis.scala | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 3e04a82739..4cbb6cb061 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -536,17 +536,26 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } private def seedPhase1()(implicit s: State): Unit = { + def propagateSeed(e: Path, f: EdgeFunction[Value]): Unit = { + val oldJumpFunction = s.getJumpFunction(e) + val fPrime = f.meetWith(oldJumpFunction) + + if (!fPrime.equalTo(oldJumpFunction)) { + s.setJumpFunction(e, fPrime) + s.enqueuePath(e) + } + } + val callables = s.getTargetCallables callables.foreach { callable => // IDE P1 lines 5 - 6 icfg.getStartStatements(callable).foreach { stmt => val path = ((stmt, problem.nullFact), (stmt, problem.nullFact)) - s.enqueuePath(path) - s.setJumpFunction(path, identityEdgeFunction) + propagateSeed(path, identityEdgeFunction) problem.getAdditionalSeeds(stmt, callable).foreach { fact => val path = ((stmt, problem.nullFact), (stmt, fact)) - s.setJumpFunction(path, identityEdgeFunction) + propagateSeed(path, identityEdgeFunction) def processAdditionalSeed(): Unit = { val edgeFunction = @@ -554,8 +563,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent problem.getAdditionalSeedsEdgeFunction(stmt, fact, callable), processAdditionalSeed _ ) - s.setJumpFunction(path, edgeFunction.meetWith(s.getJumpFunction(path))) - s.enqueuePath(path) + propagateSeed(path, edgeFunction) } processAdditionalSeed() From e5cb9735984632bafa616e30f16db460b6a0a37d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 7 Feb 2025 15:57:35 +0100 Subject: [PATCH 149/167] Improve comparability of property stringification --- .../main/scala/org/opalj/ide/integration/IDEProperty.scala | 2 +- .../main/scala/org/opalj/ide/integration/IDERawProperty.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala index af339811e7..803b15ed13 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala @@ -24,7 +24,7 @@ class BasicIDEProperty[Fact <: IDEFact, Value <: IDEValue]( override def toString: String = { s"BasicIDEProperty(${PropertyKey.name(key)}, {\n${ - results.map { case (fact, value) => s"\t($fact,$value)" }.mkString("\n") + results.map { case (fact, value) => s"\t($fact,$value)" }.toList.sorted.mkString("\n") }\n})" } diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala index d03f689afe..277dde8880 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala @@ -23,11 +23,11 @@ class IDERawProperty[Fact <: IDEFact, Value <: IDEValue, Statement]( s"IDERawProperty(${PropertyKey.name(key)}, {\n${ stmtResults.map { case (stmt, results) => s"\t$stmt\n${ - results.map { case (fact, value) => s"\t\t($fact,$value)" }.mkString("\n") + results.map { case (fact, value) => s"\t\t($fact,$value)" }.toList.sorted.mkString("\n") }" }.mkString("\n") }\n}, {\n${ - callableResults.map { case (fact, value) => s"\t($fact,$value)" }.mkString("\n") + callableResults.map { case (fact, value) => s"\t($fact,$value)" }.toList.sorted.mkString("\n") }\n})" } From 0041c6fcab7ab58711a150410122a72844ef7025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 7 Feb 2025 17:05:09 +0100 Subject: [PATCH 150/167] Implement incremental phase 2 by tracking changes --- .../org/opalj/ide/solver/IDEAnalysis.scala | 70 +++++++++++++++---- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 4cbb6cb061..fb9fd3a4ad 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -93,6 +93,13 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent processTargetCallablesEOptionP(initialTargetCallablesEOptionP) + /** + * Collection of callables that received changes (compared to the last update) which may affect their final + * value, e.g. a changed jump function. Especially used to reduce computation overhead in phase 2 and to reduce + * amount of created results. + */ + private val callablesWithChanges = mutable.Set.empty[Callable] + /** * The work list for paths used in P1 */ @@ -156,10 +163,25 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent ]): Unit = { targetCallablesEOptionP = newTargetCallablesEOptionP if (targetCallablesEOptionP.hasUBP) { - targetCallables.addAll(targetCallablesEOptionP.ub.targetCallables) + val addedTargetCallables = targetCallablesEOptionP.ub.targetCallables.diff(targetCallables) + targetCallables.addAll(addedTargetCallables) + // Mark new target callables as changed to trigger result creation + callablesWithChanges.addAll(addedTargetCallables) } } + def clearCallablesWithChanges(): Unit = { + callablesWithChanges.clear() + } + + def rememberCallableWithChanges(callable: Callable): Unit = { + callablesWithChanges.add(callable) + } + + def getCallablesWithChanges: collection.Set[Callable] = { + callablesWithChanges + } + def enqueuePath(path: Path): Unit = { pathWorkList.enqueue(path) } @@ -436,8 +458,6 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent private def performPhase2()(implicit s: State): Unit = { logDebug("starting phase 2") - // TODO (IDE) PHASE 2 IS PERFORMED FROM SCRATCH ON EACH UPDATE AT THE MOMENT. CONSIDER IMPLEMENTING AN - // INCREMENTAL SOLUTION (DECIDE WHAT COULD HAVE CHANGED WHILE RUNNING PHASE 1) s.clearValues() seedPhase2() @@ -446,12 +466,25 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logDebug("finished phase 2") } + /** + * Continue phase 2 from based on previous result + */ + private def continuePhase2()(implicit s: State): Unit = { + logDebug("continuing phase 2") + + seedPhase2() + computeValues() + + logDebug("finished phase 2") + } + private def createResult()( implicit s: State ): ProperPropertyComputationResult = { logDebug("starting creation of properties and results") - val callables = s.getTargetCallables + // Only create results for target callables whose values could have changed + val callables = s.getCallablesWithChanges.intersect(s.getTargetCallables) val collectedResults = s.collectResults(callables) val callableResults = callables.map { callable => val (resultsByStatement, resultsForExit) = @@ -485,7 +518,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent logDebug("finished creation of properties and results") Results( - Seq( + callableResults ++ Seq( InterimPartialResult( None, s.getDependees.toSet ++ immutable.Set(s.getTargetCallablesEOptionP), @@ -500,7 +533,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } } ) - ) ++ callableResults + ) ) } @@ -508,13 +541,15 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent IDEPropertyMetaInformation[Fact, Value, Statement, Callable], IDETargetCallablesProperty[Callable] ])(implicit s: State): ProperPropertyComputationResult = { + s.clearCallablesWithChanges() + s.processTargetCallablesEOptionP(eps) logInfo(s"performing ${PropertyKey.name(propertyMetaInformation.key)} for ${s.getTargetCallables.size} callables") seedPhase1() continuePhase1() - performPhase2() + continuePhase2() createResult() } @@ -522,6 +557,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent private def onDependeeUpdateContinuation(eps: SomeEPS)( implicit s: State ): ProperPropertyComputationResult = { + s.clearCallablesWithChanges() + // Get and remove all continuations that are remembered for the EPS val cs = s.getAndRemoveDependeeContinuations(eps) @@ -530,19 +567,20 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // The continuations can have enqueued paths to the path work list continuePhase1() - performPhase2() + continuePhase2() createResult() } private def seedPhase1()(implicit s: State): Unit = { - def propagateSeed(e: Path, f: EdgeFunction[Value]): Unit = { + def propagateSeed(e: Path, callable: Callable, f: EdgeFunction[Value]): Unit = { val oldJumpFunction = s.getJumpFunction(e) val fPrime = f.meetWith(oldJumpFunction) if (!fPrime.equalTo(oldJumpFunction)) { s.setJumpFunction(e, fPrime) s.enqueuePath(e) + s.rememberCallableWithChanges(callable) } } @@ -551,11 +589,11 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // IDE P1 lines 5 - 6 icfg.getStartStatements(callable).foreach { stmt => val path = ((stmt, problem.nullFact), (stmt, problem.nullFact)) - propagateSeed(path, identityEdgeFunction) + propagateSeed(path, callable, identityEdgeFunction) problem.getAdditionalSeeds(stmt, callable).foreach { fact => val path = ((stmt, problem.nullFact), (stmt, fact)) - propagateSeed(path, identityEdgeFunction) + propagateSeed(path, callable, identityEdgeFunction) def processAdditionalSeed(): Unit = { val edgeFunction = @@ -563,7 +601,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent problem.getAdditionalSeedsEdgeFunction(stmt, fact, callable), processAdditionalSeed _ ) - propagateSeed(path, edgeFunction) + propagateSeed(path, callable, edgeFunction) } processAdditionalSeed() @@ -816,6 +854,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent s.setJumpFunction(e, fPrime) s.enqueuePath(e) + s.rememberCallableWithChanges(icfg.getCallable(e._2._1)) } else { logTrace(s"nothing to do as oldJumpFunction=$oldJumpFunction == fPrime=$fPrime") } @@ -875,7 +914,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } private def seedPhase2()(implicit s: State): Unit = { - val callables = s.getTargetCallables + val callables = s.getCallablesWithChanges callables.foreach { callable => // IDE P2 lines 2 - 3 icfg.getStartStatements(callable).foreach { stmt => @@ -914,8 +953,8 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // IDE P2 part (ii) // IDE P2 lines 15 - 17 - // Reduced to the callables, results are created for - val ps = s.getTargetCallables + // Reduced to the callables whose values could have changed + val ps = s.getCallablesWithChanges val sps = ps.flatMap { p => icfg.getStartStatements(p) } val ns = collectReachableStmts(sps, stmt => !icfg.isCallStatement(stmt)) @@ -1022,6 +1061,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent s.setValue(nSharp, vPrime) s.enqueueNode(nSharp) + s.rememberCallableWithChanges(icfg.getCallable(nSharp._1)) } else { logTrace(s"nothing to do as oldValue=$oldValue == vPrime=$vPrime") } From c6cd5f296e69bb8647b8ac3f96b7c8ee281f6f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Fri, 7 Feb 2025 17:29:47 +0100 Subject: [PATCH 151/167] Fix linting issue --- .../problem/LinearConstantPropagationProblemExtended.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala index c6a5bedb93..59396b843a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala @@ -13,7 +13,7 @@ import org.opalj.tac.ArrayLoad import org.opalj.tac.GetField import org.opalj.tac.GetStatic import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsPropertyMetaInformation -import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.VariableValue as LCPVariableValue +import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.{VariableValue => LCPVariableValue} import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.ConstantValue import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearCombinationEdgeFunction import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact From 6e740a54d0e1b6d5d1dcceb4c0af2ed4408629c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Tue, 11 Feb 2025 13:13:53 +0100 Subject: [PATCH 152/167] Improve handling of static fields in LCP on fields --- .../lcp_on_fields/problem/LCPOnFieldsProblem.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 060cc6ceb0..f1d19b89a1 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -418,7 +418,12 @@ class LCPOnFieldsProblem( } case f: AbstractStaticFieldFact => - immutable.Set(f.toStaticFieldFact) + /* Only propagate fact if the caller can access the corresponding static field */ + if (returnSite.method.classFile.thisType == f.objectType) { + immutable.Set(f.toStaticFieldFact) + } else { + immutable.Set.empty + } } } } From f05001370950a6bea7607f573550fc9fe4709de2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 12 Feb 2025 16:41:58 +0100 Subject: [PATCH 153/167] Memory usage improvements --- .../org/opalj/ide/solver/IDEAnalysis.scala | 85 +++++++++++++------ 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index fb9fd3a4ad..6a72b2571f 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -955,26 +955,28 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // IDE P2 lines 15 - 17 // Reduced to the callables whose values could have changed val ps = s.getCallablesWithChanges - val sps = ps.flatMap { p => icfg.getStartStatements(p) } - val ns = collectReachableStmts(sps, stmt => !icfg.isCallStatement(stmt)) + ps.foreach { p => + val sps = icfg.getStartStatements(p) + val ns = collectReachableStmts(sps, stmt => !icfg.isCallStatement(stmt)) - // IDE P2 line 16 - 17 - sps.foreach { sp => + // IDE P2 line 16 - 17 ns.foreach { n => - val jumpFunctionsMatchingTarget = s.lookupJumpFunctions(source = Some(sp), target = Some(n)) - jumpFunctionsMatchingTarget.foreach { - case (((_, dPrime), (_, d)), fPrime) if !fPrime.equalTo(allTopEdgeFunction) => - val nSharp = (n, d) - val vPrime = problem.lattice.meet( - s.getValue(nSharp), - fPrime.compute(s.getValue((sp, dPrime))) - ) + sps.foreach { sp => + val jumpFunctionsMatchingTarget = s.lookupJumpFunctions(source = Some(sp), target = Some(n)) + jumpFunctionsMatchingTarget.foreach { + case (((_, dPrime), (_, d)), fPrime) if !fPrime.equalTo(allTopEdgeFunction) => + val nSharp = (n, d) + val vPrime = problem.lattice.meet( + s.getValue(nSharp), + fPrime.compute(s.getValue((sp, dPrime))) + ) - logTrace(s"setting value of nSharp=${nodeToString(nSharp)} to vPrime=$vPrime") + logTrace(s"setting value of nSharp=${nodeToString(nSharp)} to vPrime=$vPrime") - s.setValue(nSharp, vPrime) + s.setValue(nSharp, vPrime) - case _ => + case _ => + } } } } @@ -990,20 +992,47 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent private def collectReachableStmts( originStmts: collection.Set[Statement], filterPredicate: Statement => Boolean - ): collection.Set[Statement] = { - val collectedStmts = mutable.Set.empty[Statement] - var workingStmts = originStmts - val seenStmts = mutable.Set.empty[Statement] - - while (workingStmts.nonEmpty) { - collectedStmts.addAll(workingStmts.filter(filterPredicate)) - seenStmts.addAll(workingStmts) - workingStmts = workingStmts.foldLeft(mutable.Set.empty[Statement]) { (nextStmts, stmt) => - nextStmts.addAll(icfg.getNextStatements(stmt)) - }.diff(seenStmts) - } + ): Iterator[Statement] = { + new Iterator[Statement]() { + private val collectedStmts = mutable.Set.empty[Statement] + private val seenStmts = mutable.Set.empty[Statement] + + collectedStmts.addAll(originStmts.filter(filterPredicate)) + seenStmts.addAll(originStmts) + originStmts.filterNot(filterPredicate).foreach { stmt => processStatement(stmt) } + + private def processStatement(stmt: Statement): Unit = { + val workingStmts = mutable.Queue(stmt) + + while (workingStmts.nonEmpty) { + icfg.getNextStatements(workingStmts.dequeue()) + .foreach { followingStmt => + if (!seenStmts.contains(followingStmt)) { + seenStmts.add(followingStmt) + + if (filterPredicate(followingStmt)) { + collectedStmts.add(followingStmt) + } else { + workingStmts.enqueue(followingStmt) + } + } + } + } + } + + override def hasNext: Boolean = { + collectedStmts.nonEmpty + } + + override def next(): Statement = { + val stmt = collectedStmts.head + collectedStmts.remove(stmt) - collectedStmts + processStatement(stmt) + + stmt + } + } } private def processStartNode(node: Node)(implicit s: State): Unit = { From 046c6a59880911f16d175cbae48e8e8405d2362d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 12 Feb 2025 18:16:39 +0100 Subject: [PATCH 154/167] Improve value handling --- .../org/opalj/ide/solver/IDEAnalysis.scala | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 6a72b2571f..7505e16a6b 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -138,7 +138,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent /** * Store all calculated (intermediate) values */ - private val values: Values = mutable.Map.empty + private val values: mutable.Map[Callable, Values] = mutable.Map.empty /** * Map outstanding EPKs to the last processed property result and the continuations to be executed when a new @@ -300,12 +300,20 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent nodeWorkList.size } + def getValue(node: Node, callable: Callable): Value = { + values.getOrElse(callable, mutable.Map.empty).getOrElse(node, problem.lattice.top) // else part handles IDE line 1 + } + def getValue(node: Node): Value = { - values.getOrElse(node, problem.lattice.top) // else part handles IDE line 1 + getValue(node, icfg.getCallable(node._1)) + } + + def setValue(node: Node, newValue: Value, callable: Callable): Unit = { + values.getOrElseUpdate(callable, { mutable.Map.empty }).put(node, newValue) } def setValue(node: Node, newValue: Value): Unit = { - values.put(node, newValue) + setValue(node, newValue, icfg.getCallable(node._1)) } def clearValues(): Unit = { @@ -320,13 +328,14 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent Callable, (collection.Map[Statement, collection.Set[(Fact, Value)]], collection.Set[(Fact, Value)]) ] = { - val relevantValuesByCallable = values - .filter { case ((n, d), _) => - callables.contains(icfg.getCallable(n)) && d != problem.nullFact - }.groupBy { case ((n, _), _) => icfg.getCallable(n) } - - relevantValuesByCallable.map { case (callable, relevantValues) => - val resultsByStatement = relevantValues + val valuesByCallable = callables.map { callable => + callable -> values.getOrElse(callable, immutable.Map.empty[Node, Value]) + }.toMap + + valuesByCallable.map { case (callable, values) => + val resultsByStatement = values + .view + .filterKeys { case (n, d) => d != problem.nullFact } .groupMap(_._1._1) { case ((_, d), value) => (d, value) } .map { case (n, dValuePairs) => ( @@ -920,7 +929,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent icfg.getStartStatements(callable).foreach { stmt => val node = (stmt, problem.nullFact) s.enqueueNode(node) - s.setValue(node, problem.lattice.bottom) + s.setValue(node, problem.lattice.bottom, callable) } } @@ -967,13 +976,13 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent case (((_, dPrime), (_, d)), fPrime) if !fPrime.equalTo(allTopEdgeFunction) => val nSharp = (n, d) val vPrime = problem.lattice.meet( - s.getValue(nSharp), - fPrime.compute(s.getValue((sp, dPrime))) + s.getValue(nSharp, p), + fPrime.compute(s.getValue((sp, dPrime), p)) ) logTrace(s"setting value of nSharp=${nodeToString(nSharp)} to vPrime=$vPrime") - s.setValue(nSharp, vPrime) + s.setValue(nSharp, vPrime, p) case _ => } @@ -1081,16 +1090,18 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent private def propagateValue(nSharp: Node, v: Value)(implicit s: State): Unit = { logTrace(s"handling propagation for nSharp=${nodeToString(nSharp)} and v=$v") + val callable = icfg.getCallable(nSharp._1) + // IDE P2 lines 18 - 21 - val oldValue = s.getValue(nSharp) + val oldValue = s.getValue(nSharp, callable) val vPrime = problem.lattice.meet(v, oldValue) if (vPrime != oldValue) { logTrace(s"updating and re-enqueuing node as oldValue=$oldValue != vPrime=$vPrime") - s.setValue(nSharp, vPrime) + s.setValue(nSharp, vPrime, callable) s.enqueueNode(nSharp) - s.rememberCallableWithChanges(icfg.getCallable(nSharp._1)) + s.rememberCallableWithChanges(callable) } else { logTrace(s"nothing to do as oldValue=$oldValue == vPrime=$vPrime") } From 291ff027cc77aee29785d21727ed6d25959faea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 12 Feb 2025 18:47:50 +0100 Subject: [PATCH 155/167] Memory usage improvements --- .../org/opalj/ide/solver/IDEAnalysis.scala | 85 +++++++------------ 1 file changed, 29 insertions(+), 56 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 7505e16a6b..6e767312d7 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -52,7 +52,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent private type PathWorkList = mutable.Queue[Path] private type JumpFunction = EdgeFunction[Value] - private type JumpFunctions = mutable.Map[Path, JumpFunction] + private type JumpFunctions = mutable.Map[(Statement, Statement), mutable.Map[(Fact, Fact), JumpFunction]] private type SummaryFunction = EdgeFunction[Value] private type SummaryFunctions = mutable.Map[Path, SummaryFunction] @@ -109,10 +109,6 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent * The jump functions (incrementally calculated) in P1 */ private val jumpFunctions: JumpFunctions = mutable.Map.empty - /** - * Index-like structure for faster access of jump functions map - */ - private val jumpFunctionSFTFByST = mutable.Map.empty[(Statement, Statement), mutable.Set[(Fact, Fact)]] /** * The summary functions (incrementally calculated) in P1 @@ -199,59 +195,36 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } def setJumpFunction(path: Path, jumpFunction: JumpFunction): Unit = { - jumpFunctions.put(path, jumpFunction) - val ((source, sourceFact), (target, targetFact)) = path - jumpFunctionSFTFByST - .getOrElseUpdate((source, target), { mutable.Set.empty }) - .add((sourceFact, targetFact)) + jumpFunctions + .getOrElseUpdate((source, target), { mutable.Map.empty }) + .put((sourceFact, targetFact), jumpFunction) } def getJumpFunction(path: Path): JumpFunction = { - jumpFunctions.getOrElse(path, allTopEdgeFunction) // else part handles IDE lines 1 - 2 + val ((source, sourceFact), (target, targetFact)) = path + jumpFunctions + .getOrElse((source, target), immutable.Map.empty[(Fact, Fact), JumpFunction]) + .getOrElse((sourceFact, targetFact), allTopEdgeFunction) // else part handles IDE lines 1 - 2 } def lookupJumpFunctions( - source: Option[Statement] = None, - sourceFact: Option[Fact] = None, - target: Option[Statement] = None, - targetFact: Option[Fact] = None - ): collection.Map[Path, JumpFunction] = { - ((source, sourceFact), (target, targetFact)) match { - case ((Some(s), None), (Some(t), None)) => - jumpFunctionSFTFByST.getOrElse((s, t), immutable.Set.empty[(Fact, Fact)]) - .map { case (sF, tF) => - val path = ((s, sF), (t, tF)) - path -> jumpFunctions(path) - } - .toMap - - case ((Some(s), None), (Some(t), Some(tF))) => - jumpFunctionSFTFByST.getOrElse((s, t), immutable.Set.empty[(Fact, Fact)]) - .filter { case (_, tF2) => tF2 == tF } - .map { case (sF, _) => - val path = ((s, sF), (t, tF)) - path -> jumpFunctions(path) - } - .toMap - - case ((Some(s), Some(sF)), (Some(t), None)) => - jumpFunctionSFTFByST.getOrElse((s, t), immutable.Set.empty[(Fact, Fact)]) - .filter { case (sF2, _) => sF2 == sF } - .map { case (_, tF) => - val path = ((s, sF), (t, tF)) - path -> jumpFunctions(path) - } - .toMap - + source: Statement, + sourceFactOption: Option[Fact] = None, + target: Statement, + targetFactOption: Option[Fact] = None + ): collection.Map[(Fact, Fact), JumpFunction] = { + val subMap = jumpFunctions.getOrElse((source, target), immutable.Map.empty[(Fact, Fact), JumpFunction]) + + (sourceFactOption, targetFactOption) match { + case (Some(sourceFact), Some(targetFact)) => + subMap.filter { case ((sF, tF), _) => sF == sourceFact && tF == targetFact } + case (Some(sourceFact), None) => + subMap.filter { case ((sF, _), _) => sF == sourceFact } + case (None, Some(targetFact)) => + subMap.filter { case ((_, tF), _) => tF == targetFact } case _ => - jumpFunctions.filter { - case (((s, sf), (t, tf)), _) => - source.forall { source => s == source } && - sourceFact.forall { sourceFact => sf == sourceFact } && - target.forall { target => t == target } && - targetFact.forall { targetFact => tf == targetFact } - } + subMap } } @@ -818,9 +791,9 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent val sqs = icfg.getStartStatements(icfg.getCallable(c)) sqs.foreach { sq => val jumpFunctionsMatchingTarget = - s.lookupJumpFunctions(source = Some(sq), target = Some(c), targetFact = Some(d4)) + s.lookupJumpFunctions(source = sq, target = c, targetFactOption = Some(d4)) jumpFunctionsMatchingTarget.foreach { - case (((_, d3), (_, _)), f3) if !f3.equalTo(allTopEdgeFunction) => + case ((d3, _), f3) if !f3.equalTo(allTopEdgeFunction) => propagate(((sq, d3), (r, d5)), f3.composeWith(fPrime)) case _ => } @@ -971,9 +944,9 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // IDE P2 line 16 - 17 ns.foreach { n => sps.foreach { sp => - val jumpFunctionsMatchingTarget = s.lookupJumpFunctions(source = Some(sp), target = Some(n)) + val jumpFunctionsMatchingTarget = s.lookupJumpFunctions(source = sp, target = n) jumpFunctionsMatchingTarget.foreach { - case (((_, dPrime), (_, d)), fPrime) if !fPrime.equalTo(allTopEdgeFunction) => + case ((dPrime, d), fPrime) if !fPrime.equalTo(allTopEdgeFunction) => val nSharp = (n, d) val vPrime = problem.lattice.meet( s.getValue(nSharp, p), @@ -1055,9 +1028,9 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // IDE P2 lines 9 - 10 cs.foreach { c => val jumpFunctionsMatchingTarget = - s.lookupJumpFunctions(source = Some(n), sourceFact = Some(d), target = Some(c)) + s.lookupJumpFunctions(source = n, sourceFactOption = Some(d), target = c) jumpFunctionsMatchingTarget.foreach { - case (((_, _), (_, dPrime)), fPrime) if !fPrime.equalTo(allTopEdgeFunction) => + case ((_, dPrime), fPrime) if !fPrime.equalTo(allTopEdgeFunction) => propagateValue((c, dPrime), fPrime.compute(s.getValue((n, d)))) case _ => } From 3a733953bf18e35dfda064b20e30c9e5da0a51dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 12 Feb 2025 21:16:47 +0100 Subject: [PATCH 156/167] Improve comparability of property stringification --- .../instances/lcp_on_fields/problem/LCPOnFieldsValue.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala index e6c2beaed0..b527a5560e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala @@ -20,7 +20,8 @@ case object UnknownValue extends LCPOnFieldsValue * Value representing the state of an object */ case class ObjectValue(values: immutable.Map[String, LinearConstantPropagationValue]) extends LCPOnFieldsValue { - override def toString: String = s"ObjectValue(${values.mkString(", ")})" + override def toString: String = + s"ObjectValue(${values.toSeq.sortBy(_._1).map { case (fieldName, value) => s"$fieldName -> $value" }.mkString(", ")})" } /** @@ -30,7 +31,8 @@ case class ArrayValue( initValue: LinearConstantPropagationValue, elements: immutable.Map[Int, LinearConstantPropagationValue] ) extends LCPOnFieldsValue { - override def toString: String = s"ArrayValue($initValue, ${elements.mkString(", ")})" + override def toString: String = + s"ArrayValue($initValue, ${elements.toSeq.sortBy(_._1).map { case (index, value) => s"$index -> $value" }.mkString(", ")})" } /** From e9de3ca4632529b54d29384302a207a713ec4ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 12 Feb 2025 21:17:19 +0100 Subject: [PATCH 157/167] Memory usage/performance improvements --- OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 6e767312d7..6b535ba1d4 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -936,7 +936,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent // IDE P2 part (ii) // IDE P2 lines 15 - 17 // Reduced to the callables whose values could have changed - val ps = s.getCallablesWithChanges + val ps = s.getCallablesWithChanges.intersect(s.getTargetCallables) ps.foreach { p => val sps = icfg.getStartStatements(p) val ns = collectReachableStmts(sps, stmt => !icfg.isCallStatement(stmt)) From a63d83893a173f97ee080408206eb8f8430124e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Sat, 15 Feb 2025 19:17:33 +0100 Subject: [PATCH 158/167] Make ICFG more robust --- .../org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala index ddb41abf6e..885962b5ac 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala @@ -1,6 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ide.solver +import scala.collection.immutable import scala.collection.mutable import org.opalj.br.Method @@ -68,6 +69,9 @@ abstract class JavaBaseICFG(project: SomeProject) extends JavaICFG { override def getCallees(javaStmt: JavaStatement): collection.Set[Method] = { val caller = declaredMethods(javaStmt.method) + if (caller == null) { + return immutable.Set.empty + } val calleesEOptionP = propertyStore(caller, Callees.key) calleesEOptionP match { case FinalP(callees) => From 101950696e07a42170eed5937a51e05501f16c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 17 Feb 2025 15:20:38 +0100 Subject: [PATCH 159/167] Fix LCP on fields problem definition --- .../problem/LCPOnFieldsEdgeFunctions.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala index ecb74cbb2d..a980052507 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -434,11 +434,11 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LCPOnFieldsValue](Unk secondEdgeFunction: EdgeFunction[LCPOnFieldsValue] ): EdgeFunction[LCPOnFieldsValue] = { secondEdgeFunction match { - case ObjectEdgeFunction(_) => secondEdgeFunction - case PutFieldEdgeFunction(_, _) => this - case ArrayEdgeFunction(_, _) => secondEdgeFunction - case PutElementEdgeFunction(_, _) => ArrayEdgeFunction(linear_constant_propagation.problem.UnknownValue) - case PutStaticFieldEdgeFunction(_) => secondEdgeFunction + case ObjectEdgeFunction(_) => secondEdgeFunction + case PutFieldEdgeFunction(fieldName, value) => ObjectEdgeFunction(immutable.Map(fieldName -> value)) + case ArrayEdgeFunction(_, _) => secondEdgeFunction + case PutElementEdgeFunction(_, _) => ArrayEdgeFunction(linear_constant_propagation.problem.UnknownValue) + case PutStaticFieldEdgeFunction(_) => secondEdgeFunction case VariableValueEdgeFunction => secondEdgeFunction case UnknownValueEdgeFunction => secondEdgeFunction From 047312fac0979cdee698a40d0399060de0127e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 17 Feb 2025 18:36:36 +0100 Subject: [PATCH 160/167] Fix LCP extended problem definition --- ...inearConstantPropagationProblemExtended.scala | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala index 59396b843a..83f83df473 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala @@ -1,6 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem +import scala.collection.immutable + import org.opalj.ai.domain.l1.DefaultIntegerRangeValues import org.opalj.br.ObjectType import org.opalj.fpcf.FinalP @@ -28,8 +30,6 @@ import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.pro import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.V -import scala.collection.immutable - /** * Extended definition of the linear constant propagation problem, trying to resolve field accesses with the LCP on * fields analysis. @@ -42,14 +42,16 @@ class LinearConstantPropagationProblemExtended extends LinearConstantPropagation sourceFact: LinearConstantPropagationFact, target: JavaStatement ): Boolean = { + val arrayVar = arrayLoadExpr.arrayRef.asVar + /* Generate fact only if the variable represented by the source fact is one possible initializer of the index * variable */ sourceFact match { - case NullFact => false + case NullFact => + arrayVar.value.asReferenceValue.asReferenceType.asArrayType.componentType.isIntegerType + case VariableFact(_, definedAtIndex) => - val arrayVar = arrayLoadExpr.arrayRef.asVar val indexVar = arrayLoadExpr.index.asVar - indexVar.definedBy.contains(definedAtIndex) && arrayVar.value.asReferenceValue.asReferenceType.asArrayType.componentType.isIntegerType } @@ -63,6 +65,10 @@ class LinearConstantPropagationProblemExtended extends LinearConstantPropagation target: JavaStatement, targetFact: LinearConstantPropagationFact )(implicit propertyStore: PropertyStore): EdgeFunctionResult[LinearConstantPropagationValue] = { + if (sourceFact == nullFact) { + return UnknownValueEdgeFunction + } + val arrayVar = arrayLoadExpr.arrayRef.asVar val index = arrayLoadExpr.index.asVar.value match { From c9cb861ec08b317ef45d8ac1c54883d97a8c9e2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Tue, 18 Feb 2025 18:48:19 +0100 Subject: [PATCH 161/167] Fix LCP problem definition --- .../problem/LCPOnFieldsEdgeFunctions.scala | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala index a980052507..7ddec83e03 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -48,16 +48,21 @@ case class ObjectEdgeFunction( case ObjectEdgeFunction(values2) => ObjectEdgeFunction( values.keySet - .intersect(values2.keySet) + .union(values2.keySet) .map { fieldName => - fieldName -> LinearConstantPropagationLattice.meet(values(fieldName), values2(fieldName)) + fieldName -> LinearConstantPropagationLattice.meet( + values.getOrElse(fieldName, linear_constant_propagation.problem.VariableValue), + values2.getOrElse(fieldName, linear_constant_propagation.problem.VariableValue) + ) } .toMap ) case PutFieldEdgeFunction(fieldName, value) => ObjectEdgeFunction( - (values - fieldName) + + (values - fieldName).map { case (fieldName2, _) => + fieldName2 -> linear_constant_propagation.problem.VariableValue + } + (fieldName -> LinearConstantPropagationLattice.meet( value, values.getOrElse(fieldName, linear_constant_propagation.problem.VariableValue) @@ -126,13 +131,12 @@ case class PutFieldEdgeFunction( otherEdgeFunction match { case ObjectEdgeFunction(values2) => ObjectEdgeFunction( - (values2 - fieldName) + + (values2 - fieldName).map { case (fieldName2, _) => + fieldName2 -> linear_constant_propagation.problem.VariableValue + } + (fieldName -> LinearConstantPropagationLattice.meet( value, - values2.getOrElse( - fieldName, - linear_constant_propagation.problem.VariableValue - ) + values2.getOrElse(fieldName, linear_constant_propagation.problem.VariableValue) )) ) @@ -229,8 +233,8 @@ class ArrayEdgeFunction( ArrayEdgeFunction(linear_constant_propagation.problem.UnknownValue) case linear_constant_propagation.problem.ConstantValue(i) => ArrayEdgeFunction( - initValue, - (elements - i) + (i -> LinearConstantPropagationLattice.meet( + linear_constant_propagation.problem.VariableValue, + immutable.Map(i -> LinearConstantPropagationLattice.meet( value, elements.getOrElse(i, initValue) )) @@ -346,8 +350,8 @@ case class PutElementEdgeFunction( ArrayEdgeFunction(linear_constant_propagation.problem.UnknownValue) case linear_constant_propagation.problem.ConstantValue(i) => ArrayEdgeFunction( - initValue, - (elements - i) + (i -> LinearConstantPropagationLattice.meet( + linear_constant_propagation.problem.VariableValue, + immutable.Map(i -> LinearConstantPropagationLattice.meet( value, elements.getOrElse(i, initValue) )) From 719633939c8335f4e01ef72c29a5a3135b344efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 12 Mar 2025 15:46:32 +0100 Subject: [PATCH 162/167] Fix sbt config --- build.sbt | 1 + 1 file changed, 1 insertion(+) diff --git a/build.sbt b/build.sbt index 4b1d691433..379a5b336c 100644 --- a/build.sbt +++ b/build.sbt @@ -176,6 +176,7 @@ lazy val `OPAL` = (project in file(".")) ba, ai, ifds, + ide, tac, de, av, From a648b2310402eab54822bf1c846457b52ee00334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 12 Mar 2025 15:46:51 +0100 Subject: [PATCH 163/167] Run scalafmt --- .../main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala index 927082f989..dec7d63094 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala @@ -19,6 +19,6 @@ case class FinalEdgeFunction[Value <: IDEValue](edgeFunction: EdgeFunction[Value * the final edge function) */ case class InterimEdgeFunction[Value <: IDEValue]( - interimEdgeFunction: EdgeFunction[Value], - dependees: collection.Set[SomeEOptionP] + interimEdgeFunction: EdgeFunction[Value], + dependees: collection.Set[SomeEOptionP] ) extends EdgeFunctionResult[Value] From 17f602900198e51bce6366b42b239ad9ef04ee38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 12 Mar 2025 16:53:49 +0100 Subject: [PATCH 164/167] Adjust package declarations --- .../opalj/fpcf/ide/IDEPropertiesTest.scala | 5 +++-- .../LCPOnFieldsAnalysisScheduler.scala | 5 ++++- .../ide/lcp_on_fields/LCPOnFieldsTests.scala | 7 ++++--- ...PropagationAnalysisSchedulerExtended.scala | 5 ++++- ...ConstantPropagationAnalysisScheduler.scala | 5 ++++- .../LinearConstantPropagationTests.scala | 6 ++++-- .../AbstractRepeatablePropertyMatcher.scala | 5 +++-- .../lcp_on_fields/LCPOnFieldsMatcher.scala | 8 ++++---- .../LinearConstantPropagationMatcher.scala | 7 ++++--- .../IFDSPropertyMetaInformation.scala | 5 ++++- .../ide/ifds/problem/IFDSEdgeFunctions.scala | 5 ++++- .../opalj/ide/ifds/problem/IFDSLattice.scala | 5 ++++- .../opalj/ide/ifds/problem/IFDSProblem.scala | 5 ++++- .../opalj/ide/ifds/problem/IFDSValue.scala | 5 ++++- .../BaseIDEAnalysisProxyScheduler.scala | 4 +++- .../EagerIDEAnalysisProxyScheduler.scala | 4 +++- .../FlowRecordingAnalysisScheduler.scala | 4 +++- .../integration/IDEAnalysisScheduler.scala | 4 +++- .../opalj/ide/integration/IDEProperty.scala | 6 +++++- .../IDEPropertyMetaInformation.scala | 6 +++++- .../ide/integration/IDERawProperty.scala | 6 +++++- .../IDERawPropertyMetaInformation.scala | 4 +++- .../IDETargetCallablesProperty.scala | 6 +++++- ...rgetCallablesPropertyMetaInformation.scala | 4 +++- .../LazyIDEAnalysisProxyScheduler.scala | 4 +++- .../org/opalj/ide/problem/EdgeFunction.scala | 4 +++- .../ide/problem/EdgeFunctionResult.scala | 6 +++++- .../org/opalj/ide/problem/FlowFunction.scala | 5 ++++- .../ide/problem/FlowRecordingIDEProblem.scala | 5 ++++- .../scala/org/opalj/ide/problem/IDEFact.scala | 4 +++- .../org/opalj/ide/problem/IDEProblem.scala | 5 ++++- .../org/opalj/ide/problem/IDEValue.scala | 4 +++- .../org/opalj/ide/problem/MeetLattice.scala | 4 +++- .../scala/org/opalj/ide/solver/ICFG.scala | 6 +++++- .../org/opalj/ide/solver/IDEAnalysis.scala | 5 ++++- .../opalj/ide/solver/IDEAnalysisProxy.scala | 4 +++- .../scala/org/opalj/ide/util/Logging.scala | 7 +++---- .../JavaIFDSAnalysisScheduler.scala | 8 +++++++- .../JavaIFDSPropertyMetaInformation.scala | 8 +++++++- .../ide/ifds/problem/JavaIFDSProblem.scala | 8 +++++++- .../LCPOnFieldsAnalysisScheduler.scala | 8 +++++++- .../LCPOnFieldsPropertyMetaInformation.scala | 8 +++++++- ...PropagationAnalysisSchedulerExtended.scala | 8 +++++++- .../problem/LCPOnFieldsEdgeFunctions.scala | 9 ++++++++- .../problem/LCPOnFieldsFact.scala | 9 ++++++++- .../problem/LCPOnFieldsLattice.scala | 9 ++++++++- .../problem/LCPOnFieldsProblem.scala | 18 ++++++++++-------- .../problem/LCPOnFieldsValue.scala | 9 ++++++++- ...arConstantPropagationProblemExtended.scala | 12 ++++++++---- ...ConstantPropagationAnalysisScheduler.scala | 8 +++++++- ...ntPropagationPropertyMetaInformation.scala | 8 +++++++- ...nearConstantPropagationEdgeFunctions.scala | 9 ++++++++- .../LinearConstantPropagationFact.scala | 9 ++++++++- .../LinearConstantPropagationLattice.scala | 9 ++++++++- .../LinearConstantPropagationProblem.scala | 19 +++++++++---------- .../LinearConstantPropagationValue.scala | 9 ++++++++- .../JavaIDEAnalysisScheduler.scala | 7 ++++++- .../JavaIDEAnalysisSchedulerBase.scala | 7 ++++++- .../JavaIDEPropertyMetaInformation.scala | 7 ++++++- .../analyses/ide/problem/JavaIDEProblem.scala | 7 ++++++- .../ide/solver/JavaBackwardICFG.scala | 8 +++++++- .../analyses/ide/solver/JavaBaseICFG.scala | 19 +++++++------------ .../analyses/ide/solver/JavaForwardICFG.scala | 8 +++++++- .../fpcf/analyses/ide/solver/JavaICFG.scala | 7 ++++++- .../analyses/ide/solver/JavaStatement.scala | 13 ++++++------- 65 files changed, 343 insertions(+), 114 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala index 6187fa732d..0609c1a3a2 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala @@ -1,5 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.ide +package org.opalj +package fpcf +package ide import java.net.URL @@ -10,7 +12,6 @@ import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.br.analyses.Project import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey -import org.opalj.fpcf.PropertiesTest import org.opalj.ide.ConfigKeyDebugLog import org.opalj.ide.ConfigKeyTraceLog import org.opalj.tac.cg.RTACallGraphKey diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala index b43c3fa43c..1a4128de1c 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -1,5 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.ide.lcp_on_fields +package org.opalj +package fpcf +package ide +package lcp_on_fields import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsAnalysisScheduler import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala index 6101e41890..7393f6abd5 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala @@ -1,11 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.ide.lcp_on_fields +package org.opalj +package fpcf +package ide +package lcp_on_fields import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.analyses.immutability.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.immutability.LazyTypeImmutabilityAnalysis -import org.opalj.fpcf.PropertyStore -import org.opalj.fpcf.ide.IDEPropertiesTest import org.opalj.fpcf.properties.lcp_on_fields.LCPOnFieldsProperty import org.opalj.fpcf.properties.linear_constant_propagation.LinearConstantPropagationProperty import org.opalj.ide.integration.LazyIDEAnalysisProxyScheduler diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala index 1b7e305972..1f7d241a52 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala @@ -1,5 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.ide.lcp_on_fields +package org.opalj +package fpcf +package ide +package lcp_on_fields import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LinearConstantPropagationAnalysisSchedulerExtended import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala index ed2e5be782..9d4a84e735 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala @@ -1,5 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.ide.linear_constant_propagation +package org.opalj +package fpcf +package ide +package linear_constant_propagation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationAnalysisScheduler import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala index fa800ed90e..eb60af3b75 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala @@ -1,8 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.ide.linear_constant_propagation +package org.opalj +package fpcf +package ide +package linear_constant_propagation import org.opalj.br.analyses.SomeProject -import org.opalj.fpcf.ide.IDEPropertiesTest import org.opalj.fpcf.properties.linear_constant_propagation.LinearConstantPropagationProperty import org.opalj.ide.integration.EagerIDEAnalysisProxyScheduler diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/AbstractRepeatablePropertyMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/AbstractRepeatablePropertyMatcher.scala index c7d5bdf4b2..045984f759 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/AbstractRepeatablePropertyMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/AbstractRepeatablePropertyMatcher.scala @@ -1,10 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties +package org.opalj +package fpcf +package properties import org.opalj.br.AnnotationLike import org.opalj.br.ObjectType import org.opalj.br.analyses.Project -import org.opalj.fpcf.Property /** * Property matcher for repeatable annotations diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala index 168aa5a95f..9c38af418c 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala @@ -1,13 +1,13 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.lcp_on_fields +package org.opalj +package fpcf +package properties +package lcp_on_fields import org.opalj.br.AnnotationLike import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.br.analyses.Project -import org.opalj.fpcf.Property -import org.opalj.fpcf.properties.AbstractPropertyMatcher -import org.opalj.fpcf.properties.AbstractRepeatablePropertyMatcher import org.opalj.ide.integration.BasicIDEProperty import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala index ec682fb430..f0166691f6 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala @@ -1,11 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.linear_constant_propagation +package org.opalj +package fpcf +package properties +package linear_constant_propagation import org.opalj.br.AnnotationLike import org.opalj.br.ObjectType import org.opalj.br.analyses.Project -import org.opalj.fpcf.Property -import org.opalj.fpcf.properties.AbstractRepeatablePropertyMatcher import org.opalj.ide.integration.BasicIDEProperty import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala index 3e2d7b2282..aac6acddf4 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala @@ -1,5 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.ifds.integration +package org.opalj +package ide +package ifds +package integration import org.opalj.fpcf.Entity import org.opalj.ide.ifds.problem.IFDSValue diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSEdgeFunctions.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSEdgeFunctions.scala index 7c9d5ddf72..f255120966 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSEdgeFunctions.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSEdgeFunctions.scala @@ -1,5 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.ifds.problem +package org.opalj +package ide +package ifds +package problem import org.opalj.ide.problem.EdgeFunction diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSLattice.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSLattice.scala index ae8fc95c54..e839c3d25d 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSLattice.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSLattice.scala @@ -1,5 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.ifds.problem +package org.opalj +package ide +package ifds +package problem import org.opalj.ide.problem.MeetLattice diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala index bde1f65a0b..57654517e6 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala @@ -1,5 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.ifds.problem +package org.opalj +package ide +package ifds +package problem import org.opalj.fpcf.Entity import org.opalj.fpcf.PropertyStore diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSValue.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSValue.scala index faaa131d35..0f9c070308 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSValue.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSValue.scala @@ -1,5 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.ifds.problem +package org.opalj +package ide +package ifds +package problem import org.opalj.ide.problem.IDEValue diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala index 01fa8790b5..4e295df7c1 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala @@ -1,5 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.integration +package org.opalj +package ide +package integration import scala.collection.immutable diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala index ecc438bd63..1ebf4d6a33 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala @@ -1,5 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.integration +package org.opalj +package ide +package integration import org.opalj.br.Method import org.opalj.br.analyses.SomeProject diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala index 483afa895a..db7fc423fe 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala @@ -1,5 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.integration +package org.opalj +package ide +package integration import scala.annotation.tailrec diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala index d8431078f8..142a268054 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala @@ -1,5 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.integration +package org.opalj +package ide +package integration import scala.collection.immutable diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala index 803b15ed13..5f169fc565 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala @@ -1,5 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.integration +package org.opalj +package ide +package integration + +import scala.collection import org.opalj.fpcf.Property import org.opalj.fpcf.PropertyKey diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala index 6cedba1236..f4b299a7f5 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala @@ -1,5 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.integration +package org.opalj +package ide +package integration + +import scala.collection import org.opalj.fpcf.Entity import org.opalj.fpcf.PropertyMetaInformation diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala index 277dde8880..4b5870b015 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala @@ -1,5 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.integration +package org.opalj +package ide +package integration + +import scala.collection import org.opalj.fpcf.Property import org.opalj.fpcf.PropertyKey diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala index f31845ebc2..44102cc4cc 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala @@ -1,5 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.integration +package org.opalj +package ide +package integration import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyMetaInformation diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesProperty.scala index 735eccb31a..d190489250 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesProperty.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesProperty.scala @@ -1,5 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.integration +package org.opalj +package ide +package integration + +import scala.collection import org.opalj.fpcf.Entity import org.opalj.fpcf.Property diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesPropertyMetaInformation.scala index 17914fb042..a135a211b7 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesPropertyMetaInformation.scala @@ -1,5 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.integration +package org.opalj +package ide +package integration import org.opalj.fpcf.Entity import org.opalj.fpcf.PropertyKey diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala index e40010e03d..290cedf0cc 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala @@ -1,5 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.integration +package org.opalj +package ide +package integration import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.FPCFAnalysis diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala index 27e65d85be..30ce23475d 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala @@ -1,5 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.problem +package org.opalj +package ide +package problem /** * Interface representing IDE edge functions diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala index dec7d63094..f7aad5e4bf 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala @@ -1,5 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.problem +package org.opalj +package ide +package problem + +import scala.collection import org.opalj.fpcf.SomeEOptionP diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala index 506f37ba53..8745c9a24a 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala @@ -1,8 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.problem +package org.opalj +package ide +package problem import scala.language.implicitConversions +import scala.collection import scala.collection.immutable import org.opalj.fpcf.SomeEOptionP diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index 4d26849986..52ae8c0652 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -1,7 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.problem +package org.opalj +package ide +package problem import java.io.Writer +import scala.collection import scala.collection.mutable import org.opalj.fpcf.Entity diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEFact.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEFact.scala index 05084e66a3..3b3f73aa2c 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEFact.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEFact.scala @@ -1,5 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.problem +package org.opalj +package ide +package problem /** * Interface representing IDE facts diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index 2affd89864..b25a415154 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -1,9 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.problem +package org.opalj +package ide +package problem import scala.annotation.unused import scala.language.implicitConversions +import scala.collection import scala.collection.immutable import org.opalj.fpcf.Entity diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEValue.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEValue.scala index 16a882c52a..5499446430 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEValue.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEValue.scala @@ -1,5 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.problem +package org.opalj +package ide +package problem /** * Interface representing IDE values diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/MeetLattice.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/MeetLattice.scala index 53d76c9365..bf7a103229 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/MeetLattice.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/MeetLattice.scala @@ -1,5 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.problem +package org.opalj +package ide +package problem /** * Interface representing the lattice that orders the IDE values diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala index 9668ee3b3d..d038d3d91e 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala @@ -1,5 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.solver +package org.opalj +package ide +package solver + +import scala.collection import org.opalj.fpcf.Entity diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 6b535ba1d4..2327715757 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -1,6 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.solver +package org.opalj +package ide +package solver +import scala.collection import scala.collection.immutable import scala.collection.mutable diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala index db9b604c85..50b66b457b 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala @@ -1,5 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.solver +package org.opalj +package ide +package solver import scala.collection.immutable diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/util/Logging.scala b/OPAL/ide/src/main/scala/org/opalj/ide/util/Logging.scala index eabab04a2f..1a987639c7 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/util/Logging.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/util/Logging.scala @@ -1,10 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ide.util +package org.opalj +package ide +package util import org.opalj.br.analyses.SomeProject -import org.opalj.ide.ConfigKeyDebugLog -import org.opalj.ide.ConfigKeyTraceLog -import org.opalj.ide.FrameworkName import org.opalj.log.GlobalLogContext import org.opalj.log.LogContext import org.opalj.log.OPALLogger diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala index f99baa6a91..e0916f028f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala @@ -1,5 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.ifds.integration +package org.opalj +package tac +package fpcf +package analyses +package ide +package ifds +package integration import org.opalj.br.analyses.SomeProject import org.opalj.ide.ifds.problem.IFDSValue diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala index dff3ff931e..0276b3f752 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala @@ -1,5 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.ifds.integration +package org.opalj +package tac +package fpcf +package analyses +package ide +package ifds +package integration import org.opalj.br.Method import org.opalj.ide.ifds.integration.IFDSPropertyMetaInformation diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala index 59b6bdc655..85d246faf5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala @@ -1,5 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.ifds.problem +package org.opalj +package tac +package fpcf +package analyses +package ide +package ifds +package problem import org.opalj.br.Method import org.opalj.ide.ifds.problem.IFDSProblem diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala index 56ea5d362a..857be1fd68 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -1,5 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package lcp_on_fields import scala.collection.immutable diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala index 6fef15e7f4..309ad516c8 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala @@ -1,5 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package lcp_on_fields import org.opalj.fpcf.PropertyKey import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsFact diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala index 3616162a79..337f4b2378 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala @@ -1,5 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package lcp_on_fields import scala.collection.immutable diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala index 7ddec83e03..7db4f9bd59 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -1,5 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package lcp_on_fields +package problem import scala.collection.immutable diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala index a87b982823..682c6b2402 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala @@ -1,5 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package lcp_on_fields +package problem import org.opalj.br.ObjectType import org.opalj.ide.problem.IDEFact diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala index 1ebc4e6aee..d15001806b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala @@ -1,5 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package lcp_on_fields +package problem import org.opalj.ide.problem.MeetLattice import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationLattice diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index f1d19b89a1..acfd3d2c35 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -1,6 +1,14 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem - +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package lcp_on_fields +package problem + +import scala.collection import scala.collection.immutable import scala.collection.mutable @@ -21,12 +29,6 @@ import org.opalj.ide.problem.FinalEdgeFunction import org.opalj.ide.problem.FlowFunction import org.opalj.ide.problem.InterimEdgeFunction import org.opalj.ide.problem.MeetLattice -import org.opalj.tac.ArrayStore -import org.opalj.tac.Assignment -import org.opalj.tac.New -import org.opalj.tac.NewArray -import org.opalj.tac.PutField -import org.opalj.tac.PutStatic import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationLattice diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala index b527a5560e..3324962d67 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala @@ -1,5 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package lcp_on_fields +package problem import scala.collection.immutable diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala index 83f83df473..eb796d22fa 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala @@ -1,5 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package lcp_on_fields +package problem import scala.collection.immutable @@ -11,9 +18,6 @@ import org.opalj.fpcf.PropertyStore import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.FinalEdgeFunction import org.opalj.ide.problem.InterimEdgeFunction -import org.opalj.tac.ArrayLoad -import org.opalj.tac.GetField -import org.opalj.tac.GetStatic import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.{VariableValue => LCPVariableValue} import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.ConstantValue diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala index 3c5408a43b..f875491f65 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala @@ -1,5 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package linear_constant_propagation import org.opalj.br.analyses.SomeProject import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala index b875ceeabb..a3c9518b7d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala @@ -1,5 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package linear_constant_propagation import org.opalj.fpcf.PropertyKey import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationFact diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala index 57743c5819..88f1b63623 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala @@ -1,5 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package linear_constant_propagation +package problem import org.opalj.ide.problem.AllBottomEdgeFunction import org.opalj.ide.problem.AllTopEdgeFunction diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationFact.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationFact.scala index fa8021c923..bb37b19ba6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationFact.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationFact.scala @@ -1,5 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package linear_constant_propagation +package problem import org.opalj.ide.problem.IDEFact diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationLattice.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationLattice.scala index 403dad281d..61cd4b762a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationLattice.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationLattice.scala @@ -1,5 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package linear_constant_propagation +package problem import org.opalj.ide.problem.MeetLattice diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index 06d54f2895..ff45364ca4 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -1,8 +1,16 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package linear_constant_propagation +package problem import scala.annotation.unused +import scala.collection import scala.collection.immutable import org.opalj.BinaryArithmeticOperators @@ -13,15 +21,6 @@ import org.opalj.ide.problem.EdgeFunctionResult import org.opalj.ide.problem.FlowFunction import org.opalj.ide.problem.IdentityFlowFunction import org.opalj.ide.problem.MeetLattice -import org.opalj.tac.ArrayLength -import org.opalj.tac.ArrayLoad -import org.opalj.tac.Assignment -import org.opalj.tac.BinaryExpr -import org.opalj.tac.Expr -import org.opalj.tac.GetField -import org.opalj.tac.GetStatic -import org.opalj.tac.IntConst -import org.opalj.tac.Var import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationValue.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationValue.scala index 3c743c4537..4806e25b6a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationValue.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationValue.scala @@ -1,5 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem +package org.opalj +package tac +package fpcf +package analyses +package ide +package instances +package linear_constant_propagation +package problem import org.opalj.ide.problem.IDEValue diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala index 29a03d713f..5eb08c3767 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala @@ -1,5 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.integration +package org.opalj +package tac +package fpcf +package analyses +package ide +package integration import org.opalj.br.analyses.SomeProject import org.opalj.ide.problem.IDEFact diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala index 6d36d15fbd..d27416f8f2 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala @@ -1,5 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.integration +package org.opalj +package tac +package fpcf +package analyses +package ide +package integration import scala.collection.immutable diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala index ca0039467f..7f7c88b9ee 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala @@ -1,5 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.integration +package org.opalj +package tac +package fpcf +package analyses +package ide +package integration import org.opalj.br.Method import org.opalj.ide.integration.IDEPropertyMetaInformation diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala index 16423d1641..65eb6635b0 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala @@ -1,5 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.problem +package org.opalj +package tac +package fpcf +package analyses +package ide +package problem import org.opalj.br.Method import org.opalj.ide.problem.IDEFact diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBackwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBackwardICFG.scala index 0bcb8d473b..c493d98846 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBackwardICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBackwardICFG.scala @@ -1,6 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.solver +package org.opalj +package tac +package fpcf +package analyses +package ide +package solver +import scala.collection import scala.collection.immutable import scala.collection.mutable diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala index 885962b5ac..059d3b13e8 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala @@ -1,6 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.solver +package org.opalj +package tac +package fpcf +package analyses +package ide +package solver +import scala.collection import scala.collection.immutable import scala.collection.mutable @@ -14,17 +20,6 @@ import org.opalj.br.fpcf.analyses.ContextProvider import org.opalj.br.fpcf.properties.cg.Callees import org.opalj.fpcf.FinalP import org.opalj.fpcf.PropertyStore -import org.opalj.tac.AITACode -import org.opalj.tac.Assignment -import org.opalj.tac.ExprStmt -import org.opalj.tac.LazyDetachedTACAIKey -import org.opalj.tac.NonVirtualFunctionCall -import org.opalj.tac.NonVirtualMethodCall -import org.opalj.tac.StaticFunctionCall -import org.opalj.tac.StaticMethodCall -import org.opalj.tac.TACMethodParameter -import org.opalj.tac.VirtualFunctionCall -import org.opalj.tac.VirtualMethodCall import org.opalj.value.ValueInformation /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala index ac722d0bd1..cd90ca2457 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala @@ -1,6 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.solver +package org.opalj +package tac +package fpcf +package analyses +package ide +package solver +import scala.collection import scala.collection.immutable import scala.collection.mutable diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala index ea6384d6b0..fd87aa8b31 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala @@ -1,5 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.solver +package org.opalj +package tac +package fpcf +package analyses +package ide +package solver import org.opalj.br.Method import org.opalj.ide.solver.ICFG diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala index e8c90cbabf..3e84dee3b3 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala @@ -1,15 +1,14 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ide.solver +package org.opalj +package tac +package fpcf +package analyses +package ide +package solver import org.opalj.br.Method import org.opalj.br.cfg.BasicBlock import org.opalj.br.cfg.CFG -import org.opalj.tac.Assignment -import org.opalj.tac.Call -import org.opalj.tac.DUVar -import org.opalj.tac.ExprStmt -import org.opalj.tac.Stmt -import org.opalj.tac.TACStmts import org.opalj.value.ValueInformation /** From e932212bfa49493cb0babce789519f53c1b66737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 12 Mar 2025 19:04:40 +0100 Subject: [PATCH 165/167] Adjust author and documentation --- .../ArrayNativeMethodExample.java | 5 ++ .../ArrayReadWriteAcrossMethodsExample.java | 5 ++ .../ArrayReadWriteConstantExample.java | 5 ++ .../ArrayUnknownIndicesExample.java | 5 ++ .../CreateObjectInMethodExample.java | 5 ++ .../FieldReadWriteAcrossMethodsExample.java | 5 ++ .../FieldReadWriteConstantExample.java | 5 ++ .../FieldReadWriteWithBranchingExample.java | 5 ++ .../ObjectNativeMethodExample.java | 5 ++ .../StaticFieldImmutableExample.java | 5 ++ .../StaticFieldNonImmutableExample.java | 5 ++ ...ticFieldReadWriteAcrossMethodsExample.java | 5 ++ .../StaticFieldReadWriteExample.java | 5 ++ .../BranchingConstantsExample.java | 5 ++ .../BranchingLinearCombinationExample.java | 5 ++ .../ConstantsWithinMethodExample.java | 5 ++ .../FieldAccessExample.java | 5 ++ .../LoopExample.java | 5 ++ .../PropagationAcrossMethodsExample.java | 5 ++ .../RecursionExample.java | 5 ++ .../VariablesWithinMethodExample.java | 5 ++ .../properties/lcp_on_fields/ArrayValue.java | 4 +- .../properties/lcp_on_fields/ArrayValues.java | 4 +- .../lcp_on_fields/ConstantArrayElement.java | 4 +- .../lcp_on_fields/LCPOnFieldsProperty.java | 5 ++ .../properties/lcp_on_fields/ObjectValue.java | 4 +- .../lcp_on_fields/ObjectValues.java | 4 +- .../lcp_on_fields/StaticValues.java | 4 +- .../lcp_on_fields/UnknownArrayElement.java | 4 +- .../lcp_on_fields/UnknownValue.java | 4 +- .../lcp_on_fields/UnknownValues.java | 4 +- .../lcp_on_fields/VariableArrayElement.java | 4 +- .../lcp_on_fields/VariableValue.java | 4 +- .../lcp_on_fields/VariableValues.java | 4 +- .../ConstantValue.java | 4 +- .../ConstantValues.java | 4 +- .../LinearConstantPropagationProperty.java | 5 ++ .../UnknownValue.java | 4 +- .../UnknownValues.java | 4 +- .../VariableValue.java | 4 +- .../VariableValues.java | 4 +- .../opalj/fpcf/ide/IDEPropertiesTest.scala | 5 ++ .../LCPOnFieldsAnalysisScheduler.scala | 6 ++ .../ide/lcp_on_fields/LCPOnFieldsTests.scala | 5 ++ ...PropagationAnalysisSchedulerExtended.scala | 7 +++ ...ConstantPropagationAnalysisScheduler.scala | 7 +++ .../LinearConstantPropagationTests.scala | 5 ++ .../AbstractRepeatablePropertyMatcher.scala | 4 +- .../lcp_on_fields/LCPOnFieldsMatcher.scala | 24 ++++++-- .../LinearConstantPropagationMatcher.scala | 15 ++++- .../IFDSPropertyMetaInformation.scala | 4 +- .../ide/ifds/problem/IFDSEdgeFunctions.scala | 4 +- .../opalj/ide/ifds/problem/IFDSLattice.scala | 4 +- .../opalj/ide/ifds/problem/IFDSProblem.scala | 5 +- .../opalj/ide/ifds/problem/IFDSValue.scala | 10 +++- .../BaseIDEAnalysisProxyScheduler.scala | 4 +- .../EagerIDEAnalysisProxyScheduler.scala | 5 +- .../FlowRecordingAnalysisScheduler.scala | 3 + .../integration/IDEAnalysisScheduler.scala | 4 +- .../opalj/ide/integration/IDEProperty.scala | 9 ++- .../IDEPropertyMetaInformation.scala | 3 + .../ide/integration/IDERawProperty.scala | 3 + .../IDERawPropertyMetaInformation.scala | 3 + .../IDETargetCallablesProperty.scala | 2 + ...rgetCallablesPropertyMetaInformation.scala | 2 + .../LazyIDEAnalysisProxyScheduler.scala | 4 +- .../main/scala/org/opalj/ide/package.scala | 3 + .../org/opalj/ide/problem/EdgeFunction.scala | 14 ++++- .../ide/problem/EdgeFunctionResult.scala | 13 ++++- .../org/opalj/ide/problem/FlowFunction.scala | 13 ++++- .../ide/problem/FlowRecordingIDEProblem.scala | 9 +++ .../scala/org/opalj/ide/problem/IDEFact.scala | 4 +- .../org/opalj/ide/problem/IDEProblem.scala | 37 ++++++++---- .../org/opalj/ide/problem/IDEValue.scala | 4 +- .../org/opalj/ide/problem/MeetLattice.scala | 4 +- .../scala/org/opalj/ide/solver/ICFG.scala | 7 ++- .../org/opalj/ide/solver/IDEAnalysis.scala | 20 +++++-- .../opalj/ide/solver/IDEAnalysisProxy.scala | 3 + .../scala/org/opalj/ide/util/Logging.scala | 4 +- .../JavaIFDSAnalysisScheduler.scala | 4 +- .../JavaIFDSPropertyMetaInformation.scala | 4 +- .../ide/ifds/problem/JavaIFDSProblem.scala | 4 +- .../LCPOnFieldsAnalysisScheduler.scala | 2 + .../LCPOnFieldsPropertyMetaInformation.scala | 4 +- ...PropagationAnalysisSchedulerExtended.scala | 4 +- .../problem/LCPOnFieldsEdgeFunctions.scala | 34 ++++++++--- .../problem/LCPOnFieldsFact.scala | 57 ++++++++++++++----- .../problem/LCPOnFieldsLattice.scala | 4 +- .../problem/LCPOnFieldsProblem.scala | 6 ++ .../problem/LCPOnFieldsValue.scala | 24 ++++++-- ...arConstantPropagationProblemExtended.scala | 2 + ...ConstantPropagationAnalysisScheduler.scala | 4 +- ...ntPropagationPropertyMetaInformation.scala | 4 +- ...nearConstantPropagationEdgeFunctions.scala | 12 +++- .../LinearConstantPropagationFact.scala | 13 ++++- .../LinearConstantPropagationLattice.scala | 4 +- .../LinearConstantPropagationProblem.scala | 4 +- .../LinearConstantPropagationValue.scala | 16 ++++-- .../JavaIDEAnalysisScheduler.scala | 4 +- .../JavaIDEAnalysisSchedulerBase.scala | 4 +- .../JavaIDEPropertyMetaInformation.scala | 4 +- .../analyses/ide/problem/JavaIDEProblem.scala | 4 +- .../ide/solver/JavaBackwardICFG.scala | 2 + .../analyses/ide/solver/JavaBaseICFG.scala | 2 + .../analyses/ide/solver/JavaForwardICFG.scala | 2 + .../fpcf/analyses/ide/solver/JavaICFG.scala | 4 +- .../analyses/ide/solver/JavaStatement.scala | 5 +- 107 files changed, 583 insertions(+), 123 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayNativeMethodExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayNativeMethodExample.java index dec1119a49..41971e0479 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayNativeMethodExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayNativeMethodExample.java @@ -6,6 +6,11 @@ import org.opalj.fpcf.properties.lcp_on_fields.ConstantArrayElement; import org.opalj.fpcf.properties.lcp_on_fields.VariableArrayElement; +/** + * An example to test conservative handling of array elements in native method calls. + * + * @author Robin Körkemeier + */ public class ArrayNativeMethodExample { @ArrayValues({ @ArrayValue(variable = "lv1", variableElements = { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteAcrossMethodsExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteAcrossMethodsExample.java index b3197a75be..11bb92954b 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteAcrossMethodsExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteAcrossMethodsExample.java @@ -3,6 +3,11 @@ import org.opalj.fpcf.properties.lcp_on_fields.*; +/** + * An example to test reading and writing array elements across methods. + * + * @author Robin Körkemeier + */ public class ArrayReadWriteAcrossMethodsExample { public void setIndexTo23(int[] arr, int index) { arr[index] = 23; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteConstantExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteConstantExample.java index c256ccb143..3265f6cc23 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteConstantExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayReadWriteConstantExample.java @@ -5,6 +5,11 @@ import org.opalj.fpcf.properties.lcp_on_fields.ArrayValues; import org.opalj.fpcf.properties.lcp_on_fields.ConstantArrayElement; +/** + * An example to test reading and writing array elements in one method. + * + * @author Robin Körkemeier + */ public class ArrayReadWriteConstantExample { @ArrayValues({ @ArrayValue(variable = "lv1", constantElements = { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayUnknownIndicesExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayUnknownIndicesExample.java index 008c13c84c..167132ef67 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayUnknownIndicesExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ArrayUnknownIndicesExample.java @@ -7,6 +7,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; +/** + * An example to test reading and writing of arrays at an unknown index. + * + * @author Robin Körkemeier + */ public class ArrayUnknownIndicesExample { @ArrayValue(variable = "lv1", variableElements = { @VariableArrayElement(index = 0), diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java index 0b6866f7d9..f2fa7de0b2 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/CreateObjectInMethodExample.java @@ -6,6 +6,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; +/** + * An example to test objects created across methods. + * + * @author Robin Körkemeier + */ public class CreateObjectInMethodExample { private int a = 42; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java index c90f9ab459..5e4c54867e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteAcrossMethodsExample.java @@ -6,6 +6,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; import org.opalj.fpcf.properties.linear_constant_propagation.UnknownValue; +/** + * An example to test reading and writing fields of objects across methods. + * + * @author Robin Körkemeier + */ public class FieldReadWriteAcrossMethodsExample { private int a = -2; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteConstantExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteConstantExample.java index 6f3e6f8046..aa89a74c21 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteConstantExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteConstantExample.java @@ -5,6 +5,11 @@ import org.opalj.fpcf.properties.lcp_on_fields.ObjectValues; import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +/** + * An example to test reading and writing of object fields with constants. + * + * @author Robin Körkemeier + */ public class FieldReadWriteConstantExample { private int a = -1; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java index c945a3e33a..8f6207ee39 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/FieldReadWriteWithBranchingExample.java @@ -6,6 +6,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; +/** + * An example to test reading and writing object fields in presence of if-then-else constructs. + * + * @author Robin Körkemeier + */ public class FieldReadWriteWithBranchingExample { private int a = -1; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ObjectNativeMethodExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ObjectNativeMethodExample.java index c570bb1718..7674362195 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ObjectNativeMethodExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/ObjectNativeMethodExample.java @@ -6,6 +6,11 @@ import org.opalj.fpcf.properties.lcp_on_fields.VariableValue; import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +/** + * An example to test conservative handling of objects when encountering native methods. + * + * @author Robin Körkemeier + */ public class ObjectNativeMethodExample { int a = 2; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java index 4839af5299..1e1d03b503 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldImmutableExample.java @@ -6,6 +6,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValues; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; +/** + * An example to test detection of static immutable fields. + * + * @author Robin Körkemeier + */ public final class StaticFieldImmutableExample { protected static int a = 42; static int b; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldNonImmutableExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldNonImmutableExample.java index 1c3907716e..1d80a6f8f4 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldNonImmutableExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldNonImmutableExample.java @@ -5,6 +5,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; +/** + * An example to test detection of static but non-immutable fields. + * + * @author Robin Körkemeier + */ public class StaticFieldNonImmutableExample { static int a = 42; protected static int b = 23; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteAcrossMethodsExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteAcrossMethodsExample.java index e1397e08c1..2d09d12b08 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteAcrossMethodsExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteAcrossMethodsExample.java @@ -5,6 +5,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValues; +/** + * An example to test reading and writing private static fields across methods. + * + * @author Robin Körkemeier + */ public class StaticFieldReadWriteAcrossMethodsExample { private static int a; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteExample.java index f8c1e6adcc..76e622b42c 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/lcp_on_fields/StaticFieldReadWriteExample.java @@ -6,6 +6,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValues; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; +/** + * An example to test reading and writing private static fields in one method. + * + * @author Robin Körkemeier + */ public class StaticFieldReadWriteExample { private static int a; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingConstantsExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingConstantsExample.java index e9589d1a7d..d52589c65c 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingConstantsExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingConstantsExample.java @@ -5,6 +5,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; +/** + * An example to test simple variable values in presence of if-then-else constructs. + * + * @author Robin Körkemeier + */ public class BranchingConstantsExample { @ConstantValue(variable = "lvd", value = 8) @VariableValues({ diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingLinearCombinationExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingLinearCombinationExample.java index eb95664761..268c272e1a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingLinearCombinationExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/BranchingLinearCombinationExample.java @@ -6,6 +6,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; +/** + * An example to test linear combination values in the presence of if-then-else constructs. + * + * @author Robin Körkemeier + */ public class BranchingLinearCombinationExample { private static int linearCalculation1(int y, int x) { int z; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/ConstantsWithinMethodExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/ConstantsWithinMethodExample.java index 05e0cadcbe..334327f597 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/ConstantsWithinMethodExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/ConstantsWithinMethodExample.java @@ -4,6 +4,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValues; +/** + * An example to test constants (simple and linear) in a single method. + * + * @author Robin Körkemeier + */ public class ConstantsWithinMethodExample { @ConstantValues({ @ConstantValue(variable = "lv0", value = 4), diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java index 29fde447c2..ee5aff09ad 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/FieldAccessExample.java @@ -4,6 +4,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; +/** + * An example to test field accesses are detected by classical linear constant propagation. + * + * @author Robin Körkemeier + */ public class FieldAccessExample { private final int a; int b; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/LoopExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/LoopExample.java index 41c8de95bf..df01fef725 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/LoopExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/LoopExample.java @@ -4,6 +4,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; +/** + * An example to test behavior of IDE solver on loop constructs. + * + * @author Robin Körkemeier + */ public class LoopExample { public static int loop1(int a) { int res = 0; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java index bd3f69d1a7..e6da7906e4 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/PropagationAcrossMethodsExample.java @@ -3,6 +3,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.*; +/** + * An example to test fact and value propagation across methods. + * + * @author Robin Körkemeier + */ public class PropagationAcrossMethodsExample { @VariableValues({ @VariableValue(variable = "param2"), diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/RecursionExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/RecursionExample.java index e724b4d6bc..94c5099db0 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/RecursionExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/RecursionExample.java @@ -3,6 +3,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue; +/** + * An example to test behavior of IDE solver when encountering recursion. + * + * @author Robin Körkemeier + */ public class RecursionExample { public static int recursive1(int a) { if (a > 0) { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/VariablesWithinMethodExample.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/VariablesWithinMethodExample.java index 980de0dddb..ff30f1db36 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/VariablesWithinMethodExample.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/linear_constant_propagation/VariablesWithinMethodExample.java @@ -4,6 +4,11 @@ import org.opalj.fpcf.properties.linear_constant_propagation.VariableValue; import org.opalj.fpcf.properties.linear_constant_propagation.VariableValues; +/** + * An example to test detection of variable values within a method. + * + * @author Robin Körkemeier + */ public class VariablesWithinMethodExample { @VariableValues({ @VariableValue(variable = "lv0"), diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValue.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValue.java index 8a8268c3db..d18065005e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValue.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValue.java @@ -6,7 +6,9 @@ import java.lang.annotation.*; /** - * Annotation to state that an array has been identified and has certain constant and non-constant elements + * Annotation to state that an array has been identified and has certain constant and non-constant elements. + * + * @author Robin Körkemeier */ @PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = ArrayValueMatcher.class) @Repeatable(ArrayValues.class) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValues.java index e1c9280df2..5201e8a0c3 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValues.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ArrayValues.java @@ -6,7 +6,9 @@ import java.lang.annotation.*; /** - * Container annotation for {@link ArrayValue} annotations + * Container annotation for {@link ArrayValue} annotations. + * + * @author Robin Körkemeier */ @PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = ArrayValueMatcher.class) @Documented diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ConstantArrayElement.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ConstantArrayElement.java index 292975d90c..a9166b4ef7 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ConstantArrayElement.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ConstantArrayElement.java @@ -4,7 +4,9 @@ import java.lang.annotation.*; /** - * Annotation to state that an array element has a constant value + * Annotation to state that an array element has a constant value. + * + * @author Robin Körkemeier */ @Documented @Target(ElementType.ANNOTATION_TYPE) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsProperty.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsProperty.java index 2af58486e4..b15a23ba96 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsProperty.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsProperty.java @@ -1,6 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.lcp_on_fields; +/** + * Centralized property validator key for linear constant propagation on fields. + * + * @author Robin Körkemeier + */ public class LCPOnFieldsProperty { public static final String KEY = "LCPOnFields"; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValue.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValue.java index 83090e48ea..d66d7262c8 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValue.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValue.java @@ -9,7 +9,9 @@ import java.lang.annotation.*; /** - * Annotation to state that an object has been identified and has certain constant and non-constant values + * Annotation to state that an object has been identified and has certain constant and non-constant values. + * + * @author Robin Körkemeier */ @PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = ObjectValueMatcher.class) @Repeatable(ObjectValues.class) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValues.java index 52f0b61aaf..819174c44d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValues.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/ObjectValues.java @@ -6,7 +6,9 @@ import java.lang.annotation.*; /** - * Container annotation for {@link ObjectValue} annotations + * Container annotation for {@link ObjectValue} annotations. + * + * @author Robin Körkemeier */ @PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = ObjectValueMatcher.class) @Documented diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/StaticValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/StaticValues.java index 7cefe01f0b..5f016501dd 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/StaticValues.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/StaticValues.java @@ -9,7 +9,9 @@ import java.lang.annotation.*; /** - * Annotation to state that an object has certain constant and non-constant static values + * Annotation to state that an object has certain constant and non-constant static values. + * + * @author Robin Körkemeier */ @PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = StaticValuesMatcher.class) @Documented diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownArrayElement.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownArrayElement.java index c6d4ee6e98..1a46223fe0 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownArrayElement.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownArrayElement.java @@ -6,7 +6,9 @@ import java.lang.annotation.Target; /** - * Annotation to state that an array elements value is unknown + * Annotation to state that an array elements value is unknown. + * + * @author Robin Körkemeier */ @Documented @Target(ElementType.ANNOTATION_TYPE) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValue.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValue.java index 5852b3cdff..a98342d4d9 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValue.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValue.java @@ -6,7 +6,9 @@ import java.lang.annotation.*; /** - * Annotation to state that a variables value is unknown + * Annotation to state that a variables value is unknown. + * + * @author Robin Körkemeier */ @PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = UnknownValueMatcher.class) @Repeatable(UnknownValues.class) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValues.java index 5bc3532bdb..9d27ede559 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValues.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/UnknownValues.java @@ -6,7 +6,9 @@ import java.lang.annotation.*; /** - * Container annotation for {@link UnknownValue} annotations + * Container annotation for {@link UnknownValue} annotations. + * + * @author Robin Körkemeier */ @PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = UnknownValueMatcher.class) @Documented diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableArrayElement.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableArrayElement.java index 333f0af140..a88c7d124d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableArrayElement.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableArrayElement.java @@ -6,7 +6,9 @@ import java.lang.annotation.Target; /** - * Annotation to state that an array element has a non-constant value + * Annotation to state that an array element has a non-constant value. + * + * @author Robin Körkemeier */ @Documented @Target(ElementType.ANNOTATION_TYPE) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValue.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValue.java index cb88d243a3..d4f4842a8a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValue.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValue.java @@ -6,7 +6,9 @@ import java.lang.annotation.*; /** - * Annotation to state that a variable has a non-constant value + * Annotation to state that a variable has a non-constant value. + * + * @author Robin Körkemeier */ @PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = VariableValueMatcher.class) @Repeatable(VariableValues.class) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValues.java index cb8647d2e0..dedf075a54 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValues.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/lcp_on_fields/VariableValues.java @@ -6,7 +6,9 @@ import java.lang.annotation.*; /** - * Container annotation for {@link VariableValue} annotations + * Container annotation for {@link VariableValue} annotations. + * + * @author Robin Körkemeier */ @PropertyValidator(key = LCPOnFieldsProperty.KEY, validator = VariableValueMatcher.class) @Documented diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValue.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValue.java index c6891eebbe..bb0c127256 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValue.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValue.java @@ -6,7 +6,9 @@ import java.lang.annotation.*; /** - * Annotation to state that a variable has a constant value + * Annotation to state that a variable has a constant value. + * + * @author Robin Körkemeier */ @PropertyValidator(key = LinearConstantPropagationProperty.KEY, validator = ConstantValueMatcher.class) @Repeatable(ConstantValues.class) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValues.java index 15b14d12aa..31349aadff 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValues.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/ConstantValues.java @@ -6,7 +6,9 @@ import java.lang.annotation.*; /** - * Container annotation for {@link ConstantValue} annotations + * Container annotation for {@link ConstantValue} annotations. + * + * @author Robin Körkemeier */ @PropertyValidator(key = LinearConstantPropagationProperty.KEY, validator = ConstantValueMatcher.class) @Documented diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationProperty.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationProperty.java index a952bdab5f..73c03bb739 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationProperty.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationProperty.java @@ -1,6 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.linear_constant_propagation; +/** + * Centralized property validator key for linear constant propagation. + * + * @author Robin Körkemeier + */ public class LinearConstantPropagationProperty { public static final String KEY = "LinearConstantPropagation"; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValue.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValue.java index 198d6c87c0..c925b193b7 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValue.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValue.java @@ -6,7 +6,9 @@ import java.lang.annotation.*; /** - * Annotation to state that a variables value is unknown + * Annotation to state that a variables value is unknown. + * + * @author Robin Körkemeier */ @PropertyValidator(key = LinearConstantPropagationProperty.KEY, validator = UnknownValueMatcher.class) @Repeatable(UnknownValues.class) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValues.java index 15c97bef02..92851c5f53 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValues.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/UnknownValues.java @@ -6,7 +6,9 @@ import java.lang.annotation.*; /** - * Container annotation for {@link UnknownValue} annotations + * Container annotation for {@link UnknownValue} annotations. + * + * @author Robin Körkemeier */ @PropertyValidator(key = LinearConstantPropagationProperty.KEY, validator = UnknownValueMatcher.class) @Documented diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValue.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValue.java index 59764549b6..2b0c003731 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValue.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValue.java @@ -6,7 +6,9 @@ import java.lang.annotation.*; /** - * Annotation to state that a variable has a non-constant value + * Annotation to state that a variable has a non-constant value. + * + * @author Robin Körkemeier */ @PropertyValidator(key = LinearConstantPropagationProperty.KEY, validator = VariableValueMatcher.class) @Repeatable(VariableValues.class) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValues.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValues.java index 1d13b978cd..aaa8029a92 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValues.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/linear_constant_propagation/VariableValues.java @@ -6,7 +6,9 @@ import java.lang.annotation.*; /** - * Container annotation for {@link VariableValue} annotations + * Container annotation for {@link VariableValue} annotations. + * + * @author Robin Körkemeier */ @PropertyValidator(key = LinearConstantPropagationProperty.KEY, validator = VariableValueMatcher.class) @Documented diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala index 0609c1a3a2..499ab24ae9 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/IDEPropertiesTest.scala @@ -16,6 +16,11 @@ import org.opalj.ide.ConfigKeyDebugLog import org.opalj.ide.ConfigKeyTraceLog import org.opalj.tac.cg.RTACallGraphKey +/** + * Specialized test for IDE analyses preparing the configuration. + * + * @author Robin Körkemeier + */ class IDEPropertiesTest extends PropertiesTest { override def withRT: Boolean = false diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala index 1a4128de1c..1bba0df097 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -7,5 +7,11 @@ package lcp_on_fields import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LCPOnFieldsAnalysisScheduler import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase +/** + * Scheduler for [[org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsProblem]] for Java using + * RTA call graph. + * + * @author Robin Körkemeier + */ object LCPOnFieldsAnalysisScheduler extends LCPOnFieldsAnalysisScheduler with JavaIDEAnalysisSchedulerBase.RTACallGraph diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala index 7393f6abd5..76842e07f4 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala @@ -14,6 +14,11 @@ import org.opalj.tac.fpcf.analyses.LazyFieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.fieldaccess.EagerFieldAccessInformationAnalysis import org.opalj.tac.fpcf.analyses.fieldassignability.LazyL2FieldAssignabilityAnalysis +/** + * Test runner for linear constant propagation on fields. + * + * @author Robin Körkemeier + */ class LCPOnFieldsTests extends IDEPropertiesTest { override def fixtureProjectPackage: List[String] = { List("org/opalj/fpcf/fixtures/lcp_on_fields") diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala index 1f7d241a52..12dd03e2cc 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala @@ -7,5 +7,12 @@ package lcp_on_fields import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.LinearConstantPropagationAnalysisSchedulerExtended import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase +/** + * Scheduler for + * [[org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LinearConstantPropagationProblemExtended]] for Java + * using RTA call graph. + * + * @author Robin Körkemeier + */ object LinearConstantPropagationAnalysisSchedulerExtended extends LinearConstantPropagationAnalysisSchedulerExtended with JavaIDEAnalysisSchedulerBase.RTACallGraph diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala index 9d4a84e735..dc78fcb000 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala @@ -7,5 +7,12 @@ package linear_constant_propagation import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.LinearConstantPropagationAnalysisScheduler import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase +/** + * Scheduler for + * [[org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationProblem]] + * for Java using RTA call graph. + * + * @author Robin Körkemeier + */ object LinearConstantPropagationAnalysisScheduler extends LinearConstantPropagationAnalysisScheduler with JavaIDEAnalysisSchedulerBase.RTACallGraph diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala index eb60af3b75..a24d49d71b 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/linear_constant_propagation/LinearConstantPropagationTests.scala @@ -8,6 +8,11 @@ import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.properties.linear_constant_propagation.LinearConstantPropagationProperty import org.opalj.ide.integration.EagerIDEAnalysisProxyScheduler +/** + * Test runner for linear constant propagation. + * + * @author Robin Körkemeier + */ class LinearConstantPropagationTests extends IDEPropertiesTest { override def fixtureProjectPackage: List[String] = { List("org/opalj/fpcf/fixtures/linear_constant_propagation") diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/AbstractRepeatablePropertyMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/AbstractRepeatablePropertyMatcher.scala index 045984f759..ea80b44e55 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/AbstractRepeatablePropertyMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/AbstractRepeatablePropertyMatcher.scala @@ -8,7 +8,9 @@ import org.opalj.br.ObjectType import org.opalj.br.analyses.Project /** - * Property matcher for repeatable annotations + * Basic property matcher for repeatable annotations. + * + * @author Robin Körkemeier */ abstract class AbstractRepeatablePropertyMatcher extends AbstractPropertyMatcher { /** diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala index 9c38af418c..d95fdb558b 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/lcp_on_fields/LCPOnFieldsMatcher.scala @@ -13,7 +13,10 @@ import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation /** - * Matcher for [[ObjectValue]] and [[ObjectValues]] annotations + * Matcher for [[org.opalj.fpcf.properties.lcp_on_fields.ObjectValue]] and + * [[org.opalj.fpcf.properties.lcp_on_fields.ObjectValues]] annotations. + * + * @author Robin Körkemeier */ class ObjectValueMatcher extends AbstractRepeatablePropertyMatcher { override val singleAnnotationType: ObjectType = @@ -119,7 +122,10 @@ class ObjectValueMatcher extends AbstractRepeatablePropertyMatcher { } /** - * Matcher for [[ArrayValue]] and [[ArrayValues]] annotations + * Matcher for [[org.opalj.fpcf.properties.lcp_on_fields.ArrayValue]] and + * [[org.opalj.fpcf.properties.lcp_on_fields.ArrayValues]] annotations. + * + * @author Robin Körkemeier */ class ArrayValueMatcher extends AbstractRepeatablePropertyMatcher { override val singleAnnotationType: ObjectType = @@ -234,7 +240,9 @@ class ArrayValueMatcher extends AbstractRepeatablePropertyMatcher { } /** - * Matcher for [[StaticValues]] annotations + * Matcher for [[org.opalj.fpcf.properties.lcp_on_fields.StaticValues]] annotations. + * + * @author Robin Körkemeier */ class StaticValuesMatcher extends AbstractPropertyMatcher { private val annotationType: ObjectType = @@ -355,7 +363,10 @@ class StaticValuesMatcher extends AbstractPropertyMatcher { } /** - * Matcher for [[VariableValue]] and [[VariableValues]] annotations + * Matcher for [[org.opalj.fpcf.properties.lcp_on_fields.VariableValue]] and + * [[org.opalj.fpcf.properties.lcp_on_fields.VariableValues]] annotations. + * + * @author Robin Körkemeier */ class VariableValueMatcher extends AbstractRepeatablePropertyMatcher { override val singleAnnotationType: ObjectType = @@ -403,7 +414,10 @@ class VariableValueMatcher extends AbstractRepeatablePropertyMatcher { } /** - * Matcher for [[UnknownValue]] and [[UnknownValues]] annotations + * Matcher for [[org.opalj.fpcf.properties.lcp_on_fields.UnknownValue]] and + * [[org.opalj.fpcf.properties.lcp_on_fields.UnknownValues]] annotations. + * + * @author Robin Körkemeier */ class UnknownValueMatcher extends AbstractRepeatablePropertyMatcher { override val singleAnnotationType: ObjectType = diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala index f0166691f6..5791ae7f11 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/linear_constant_propagation/LinearConstantPropagationMatcher.scala @@ -11,7 +11,10 @@ import org.opalj.ide.integration.BasicIDEProperty import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation /** - * Matcher for [[ConstantValue]] and [[ConstantValues]] annotations + * Matcher for [[org.opalj.fpcf.properties.linear_constant_propagation.ConstantValue]] and + * [[org.opalj.fpcf.properties.linear_constant_propagation.ConstantValues]] annotations. + * + * @author Robin Körkemeier */ class ConstantValueMatcher extends AbstractRepeatablePropertyMatcher { override val singleAnnotationType: ObjectType = @@ -56,7 +59,10 @@ class ConstantValueMatcher extends AbstractRepeatablePropertyMatcher { } /** - * Matcher for [[VariableValue]] and [[VariableValues]] annotations + * Matcher for [[org.opalj.fpcf.properties.linear_constant_propagation.VariableValue]] and + * [[org.opalj.fpcf.properties.linear_constant_propagation.VariableValues]] annotations. + * + * @author Robin Körkemeier */ class VariableValueMatcher extends AbstractRepeatablePropertyMatcher { override val singleAnnotationType: ObjectType = @@ -99,7 +105,10 @@ class VariableValueMatcher extends AbstractRepeatablePropertyMatcher { } /** - * Matcher for [[UnknownValue]] and [[UnknownValues]] annotations + * Matcher for [[org.opalj.fpcf.properties.linear_constant_propagation.UnknownValue]] and + * [[org.opalj.fpcf.properties.linear_constant_propagation.UnknownValues]] annotations. + * + * @author Robin Körkemeier */ class UnknownValueMatcher extends AbstractRepeatablePropertyMatcher { override val singleAnnotationType: ObjectType = diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala index aac6acddf4..849e48db9c 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/integration/IFDSPropertyMetaInformation.scala @@ -10,7 +10,9 @@ import org.opalj.ide.integration.IDEPropertyMetaInformation import org.opalj.ide.problem.IDEFact /** - * Interface for property meta information for IFDS problems based on an IDE problem + * Interface for property meta information for IFDS problems based on an IDE problem. + * + * @author Robin Körkemeier */ trait IFDSPropertyMetaInformation[Fact <: IDEFact, Statement, Callable <: Entity] extends IDEPropertyMetaInformation[Fact, IFDSValue, Statement, Callable] diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSEdgeFunctions.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSEdgeFunctions.scala index f255120966..ee687ee4a4 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSEdgeFunctions.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSEdgeFunctions.scala @@ -7,7 +7,9 @@ package problem import org.opalj.ide.problem.EdgeFunction /** - * Edge function evaluating all source values to the bottom value + * Edge function evaluating all source values to the bottom value. + * + * @author Robin Körkemeier */ object AllBottomEdgeFunction extends org.opalj.ide.problem.AllBottomEdgeFunction[IFDSValue](Bottom) { override def composeWith(secondEdgeFunction: EdgeFunction[IFDSValue]): EdgeFunction[IFDSValue] = { diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSLattice.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSLattice.scala index e839c3d25d..124547ee0f 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSLattice.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSLattice.scala @@ -7,7 +7,9 @@ package problem import org.opalj.ide.problem.MeetLattice /** - * Lattice to use for IFDS problems that are solved with an IDE solver + * Lattice to use for IFDS problems that are solved with an IDE solver. + * + * @author Robin Körkemeier */ object IFDSLattice extends MeetLattice[IFDSValue] { override def top: IFDSValue = Top diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala index 57654517e6..58fde00b53 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSProblem.scala @@ -12,7 +12,9 @@ import org.opalj.ide.problem.IDEProblem import org.opalj.ide.problem.MeetLattice /** - * Interface for modeling IFDS problems based on an IDE problem + * Interface for modeling IFDS problems based on an IDE problem. + * + * @author Robin Körkemeier */ abstract class IFDSProblem[Fact <: IDEFact, Statement, Callable <: Entity] extends IDEProblem[Fact, IFDSValue, Statement, Callable] { @@ -78,6 +80,7 @@ abstract class IFDSProblem[Fact <: IDEFact, Statement, Callable <: Entity] /** * Whether precomputed flow functions for a `(callSite, callSiteFact, callee)` combination exist (resp. can be * generated). + * * @param callSite where the flow starts * @param callSiteFact the fact the flow starts with * @param callee the callable this flow is about diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSValue.scala b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSValue.scala index 0f9c070308..550a1766b1 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSValue.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/ifds/problem/IFDSValue.scala @@ -7,16 +7,22 @@ package problem import org.opalj.ide.problem.IDEValue /** - * Type for modeling values for IFDS problems that are solved with an IDE solver + * Type for modeling values for IFDS problems that are solved with an IDE solver. + * + * @author Robin Körkemeier */ trait IFDSValue extends IDEValue /** * Top value + * + * @author Robin Körkemeier */ case object Top extends IFDSValue /** - * Bottom value (all result fact have the bottom value) + * Bottom value (all result facts have the bottom value) + * + * @author Robin Körkemeier */ case object Bottom extends IFDSValue diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala index 4e295df7c1..4eed6bc74e 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/BaseIDEAnalysisProxyScheduler.scala @@ -20,7 +20,9 @@ import org.opalj.ide.problem.IDEValue import org.opalj.ide.solver.IDEAnalysisProxy /** - * Base scheduler to schedule the proxy analysis that is used to access the IDE analysis results + * Base scheduler to schedule the proxy analysis that is used to access the IDE analysis results. + * + * @author Robin Körkemeier */ trait BaseIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity] extends FPCFAnalysisScheduler { diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala index 1ebf4d6a33..9685bf4e18 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/EagerIDEAnalysisProxyScheduler.scala @@ -15,8 +15,11 @@ import org.opalj.ide.problem.IDEValue import org.opalj.ide.solver.IDEAnalysisProxy /** - * A scheduler to (eagerly) schedule the proxy analysis that is used to access the IDE analysis results + * A scheduler to (eagerly) schedule the proxy analysis that is used to access the IDE analysis results. + * * @param methodProvider for which methods the results should be computed eagerly + * + * @author Robin Körkemeier */ class EagerIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value, Statement, Callable], diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala index db7fc423fe..19fe3c54e6 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/FlowRecordingAnalysisScheduler.scala @@ -35,9 +35,12 @@ import org.opalj.ide.util.Logging * for a given base problem as graph and writes it to a file in DOT format. * DOT files can either be viewed with a suitable local program or online e.g. at * [[https://dreampuf.github.io/GraphvizOnline]]. + * * @param path the location to write the resulting DOT file (either a file ending with `.dot` or a directory) * @param uniqueFlowsOnly whether to drop or to keep duplicated flows * @param recordEdgeFunctions whether to record edge functions too or just stick with the flow + * + * @author Robin Körkemeier */ class FlowRecordingAnalysisScheduler[ Fact <: IDEFact, diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala index 142a268054..737f7fd8e9 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEAnalysisScheduler.scala @@ -20,7 +20,9 @@ import org.opalj.ide.solver.ICFG import org.opalj.ide.solver.IDEAnalysis /** - * A base scheduler for IDE analyses adding common default behavior + * A base scheduler for IDE analyses adding common default behavior. + * + * @author Robin Körkemeier */ abstract class IDEAnalysisScheduler[ Fact <: IDEFact, diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala index 5f169fc565..be4c173af1 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEProperty.scala @@ -11,14 +11,19 @@ import org.opalj.ide.problem.IDEFact import org.opalj.ide.problem.IDEValue /** - * Base interface of properties that are produced by an IDE analysis + * Base interface of properties that are produced by an IDE analysis. + * + * @author Robin Körkemeier */ trait IDEProperty[Fact <: IDEFact, Value <: IDEValue] extends Property /** - * Basic implementation of [[IDEProperty]] that simply wraps the fact-value results of an IDE analysis + * Basic implementation of [[IDEProperty]] that simply wraps the fact-value results of an IDE analysis. + * * @param key the property key * @param results the results produced by the analysis + * + * @author Robin Körkemeier */ class BasicIDEProperty[Fact <: IDEFact, Value <: IDEValue]( val key: PropertyKey[BasicIDEProperty[Fact, Value]], diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala index f4b299a7f5..ab0deba8c3 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDEPropertyMetaInformation.scala @@ -12,6 +12,8 @@ import org.opalj.ide.problem.IDEValue /** * Base interface of property meta information of IDE analyses. Creates [[BasicIDEProperty]] by default. + * + * @author Robin Körkemeier */ trait IDEPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity] extends PropertyMetaInformation { @@ -31,6 +33,7 @@ trait IDEPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue, Statement, /** * Create a property + * * @param results the results the property should represent */ def createProperty( diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala index 4b5870b015..471496c956 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawProperty.scala @@ -12,9 +12,12 @@ import org.opalj.ide.problem.IDEValue /** * Class representing a property that is directly created by an IDE analysis. + * * @param key the property key (very likely taken from an [[IDERawPropertyMetaInformation]] instance) * @param stmtResults the raw statement results produced by the analysis * @param callableResults the raw callable results produced by the analysis + * + * @author Robin Körkemeier */ class IDERawProperty[Fact <: IDEFact, Value <: IDEValue, Statement]( val key: PropertyKey[IDERawProperty[Fact, Value, Statement]], diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala index 44102cc4cc..565a1fe5ce 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDERawPropertyMetaInformation.scala @@ -11,7 +11,10 @@ import org.opalj.ide.problem.IDEValue /** * Class for property meta information for properties that are created by IDE analyses directly (also called 'raw'). * The property type is fixed to [[IDERawProperty]]. + * * @param propertyMetaInformation the property meta information this object should be backing + * + * @author Robin Körkemeier */ final class IDERawPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue, Statement]( propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value, Statement, ?] diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesProperty.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesProperty.scala index d190489250..82b0318b81 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesProperty.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesProperty.scala @@ -11,6 +11,8 @@ import org.opalj.fpcf.PropertyKey /** * Property for the target callables that should be analysed by an IDE analysis. + * + * @author Robin Körkemeier */ class IDETargetCallablesProperty[Callable <: Entity]( val key: PropertyKey[IDETargetCallablesProperty[Callable]], diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesPropertyMetaInformation.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesPropertyMetaInformation.scala index a135a211b7..e0613d4ec8 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesPropertyMetaInformation.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/IDETargetCallablesPropertyMetaInformation.scala @@ -9,6 +9,8 @@ import org.opalj.fpcf.PropertyMetaInformation /** * Class for property meta information for properties carrying the target callables. + * + * @author Robin Körkemeier */ final class IDETargetCallablesPropertyMetaInformation[Callable <: Entity]( propertyMetaInformation: IDEPropertyMetaInformation[?, ?, ?, Callable] diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala b/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala index 290cedf0cc..f16950546c 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/integration/LazyIDEAnalysisProxyScheduler.scala @@ -14,7 +14,9 @@ import org.opalj.ide.problem.IDEValue import org.opalj.ide.solver.IDEAnalysisProxy /** - * A scheduler to (lazily) schedule the proxy analysis that is used to access the IDE analysis results + * A scheduler to (lazily) schedule the proxy analysis that is used to access the IDE analysis results. + * + * @author Robin Körkemeier */ class LazyIDEAnalysisProxyScheduler[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( val propertyMetaInformation: IDEPropertyMetaInformation[Fact, Value, Statement, Callable] diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/package.scala b/OPAL/ide/src/main/scala/org/opalj/ide/package.scala index f6ca262589..6d703d7d46 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/package.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/package.scala @@ -8,6 +8,9 @@ import org.opalj.log.GlobalLogContext import org.opalj.log.LogContext import org.opalj.log.OPALLogger.info +/** + * @author Robin Körkemeier + */ package object ide { final val FrameworkName = "OPAL IDE" diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala index 30ce23475d..7cba43fb7b 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunction.scala @@ -4,17 +4,21 @@ package ide package problem /** - * Interface representing IDE edge functions + * Interface representing IDE edge functions. + * + * @author Robin Körkemeier */ trait EdgeFunction[Value <: IDEValue] { /** * Compute the value of the edge function + * * @param sourceValue the incoming parameter value */ def compute(sourceValue: Value): Value /** * Compose two edge functions + * * @param secondEdgeFunction the edge function that is applied after this one * @return an edge function computing the same values as first applying this edge function and then applying the * result to the second edge function @@ -33,7 +37,9 @@ trait EdgeFunction[Value <: IDEValue] { } /** - * Special edge function representing an identity edge function + * Special edge function representing an identity edge function. + * + * @author Robin Körkemeier */ case class IdentityEdgeFunction[Value <: IDEValue]() extends EdgeFunction[Value] { override def compute(sourceValue: Value): Value = @@ -57,6 +63,8 @@ case class IdentityEdgeFunction[Value <: IDEValue]() extends EdgeFunction[Value] /** * Special edge function representing an edge function where all source values evaluate to the top element. Implementing * [[composeWith]] is left to the user, as it requires knowledge of the other possible edge functions. + * + * @author Robin Körkemeier */ abstract case class AllTopEdgeFunction[Value <: IDEValue](private val top: Value) extends EdgeFunction[Value] { override def compute(sourceValue: Value): Value = @@ -78,6 +86,8 @@ abstract case class AllTopEdgeFunction[Value <: IDEValue](private val top: Value /** * Special edge function representing an edge function where all source values evaluate to the bottom element. * Implementing [[composeWith]] is left to the user, as it requires knowledge of the other possible edge functions. + * + * @author Robin Körkemeier */ abstract case class AllBottomEdgeFunction[Value <: IDEValue](private val bottom: Value) extends EdgeFunction[Value] { override def compute(sourceValue: Value): Value = diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala index f7aad5e4bf..5587f87768 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/EdgeFunctionResult.scala @@ -8,19 +8,26 @@ import scala.collection import org.opalj.fpcf.SomeEOptionP /** - * Interface for encapsulating different states of edge functions + * Interface for encapsulating different states of edge functions. + * + * @author Robin Körkemeier */ trait EdgeFunctionResult[Value <: IDEValue] /** - * Represent an edge function that is final + * Represent an edge function that is final. + * + * @author Robin Körkemeier */ case class FinalEdgeFunction[Value <: IDEValue](edgeFunction: EdgeFunction[Value]) extends EdgeFunctionResult[Value] /** - * Represent an interim edge function that may change when the result of one of the dependees changes + * Represent an interim edge function that may change when the result of one of the dependees changes. + * * @param interimEdgeFunction an interim edge function to use until new results are present (has to be an upper bound of * the final edge function) + * + * @author Robin Körkemeier */ case class InterimEdgeFunction[Value <: IDEValue]( interimEdgeFunction: EdgeFunction[Value], diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala index 8745c9a24a..65a8be4556 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowFunction.scala @@ -11,7 +11,9 @@ import scala.collection.immutable import org.opalj.fpcf.SomeEOptionP /** - * Interface representing IDE flow functions + * Interface representing IDE flow functions. + * + * @author Robin Körkemeier */ trait FlowFunction[Fact <: IDEFact] { type FactsAndDependees = FlowFunction.FactsAndDependees[Fact] @@ -23,6 +25,7 @@ trait FlowFunction[Fact <: IDEFact] { /** * Compute the facts that are generated by this flow function and the dependees that can cause new facts to be * generated + * * @return a set of facts and a set of dependees (a fact that is returned once must also be returned with every * subsequent call) */ @@ -34,7 +37,9 @@ object FlowFunction { } /** - * Special flow function that always returns the input fact + * Special flow function that always returns the input fact. + * + * @author Robin Körkemeier */ case class IdentityFlowFunction[Fact <: IDEFact](sourceFact: Fact) extends FlowFunction[Fact] { override def compute(): FactsAndDependees = @@ -42,7 +47,9 @@ case class IdentityFlowFunction[Fact <: IDEFact](sourceFact: Fact) extends FlowF } /** - * Special flow function that always returns an empty set + * Special flow function that always returns an empty set. + * + * @author Robin Körkemeier */ case class EmptyFlowFunction[Fact <: IDEFact]() extends FlowFunction[Fact] { override def compute(): FactsAndDependees = diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala index 52ae8c0652..bc761c7a76 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/FlowRecordingIDEProblem.scala @@ -11,6 +11,11 @@ import org.opalj.fpcf.Entity import org.opalj.fpcf.PropertyStore import org.opalj.ide.solver.ICFG +/** + * Different modes to record flow. + * + * @author Robin Körkemeier + */ object FlowRecorderModes extends Enumeration { type FlowRecorderMode = Value @@ -29,9 +34,12 @@ object FlowRecorderModes extends Enumeration { * given base problem as graph and writes it to a file in DOT format. * DOT files can either be viewed with a suitable local program or online e.g. at * [[https://dreampuf.github.io/GraphvizOnline]]. + * * @param baseProblem the base problem that defines the flows and edge functions that should be analyzed * @param uniqueFlowsOnly whether to drop or to keep duplicated flows * @param recordEdgeFunctions whether to record edge functions too or just stick with the flow + * + * @author Robin Körkemeier */ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( val baseProblem: IDEProblem[Fact, Value, Statement, Callable], @@ -301,6 +309,7 @@ class FlowRecordingIDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Cal /** * Start recording + * * @param writer to write the graph to */ def startRecording(writer: Writer): Unit = { diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEFact.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEFact.scala index 3b3f73aa2c..c7dae838df 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEFact.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEFact.scala @@ -4,6 +4,8 @@ package ide package problem /** - * Interface representing IDE facts + * Interface representing IDE facts. + * + * @author Robin Körkemeier */ trait IDEFact diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala index b25a415154..d1470129c3 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEProblem.scala @@ -13,7 +13,9 @@ import org.opalj.fpcf.Entity import org.opalj.fpcf.PropertyStore /** - * Interface for modeling IDE problems + * Interface for modeling IDE problems. + * + * @author Robin Körkemeier */ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity] { implicit def edgeFunctionToFinalEdgeFunction(edgeFunction: EdgeFunction[Value]): EdgeFunctionResult[Value] = { @@ -44,6 +46,7 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl * Add additional facts that the analysis should be seeded with. Traditionally, IDE starts with the null fact at the * start statements of the callable. E.g. additional seeds can be used for adding facts about the parameters of the * analyzed callable. + * * @param stmt the start statement * @param callee the analyzed callable */ @@ -52,7 +55,8 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl ): collection.Set[Fact] = immutable.Set.empty /** - * Generate an edge function for a flow starting with an additional seeds + * Generate an edge function for a flow starting with an additional seeds. + * * @param stmt the start statement * @param fact the start fact * @param callee the analyzed callable @@ -62,7 +66,8 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl ): EdgeFunctionResult[Value] = identityEdgeFunction /** - * Generate a flow function for a normal flow + * Generate a flow function for a normal flow. + * * @param source where the normal flow starts * @param sourceFact the fact the flow starts with * @param target where the normal flow ends @@ -72,7 +77,8 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl ): FlowFunction[Fact] /** - * Generate a flow function for a call flow + * Generate a flow function for a call flow. + * * @param callSite where the call flow starts (always a call statement) * @param callSiteFact the fact the flow starts with * @param calleeEntry where the callable starts (the statement which the callable is started with) @@ -83,7 +89,8 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl ): FlowFunction[Fact] /** - * Generate a flow function for a return flow + * Generate a flow function for a return flow. + * * @param calleeExit where the return flow starts (the statement the callable is exited with) * @param calleeExitFact the fact the flow starts with * @param callee the callable that is returned from @@ -103,7 +110,8 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl ): FlowFunction[Fact] /** - * Generate a flow function for a call-to-return flow + * Generate a flow function for a call-to-return flow. + * * @param callSite where the call-to-return flow starts (always a call statement) * @param callSiteFact the fact the flow starts with * @param callee the callable this flow is about @@ -114,7 +122,8 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl ): FlowFunction[Fact] /** - * Generate an edge function for a normal flow + * Generate an edge function for a normal flow. + * * @param source where the normal flow starts * @param sourceFact the fact the flow starts with * @param target where the normal flow ends @@ -128,7 +137,8 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] /** - * Generate an edge function for a call flow + * Generate an edge function for a call flow. + * * @param callSite where the call flow starts (always a call statement) * @param callSiteFact the fact the flow starts with * @param calleeEntry where the callable starts (the statement which the callable is started with) @@ -144,7 +154,8 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] /** - * Generate an edge function for a return flow + * Generate an edge function for a return flow. + * * @param calleeExit where the return flow starts (the statement the callable is exited with) * @param calleeExitFact the fact the flow starts with * @param callee the callable that is returned from @@ -164,7 +175,8 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl )(implicit propertyStore: PropertyStore): EdgeFunctionResult[Value] /** - * Generate an edge function for a call-to-return flow + * Generate an edge function for a call-to-return flow. + * * @param callSite where the call-to-return flow starts (always a call statement) * @param callSiteFact the fact the flow starts with * @param callee the callable this flow is about @@ -182,6 +194,7 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl /** * Whether precomputed flow and summary functions for a `(callSite, callSiteFact, callee)` combination exist * (resp. can be generated). + * * @param callSite where the flow starts * @param callSiteFact the fact the flow starts with * @param callee the callable this flow is about @@ -196,6 +209,7 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl * Generate a flow function that yields the facts that are valid when going through the callable and reaching the * return site. Similar to a call-to-return flow (cfg. [[getCallToReturnFlowFunction]]) but capturing the effects * that flow through the callable. + * * @param callSite where the flow starts (always a call statement) * @param callSiteFact the fact the flow starts with * @param callee the callable this flow is about @@ -215,6 +229,7 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl /** * Generate a summary function from a call-site node up to a return-site node (just what summary functions are in * the foundation paper, but in one step). + * * @param callSite where the flow starts (always a call statement) * @param callSiteFact the fact the flow starts with * @param callee the callable the flow is about @@ -240,6 +255,7 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl * Generate a flow function that yields the facts that are valid when going through the unknown callable and * reaching the return site. Similar to a call-to-return flow (cfg. [[getCallToReturnFlowFunction]]) but capturing * the effects that flow through the possible callables. + * * @param callSite where the flow starts (always a call statement) * @param callSiteFact the fact the flow starts with * @param returnSite where the flow ends (e.g. the next statement after the call) @@ -258,6 +274,7 @@ abstract class IDEProblem[Fact <: IDEFact, Value <: IDEValue, Statement, Callabl /** * Generate a summary function from a call-site node up to a return-site node (just what summary functions are in * the foundation paper, but in one step and for all callables that are possible call targets). + * * @param callSite where the flow starts (always a call statement) * @param callSiteFact the fact the flow starts with * @param returnSite where the flow ends (e.g. the next statement after the call) diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEValue.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEValue.scala index 5499446430..c5122906cd 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEValue.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/IDEValue.scala @@ -4,6 +4,8 @@ package ide package problem /** - * Interface representing IDE values + * Interface representing IDE values. + * + * @author Robin Körkemeier */ trait IDEValue diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/problem/MeetLattice.scala b/OPAL/ide/src/main/scala/org/opalj/ide/problem/MeetLattice.scala index bf7a103229..631f6df394 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/problem/MeetLattice.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/problem/MeetLattice.scala @@ -4,7 +4,9 @@ package ide package problem /** - * Interface representing the lattice that orders the IDE values + * Interface representing the lattice that orders the IDE values. + * + * @author Robin Körkemeier */ trait MeetLattice[Value <: IDEValue] { /** diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala index d038d3d91e..d79730271a 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/ICFG.scala @@ -8,7 +8,9 @@ import scala.collection import org.opalj.fpcf.Entity /** - * Interface representing the interprocedural control flow graph + * Interface representing the interprocedural control flow graph. + * + * @author Robin Körkemeier */ trait ICFG[Statement, Callable <: Entity] { /** @@ -47,7 +49,8 @@ trait ICFG[Statement, Callable <: Entity] { def getCallable(stmt: Statement): Callable /** - * Build a string representation of a statement. Only used for debugging purposes! + * Build a string representation of a statement. Is only used for debugging purposes. + * * @param indent to use on newlines (e.g. indentation for prettier logs) * @param short whether to build a long or a more compact string */ diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala index 2327715757..efc657a138 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysis.scala @@ -37,8 +37,11 @@ import org.opalj.ide.problem.InterimEdgeFunction import org.opalj.ide.util.Logging /** - * Basic solver for IDE problems. Uses the exhaustive/forward algorithm that was presented in the original IDE paper - * from 1996 as base. + * Basic solver for IDE problems. Uses the exhaustive algorithm that was presented in the original IDE paper from 1996 + * as base. For an example problem have a look at `LinearConstantPropagationProblem` in the TAC module. For an example + * of interacting IDE problems have a look at `LCPOnFieldsProblem` and `LinearConstantPropagationProblemExtended`. + * + * @author Robin Körkemeier */ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( val project: SomeProject, @@ -79,7 +82,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } /** - * Container class for simpler interaction and passing of the 'shared' data + * Container class for simpler interaction and passing of the shared data */ private class State( initialTargetCallablesEOptionP: EOptionP[ @@ -87,6 +90,9 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent IDETargetCallablesProperty[Callable] ] ) { + /** + * Collection of callables to compute results for. Used to optimize solver computation. + */ private val targetCallables = mutable.Set.empty[Callable] private var targetCallablesEOptionP: EOptionP[ @@ -135,7 +141,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent private val nodeWorkList: NodeWorkList = mutable.Queue.empty /** - * Store all calculated (intermediate) values + * Store all calculated (intermediate) values. Associated by callable for performant access. */ private val values: mutable.Map[Callable, Values] = mutable.Map.empty @@ -370,6 +376,7 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent /** * Run the IDE solver and calculate (and return) the result. This method should only be triggered in combination * with the IDE proxy! + * * @param entity Expected to be `None`. Other values do not cause errors but will only return empty (temporary) * results. * @return a result for each statement of the target callables plus one result for each target callable itself @@ -970,8 +977,9 @@ class IDEAnalysis[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Ent } /** - * Collect all statements that are reachable from a certain start set of statements - * @param originStmts the statements to start searchgin from + * Collect all statements that are reachable from a certain start set of statements. + * + * @param originStmts the statements to start searching from * @param filterPredicate an additional predicate the collected statements have to fulfill */ private def collectReachableStmts( diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala index 50b66b457b..e15a75a848 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/solver/IDEAnalysisProxy.scala @@ -29,6 +29,8 @@ import org.opalj.ide.util.Logging * The [[IDEAnalysis]] solver runs on callables only and additionally produces results for each statement of that * callable. This proxy analysis reduces all analysis requests to the callable and then forward it to the actual IDE * solver. + * + * @author Robin Körkemeier */ class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statement, Callable <: Entity]( val project: SomeProject, @@ -74,6 +76,7 @@ class IDEAnalysisProxy[Fact <: IDEFact, Value <: IDEValue, Statement, Callable < IDEPropertyMetaInformation[Fact, Value, Statement, Callable], IDETargetCallablesProperty[Callable] ]) => + /* Add target callable if not yet part of the set */ if (!eOptionP.hasUBP) { Some(InterimEUBP( propertyMetaInformation, diff --git a/OPAL/ide/src/main/scala/org/opalj/ide/util/Logging.scala b/OPAL/ide/src/main/scala/org/opalj/ide/util/Logging.scala index 1a987639c7..5910dde5e2 100644 --- a/OPAL/ide/src/main/scala/org/opalj/ide/util/Logging.scala +++ b/OPAL/ide/src/main/scala/org/opalj/ide/util/Logging.scala @@ -9,7 +9,9 @@ import org.opalj.log.LogContext import org.opalj.log.OPALLogger /** - * Logging extension for IDE + * Logging extension for IDE. + * + * @author Robin Körkemeier */ trait Logging { /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala index e0916f028f..b02177c792 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSAnalysisScheduler.scala @@ -15,7 +15,9 @@ import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEAnalysisSchedulerBase import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG /** - * Specialized IDE analysis scheduler for IFDS problems with Java programs + * Specialized IDE analysis scheduler for IFDS problems with Java programs. + * + * @author Robin Körkemeier */ abstract class JavaIFDSAnalysisScheduler[Fact <: IDEFact] extends JavaIDEAnalysisSchedulerBase[Fact, IFDSValue] { override def propertyMetaInformation: JavaIFDSPropertyMetaInformation[Fact] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala index 0276b3f752..8342d1dc4e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/integration/JavaIFDSPropertyMetaInformation.scala @@ -13,6 +13,8 @@ import org.opalj.ide.problem.IDEFact import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement /** - * Specialized property meta information for IFDS problems with Java programs + * Specialized property meta information for IFDS problems with Java programs. + * + * @author Robin Körkemeier */ trait JavaIFDSPropertyMetaInformation[Fact <: IDEFact] extends IFDSPropertyMetaInformation[Fact, JavaStatement, Method] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala index 85d246faf5..45312a848e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/ifds/problem/JavaIFDSProblem.scala @@ -13,6 +13,8 @@ import org.opalj.ide.problem.IDEFact import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement /** - * Specialized IFDS problem for Java programs based on an IDE problem + * Specialized IFDS problem for Java programs based on an IDE problem. + * + * @author Robin Körkemeier */ abstract class JavaIFDSProblem[Fact <: IDEFact] extends IFDSProblem[Fact, JavaStatement, Method] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala index 857be1fd68..99fcc0aef6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -27,6 +27,8 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG /** * Linear constant propagation on fields as IDE analysis. This implementation is mainly intended to be an example of a * cyclic IDE analysis (see [[LCPOnFieldsProblem]]). + * + * @author Robin Körkemeier */ abstract class LCPOnFieldsAnalysisScheduler extends JavaIDEAnalysisScheduler[LCPOnFieldsFact, LCPOnFieldsValue] with JavaIDEAnalysisSchedulerBase.ForwardICFG { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala index 309ad516c8..1558b27d6b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsPropertyMetaInformation.scala @@ -13,7 +13,9 @@ import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFiel import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEPropertyMetaInformation /** - * Meta information for linear constant propagation on fields + * Meta information for linear constant propagation on fields. + * + * @author Robin Körkemeier */ object LCPOnFieldsPropertyMetaInformation extends JavaIDEPropertyMetaInformation[LCPOnFieldsFact, LCPOnFieldsValue] { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala index 337f4b2378..6ccc53bd7d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LinearConstantPropagationAnalysisSchedulerExtended.scala @@ -22,7 +22,9 @@ import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG /** - * Extended linear constant propagation as IDE analysis + * Extended linear constant propagation as IDE analysis. + * + * @author Robin Körkemeier */ abstract class LinearConstantPropagationAnalysisSchedulerExtended extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala index 7db4f9bd59..5360b5fe17 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsEdgeFunctions.scala @@ -19,7 +19,9 @@ import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.pro import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue /** - * Edge function holding the current object state (in form of its field-value mapping) + * Edge function holding the current object state (in form of its field-value mapping). + * + * @author Robin Körkemeier */ case class ObjectEdgeFunction( values: immutable.Map[String, LinearConstantPropagationValue] @@ -93,14 +95,18 @@ case class ObjectEdgeFunction( } /** - * Edge function for initializing an object + * Edge function for initializing an object. + * + * @author Robin Körkemeier */ object NewObjectEdgeFunction extends ObjectEdgeFunction(immutable.Map.empty) { override def toString: String = "NewObjectEdgeFunction()" } /** - * Edge function modeling the effect of writing the field of an object + * Edge function modeling the effect of writing the field of an object. + * + * @author Robin Körkemeier */ case class PutFieldEdgeFunction( fieldName: String, @@ -177,6 +183,8 @@ case class PutFieldEdgeFunction( * elements. The array length is not tracked in this problem definition, thus arbitrary indices can be read and written. * The initial value is used as a fallback/default value for elements that are not in the collection of elements yet * (will likely be one of `ConstantValue(0)` and `VariableValue`). + * + * @author Robin Körkemeier */ class ArrayEdgeFunction( val initValue: LinearConstantPropagationValue, @@ -281,7 +289,9 @@ object ArrayEdgeFunction { } /** - * Edge function for initializing an array + * Edge function for initializing an array. + * + * @author Robin Körkemeier */ case class NewArrayEdgeFunction( override val initValue: LinearConstantPropagationValue = linear_constant_propagation.problem.ConstantValue(0) @@ -290,7 +300,9 @@ case class NewArrayEdgeFunction( } /** - * Edge function modeling the effect of writing an element of an array + * Edge function modeling the effect of writing an element of an array. + * + * @author Robin Körkemeier */ case class PutElementEdgeFunction( index: LinearConstantPropagationValue, @@ -391,7 +403,9 @@ case class PutElementEdgeFunction( } /** - * Edge function modeling the effect of when a static field gets written + * Edge function modeling the effect of when a static field gets written. + * + * @author Robin Körkemeier */ case class PutStaticFieldEdgeFunction( value: LinearConstantPropagationValue @@ -438,7 +452,9 @@ case class PutStaticFieldEdgeFunction( } /** - * Edge function for cases where a value is unknown + * Edge function for cases where a value is unknown. + * + * @author Robin Körkemeier */ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LCPOnFieldsValue](UnknownValue) { override def composeWith( @@ -478,7 +494,9 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LCPOnFieldsValue](Unk } /** - * Edge function for cases where a value is variable + * Edge function for cases where a value is variable. + * + * @author Robin Körkemeier */ object VariableValueEdgeFunction extends AllBottomEdgeFunction[LCPOnFieldsValue](VariableValue) { override def composeWith(secondEdgeFunction: EdgeFunction[LCPOnFieldsValue]): EdgeFunction[LCPOnFieldsValue] = { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala index 682c6b2402..a8c4967582 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsFact.scala @@ -12,17 +12,23 @@ import org.opalj.br.ObjectType import org.opalj.ide.problem.IDEFact /** - * Type for modeling facts for linear constant propagation on fields + * Type for modeling facts for linear constant propagation on fields. + * + * @author Robin Körkemeier */ trait LCPOnFieldsFact extends IDEFact /** - * Fact to use as null fact + * Fact to use as null fact. + * + * @author Robin Körkemeier */ case object NullFact extends LCPOnFieldsFact /** - * Common type for different types of entities + * Common type for different types of entities. + * + * @author Robin Körkemeier */ trait AbstractEntityFact extends LCPOnFieldsFact { /** @@ -38,7 +44,9 @@ trait AbstractEntityFact extends LCPOnFieldsFact { } /** - * Type for object facts + * Type for object facts. + * + * @author Robin Körkemeier */ trait AbstractObjectFact extends AbstractEntityFact { def toObjectFact: ObjectFact = ObjectFact(name, definedAtIndex) @@ -47,7 +55,9 @@ trait AbstractObjectFact extends AbstractEntityFact { } /** - * Fact representing a seen object variable + * Fact representing a seen object variable. + * + * @author Robin Körkemeier */ case class ObjectFact(name: String, definedAtIndex: Int) extends AbstractObjectFact { override def toObjectFact: ObjectFact = this @@ -56,22 +66,29 @@ case class ObjectFact(name: String, definedAtIndex: Int) extends AbstractObjectF } /** - * Fact representing a seen object variable and modeling that it gets initialized + * Fact representing a seen object variable and modeling that it gets initialized. + * + * @author Robin Körkemeier */ case class NewObjectFact(name: String, definedAtIndex: Int) extends AbstractObjectFact { override def toString: String = s"NewObjectFact($name)" } /** - * Fact representing a seen object variable and modeling that one of its fields gets written + * Fact representing a seen object variable and modeling that one of its fields gets written. + * * @param fieldName the name of the field that gets written + * + * @author Robin Körkemeier */ case class PutFieldFact(name: String, definedAtIndex: Int, fieldName: String) extends AbstractObjectFact { override def toString: String = s"PutFieldFact($name, $fieldName)" } /** - * Type for array facts + * Type for array facts. + * + * @author Robin Körkemeier */ trait AbstractArrayFact extends AbstractEntityFact { def toArrayFact: ArrayFact = ArrayFact(name, definedAtIndex) @@ -80,7 +97,9 @@ trait AbstractArrayFact extends AbstractEntityFact { } /** - * Fact representing a seen array variable + * Fact representing a seen array variable. + * + * @author Robin Körkemeier */ case class ArrayFact(name: String, definedAtIndex: Int) extends AbstractArrayFact { override def toArrayFact: ArrayFact = this @@ -89,21 +108,27 @@ case class ArrayFact(name: String, definedAtIndex: Int) extends AbstractArrayFac } /** - * Fact representing a seen array variable and modeling that it gets initialized + * Fact representing a seen array variable and modeling that it gets initialized. + * + * @author Robin Körkemeier */ case class NewArrayFact(name: String, definedAtIndex: Int) extends AbstractArrayFact { override def toString: String = s"NewArrayFact($name)" } /** - * Fact representing a seen array variable and modeling that one of its elements gets written + * Fact representing a seen array variable and modeling that one of its elements gets written. + * + * @author Robin Körkemeier */ case class PutElementFact(name: String, definedAtIndex: Int) extends AbstractArrayFact { override def toString: String = s"PutElementFact($name)" } /** - * Type for facts for static fields + * Type for facts for static fields. + * + * @author Robin Körkemeier */ trait AbstractStaticFieldFact extends LCPOnFieldsFact { /** @@ -120,7 +145,9 @@ trait AbstractStaticFieldFact extends LCPOnFieldsFact { } /** - * Fact representing a seen static field + * Fact representing a seen static field. + * + * @author Robin Körkemeier */ case class StaticFieldFact(objectType: ObjectType, fieldName: String) extends AbstractStaticFieldFact { override def toStaticFieldFact: StaticFieldFact = this @@ -129,7 +156,9 @@ case class StaticFieldFact(objectType: ObjectType, fieldName: String) extends Ab } /** - * Fact representing a seen static field and modeling that it gets written + * Fact representing a seen static field and modeling that it gets written. + * + * @author Robin Körkemeier */ case class PutStaticFieldFact(objectType: ObjectType, fieldName: String) extends AbstractStaticFieldFact { override def toString: String = s"PutStaticFieldFact(${objectType.simpleName}, $fieldName)" diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala index d15001806b..89d1ada4b6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsLattice.scala @@ -12,7 +12,9 @@ import org.opalj.ide.problem.MeetLattice import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationLattice /** - * Lattice used for linear constant propagation on fields + * Lattice used for linear constant propagation on fields. + * + * @author Robin Körkemeier */ object LCPOnFieldsLattice extends MeetLattice[LCPOnFieldsValue] { override def top: LCPOnFieldsValue = UnknownValue diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index acfd3d2c35..1fec6b14d6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -44,6 +44,8 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall * method where the value is passed as an argument (e.g. a classical setter). Similar, array read accesses can only be * resolved if the index is a constant literal. There also is just minimal support for static fields. * This implementation is mainly intended to be an example of a cyclic IDE analysis. + * + * @author Robin Körkemeier */ class LCPOnFieldsProblem( project: SomeProject, @@ -63,8 +65,10 @@ class LCPOnFieldsProblem( (if (callee.isStatic) { immutable.Set.empty } else { + /* Add fact for `this` */ immutable.Set(ObjectFact("param0", -1)) }) ++ + /* Add facts for other parameters */ callee.parameterTypes .zipWithIndex .filter { case (paramType, _) => paramType.isObjectType || paramType.isArrayType } @@ -76,6 +80,7 @@ class LCPOnFieldsProblem( } } .toSet ++ + /* Add facts for static fields of class */ callee.classFile .fields .filter(_.isStatic) @@ -720,6 +725,7 @@ class LCPOnFieldsProblem( ) { identityEdgeFunction } else { + /* It is unknown what the callee does with the object */ VariableValueEdgeFunction } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala index 3324962d67..36d1a35207 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsValue.scala @@ -14,17 +14,23 @@ import org.opalj.ide.problem.IDEValue import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.problem.LinearConstantPropagationValue /** - * Type for modeling values for linear constant propagation on fields + * Type for modeling values for linear constant propagation on fields. + * + * @author Robin Körkemeier */ trait LCPOnFieldsValue extends IDEValue /** - * Value not known (yet) + * Value not known (yet). + * + * @author Robin Körkemeier */ case object UnknownValue extends LCPOnFieldsValue /** - * Value representing the state of an object + * Value representing the state of an object. + * + * @author Robin Körkemeier */ case class ObjectValue(values: immutable.Map[String, LinearConstantPropagationValue]) extends LCPOnFieldsValue { override def toString: String = @@ -32,7 +38,9 @@ case class ObjectValue(values: immutable.Map[String, LinearConstantPropagationVa } /** - * Value representing the state of an array + * Value representing the state of an array. + * + * @author Robin Körkemeier */ case class ArrayValue( initValue: LinearConstantPropagationValue, @@ -43,7 +51,9 @@ case class ArrayValue( } /** - * Value representing the value of a static field + * Value representing the value of a static field. + * + * @author Robin Körkemeier */ case class StaticFieldValue( value: LinearConstantPropagationValue @@ -52,6 +62,8 @@ case class StaticFieldValue( } /** - * Value is variable (not really used currently, mainly for completeness) + * Value is variable (not really used currently, mainly for completeness). + * + * @author Robin Körkemeier */ case object VariableValue extends LCPOnFieldsValue diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala index eb796d22fa..4eef7375b7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LinearConstantPropagationProblemExtended.scala @@ -37,6 +37,8 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.V /** * Extended definition of the linear constant propagation problem, trying to resolve field accesses with the LCP on * fields analysis. + * + * @author Robin Körkemeier */ class LinearConstantPropagationProblemExtended extends LinearConstantPropagationProblem { override def isArrayLoadExpressionGeneratedByFact( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala index f875491f65..c64a3d18f8 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationAnalysisScheduler.scala @@ -18,7 +18,9 @@ import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG /** - * Linear constant propagation as IDE analysis + * Linear constant propagation as IDE analysis. + * + * @author Robin Körkemeier */ abstract class LinearConstantPropagationAnalysisScheduler extends JavaIDEAnalysisScheduler[LinearConstantPropagationFact, LinearConstantPropagationValue] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala index a3c9518b7d..ab3f7e4912 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/LinearConstantPropagationPropertyMetaInformation.scala @@ -13,7 +13,9 @@ import org.opalj.tac.fpcf.analyses.ide.instances.linear_constant_propagation.pro import org.opalj.tac.fpcf.analyses.ide.integration.JavaIDEPropertyMetaInformation /** - * Meta information for linear constant propagation + * Meta information for linear constant propagation. + * + * @author Robin Körkemeier */ object LinearConstantPropagationPropertyMetaInformation extends JavaIDEPropertyMetaInformation[LinearConstantPropagationFact, LinearConstantPropagationValue] { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala index 88f1b63623..7bd2f8fac8 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationEdgeFunctions.scala @@ -14,7 +14,9 @@ import org.opalj.ide.problem.EdgeFunction import org.opalj.ide.problem.IdentityEdgeFunction /** - * Edge function to calculate the value of a variable `i` for a statement `val i = a * x + b` + * Edge function to calculate the value of a variable `i` for a statement `val i = a * x + b`. + * + * @author Robin Körkemeier */ case class LinearCombinationEdgeFunction( a: Int, @@ -101,7 +103,9 @@ case class LinearCombinationEdgeFunction( } /** - * Edge function for variables whose value is unknown + * Edge function for variables whose value is unknown. + * + * @author Robin Körkemeier */ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LinearConstantPropagationValue](UnknownValue) { override def composeWith( @@ -141,7 +145,9 @@ object UnknownValueEdgeFunction extends AllTopEdgeFunction[LinearConstantPropaga } /** - * Edge function for a variable that is definitely not constant + * Edge function for a variable that is definitely not constant. + * + * @author Robin Körkemeier */ object VariableValueEdgeFunction extends AllBottomEdgeFunction[LinearConstantPropagationValue](VariableValue) { override def composeWith( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationFact.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationFact.scala index bb37b19ba6..736977c2dd 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationFact.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationFact.scala @@ -11,19 +11,26 @@ package problem import org.opalj.ide.problem.IDEFact /** - * Type for modeling facts for linear constant propagation + * Type for modeling facts for linear constant propagation. + * + * @author Robin Körkemeier */ trait LinearConstantPropagationFact extends IDEFact /** - * Fact to use as null fact + * Fact to use as null fact. + * + * @author Robin Körkemeier */ case object NullFact extends LinearConstantPropagationFact /** - * Fact representing a seen variable + * Fact representing a seen variable. + * * @param name the name of the variable (e.g. `lv0`) * @param definedAtIndex where the variable is defined (used to uniquely identify a variable/variable fact) + * + * @author Robin Körkemeier */ case class VariableFact(name: String, definedAtIndex: Int) extends LinearConstantPropagationFact { override def toString: String = s"VariableFact($name)" diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationLattice.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationLattice.scala index 61cd4b762a..250b0fe92a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationLattice.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationLattice.scala @@ -11,7 +11,9 @@ package problem import org.opalj.ide.problem.MeetLattice /** - * Lattice used for linear constant propagation + * Lattice used for linear constant propagation. + * + * @author Robin Körkemeier */ object LinearConstantPropagationLattice extends MeetLattice[LinearConstantPropagationValue] { override def top: LinearConstantPropagationValue = UnknownValue diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala index ff45364ca4..95b73ae6c9 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationProblem.scala @@ -26,7 +26,9 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement.StmtAsCall /** - * Definition of the linear constant propagation problem + * Definition of the linear constant propagation problem. + * + * @author Robin Körkemeier */ class LinearConstantPropagationProblem extends JavaIDEProblem[LinearConstantPropagationFact, LinearConstantPropagationValue] { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationValue.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationValue.scala index 4806e25b6a..496e52c020 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationValue.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/linear_constant_propagation/problem/LinearConstantPropagationValue.scala @@ -11,21 +11,29 @@ package problem import org.opalj.ide.problem.IDEValue /** - * Type for modeling values for linear constant propagation + * Type for modeling values for linear constant propagation. + * + * @author Robin Körkemeier */ trait LinearConstantPropagationValue extends IDEValue /** - * Value not known (yet) + * Value not known (yet). + * + * @author Robin Körkemeier */ case object UnknownValue extends LinearConstantPropagationValue /** - * A constant value + * A constant value. + * + * @author Robin Körkemeier */ case class ConstantValue(c: Int) extends LinearConstantPropagationValue /** - * Value is variable + * Value is variable. + * + * @author Robin Körkemeier */ case object VariableValue extends LinearConstantPropagationValue diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala index 5eb08c3767..4048ef3109 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisScheduler.scala @@ -13,7 +13,9 @@ import org.opalj.tac.fpcf.analyses.ide.problem.JavaIDEProblem import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG /** - * Specialized IDE analysis scheduler for Java programs + * Specialized IDE analysis scheduler for Java programs. + * + * @author Robin Körkemeier */ abstract class JavaIDEAnalysisScheduler[Fact <: IDEFact, Value <: IDEValue] extends JavaIDEAnalysisSchedulerBase[Fact, Value] { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala index d27416f8f2..6d3f1a96fa 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala @@ -27,7 +27,9 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement import org.opalj.tac.fpcf.properties.TACAI /** - * A base IDE analysis scheduler for Java programs + * A base IDE analysis scheduler for Java programs. + * + * @author Robin Körkemeier */ abstract class JavaIDEAnalysisSchedulerBase[Fact <: IDEFact, Value <: IDEValue] extends IDEAnalysisScheduler[Fact, Value, JavaStatement, Method, JavaICFG] { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala index 7f7c88b9ee..95ec423b28 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEPropertyMetaInformation.scala @@ -13,7 +13,9 @@ import org.opalj.ide.problem.IDEValue import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement /** - * Specialized property meta information for IDE problems with Java programs + * Specialized property meta information for IDE problems with Java programs. + * + * @author Robin Körkemeier */ trait JavaIDEPropertyMetaInformation[Fact <: IDEFact, Value <: IDEValue] extends IDEPropertyMetaInformation[Fact, Value, JavaStatement, Method] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala index 65eb6635b0..7aeae078f0 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/problem/JavaIDEProblem.scala @@ -13,6 +13,8 @@ import org.opalj.ide.problem.IDEValue import org.opalj.tac.fpcf.analyses.ide.solver.JavaStatement /** - * Specialized IDE problem for Java programs + * Specialized IDE problem for Java programs. + * + * @author Robin Körkemeier */ abstract class JavaIDEProblem[Fact <: IDEFact, Value <: IDEValue] extends IDEProblem[Fact, Value, JavaStatement, Method] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBackwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBackwardICFG.scala index c493d98846..1f4ca72dd2 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBackwardICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBackwardICFG.scala @@ -16,6 +16,8 @@ import org.opalj.br.analyses.SomeProject /** * Interprocedural control flow graph for Java programs in backward direction. This implementation is based on the * [[org.opalj.tac.fpcf.analyses.ifds.JavaBackwardICFG]] from IFDS. + * + * @author Robin Körkemeier */ class JavaBackwardICFG(project: SomeProject) extends JavaBaseICFG(project) { override def getStartStatements(callable: Method): collection.Set[JavaStatement] = { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala index 059d3b13e8..6098c708ba 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaBaseICFG.scala @@ -25,6 +25,8 @@ import org.opalj.value.ValueInformation /** * Base interprocedural control flow graph for Java programs. This implementation is based on the * [[org.opalj.tac.fpcf.analyses.ifds.JavaICFG]] from IFDS. + * + * @author Robin Körkemeier */ abstract class JavaBaseICFG(project: SomeProject) extends JavaICFG { private val lazyTacProvider: Method => AITACode[TACMethodParameter, ValueInformation] = { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala index cd90ca2457..f19fd6ef0b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaForwardICFG.scala @@ -16,6 +16,8 @@ import org.opalj.br.analyses.SomeProject /** * Interprocedural control flow graph for Java programs in forward direction. This implementation is based on the * [[org.opalj.tac.fpcf.analyses.ifds.JavaForwardICFG]] from IFDS. + * + * @author Robin Körkemeier */ class JavaForwardICFG(project: SomeProject) extends JavaBaseICFG(project) { override def getStartStatements(callable: Method): collection.Set[JavaStatement] = { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala index fd87aa8b31..dbee2b59c6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaICFG.scala @@ -10,6 +10,8 @@ import org.opalj.br.Method import org.opalj.ide.solver.ICFG /** - * Interprocedural control flow graph for Java programs + * Interprocedural control flow graph for Java programs. + * + * @author Robin Körkemeier */ trait JavaICFG extends ICFG[JavaStatement, Method] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala index 3e84dee3b3..c1e9997a8c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/solver/JavaStatement.scala @@ -12,9 +12,12 @@ import org.opalj.br.cfg.CFG import org.opalj.value.ValueInformation /** - * Class to model statements used with IDE analyses + * Class to model statements used with IDE analyses. + * * @param pc the pc of the statement in the code * @param isReturnNode whether the statement models the return node of a call + * + * @author Robin Körkemeier */ case class JavaStatement( method: Method, From c7901027cc68e695ba281c0565fe3958abdc3e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Wed, 12 Mar 2025 19:21:26 +0100 Subject: [PATCH 166/167] Fix doc references --- .../lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala | 2 +- .../ide/integration/JavaIDEAnalysisSchedulerBase.scala | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala index 99fcc0aef6..7c0a6eefe6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -26,7 +26,7 @@ import org.opalj.tac.fpcf.analyses.ide.solver.JavaICFG /** * Linear constant propagation on fields as IDE analysis. This implementation is mainly intended to be an example of a - * cyclic IDE analysis (see [[LCPOnFieldsProblem]]). + * cyclic IDE analysis (see [[org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsProblem]]). * * @author Robin Körkemeier */ diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala index 6d3f1a96fa..595f10c321 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/integration/JavaIDEAnalysisSchedulerBase.scala @@ -54,14 +54,14 @@ abstract class JavaIDEAnalysisSchedulerBase[Fact <: IDEFact, Value <: IDEValue] object JavaIDEAnalysisSchedulerBase { /** - * Trait to drop-in [[RTACallGraphKey]] as [[callGraphKey]] + * Trait to drop-in [[org.opalj.tac.cg.RTACallGraphKey]] as [[callGraphKey]] */ trait RTACallGraph { val callGraphKey: CallGraphKey = RTACallGraphKey } /** - * Trait to drop-in [[JavaForwardICFG]] for [[createICFG]] + * Trait to drop-in [[org.opalj.tac.fpcf.analyses.ide.solver.JavaForwardICFG]] for [[createICFG]] */ trait ForwardICFG { def createICFG(project: SomeProject): JavaICFG = { @@ -70,7 +70,7 @@ object JavaIDEAnalysisSchedulerBase { } /** - * Trait to drop-in [[JavaBackwardICFG]] for [[createICFG]] + * Trait to drop-in [[org.opalj.tac.fpcf.analyses.ide.solver.JavaBackwardICFG]] for [[createICFG]] */ trait BackwardICFG { def createICFG(project: SomeProject): JavaICFG = { From 47d1ce652974a383f5aef52a3de609430ec61c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=B6rkemeier?= Date: Mon, 21 Apr 2025 14:52:03 +0200 Subject: [PATCH 167/167] Replace immutability with assignability --- .../ide/lcp_on_fields/LCPOnFieldsTests.scala | 6 ----- .../LCPOnFieldsAnalysisScheduler.scala | 4 +-- .../problem/LCPOnFieldsProblem.scala | 26 ++++++++++--------- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala index 76842e07f4..3809b0f65e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ide/lcp_on_fields/LCPOnFieldsTests.scala @@ -5,12 +5,9 @@ package ide package lcp_on_fields import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.analyses.immutability.LazyClassImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.immutability.LazyTypeImmutabilityAnalysis import org.opalj.fpcf.properties.lcp_on_fields.LCPOnFieldsProperty import org.opalj.fpcf.properties.linear_constant_propagation.LinearConstantPropagationProperty import org.opalj.ide.integration.LazyIDEAnalysisProxyScheduler -import org.opalj.tac.fpcf.analyses.LazyFieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.fieldaccess.EagerFieldAccessInformationAnalysis import org.opalj.tac.fpcf.analyses.fieldassignability.LazyL2FieldAssignabilityAnalysis @@ -41,10 +38,7 @@ class LCPOnFieldsTests extends IDEPropertiesTest { } } }, - LazyFieldImmutabilityAnalysis, LazyL2FieldAssignabilityAnalysis, - LazyTypeImmutabilityAnalysis, - LazyClassImmutabilityAnalysis, EagerFieldAccessInformationAnalysis )) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala index 7c0a6eefe6..32a3a84c23 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/LCPOnFieldsAnalysisScheduler.scala @@ -12,7 +12,7 @@ import scala.collection.immutable import org.opalj.br.analyses.DeclaredFieldsKey import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties.immutability.FieldImmutability +import org.opalj.br.fpcf.properties.immutability.FieldAssignability import org.opalj.fpcf.PropertyBounds import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsFact import org.opalj.tac.fpcf.analyses.ide.instances.lcp_on_fields.problem.LCPOnFieldsProblem @@ -49,7 +49,7 @@ abstract class LCPOnFieldsAnalysisScheduler extends JavaIDEAnalysisScheduler[LCP override def uses: Set[PropertyBounds] = super.uses.union(immutable.Set( - PropertyBounds.ub(FieldImmutability), + PropertyBounds.ub(FieldAssignability), PropertyBounds.ub(LinearConstantPropagationPropertyMetaInformation) )) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala index 1fec6b14d6..bbb7b53c7a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ide/instances/lcp_on_fields/problem/LCPOnFieldsProblem.scala @@ -19,8 +19,10 @@ import org.opalj.br.IntegerType import org.opalj.br.Method import org.opalj.br.analyses.DeclaredFieldsKey import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties.immutability.FieldImmutability -import org.opalj.br.fpcf.properties.immutability.TransitivelyImmutableField +import org.opalj.br.fpcf.properties.immutability.EffectivelyNonAssignable +import org.opalj.br.fpcf.properties.immutability.FieldAssignability +import org.opalj.br.fpcf.properties.immutability.LazilyInitialized +import org.opalj.br.fpcf.properties.immutability.NonAssignable import org.opalj.fpcf.FinalP import org.opalj.fpcf.InterimUBP import org.opalj.fpcf.PropertyStore @@ -111,23 +113,23 @@ class LCPOnFieldsProblem( /* We enhance the analysis with immutability information. When a static field is immutable and we have knowledge * of an assignment site, then this will always be the value of the field. This way we can make this analysis * more precise without the need to add precise handling of static initializers. */ - val fieldImmutabilityEOptionP = propertyStore(field, FieldImmutability.key) + val fieldAssignabilityEOptionP = propertyStore(field, FieldAssignability.key) - fieldImmutabilityEOptionP match { - case FinalP(fieldImmutability) => - fieldImmutability match { - case TransitivelyImmutableField => + fieldAssignabilityEOptionP match { + case FinalP(fieldAssignability) => + fieldAssignability match { + case NonAssignable | EffectivelyNonAssignable | LazilyInitialized => val value = getValueForGetStaticExprByStaticInitializer(field) FinalEdgeFunction(PutStaticFieldEdgeFunction(value)) case _ => FinalEdgeFunction(PutStaticFieldEdgeFunction(linear_constant_propagation.problem.VariableValue)) } - case InterimUBP(fieldImmutability) => - fieldImmutability match { - case TransitivelyImmutableField => + case InterimUBP(fieldAssignability) => + fieldAssignability match { + case NonAssignable | EffectivelyNonAssignable | LazilyInitialized => val value = getValueForGetStaticExprByStaticInitializer(field) - InterimEdgeFunction(PutStaticFieldEdgeFunction(value), immutable.Set(fieldImmutabilityEOptionP)) + InterimEdgeFunction(PutStaticFieldEdgeFunction(value), immutable.Set(fieldAssignabilityEOptionP)) case _ => FinalEdgeFunction(PutStaticFieldEdgeFunction(linear_constant_propagation.problem.VariableValue)) } @@ -135,7 +137,7 @@ class LCPOnFieldsProblem( case _ => InterimEdgeFunction( PutStaticFieldEdgeFunction(linear_constant_propagation.problem.UnknownValue), - immutable.Set(fieldImmutabilityEOptionP) + immutable.Set(fieldAssignabilityEOptionP) ) } }