123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2019 The MOPSA Project. *)(* *)(* This program is free software: you can redistribute it and/or modify *)(* it under the terms of the GNU Lesser General Public License as published *)(* by the Free Software Foundation, either version 3 of the License, or *)(* (at your option) any later version. *)(* *)(* This program is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU Lesser General Public License for more details. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* along with this program. If not, see <http://www.gnu.org/licenses/>. *)(* *)(****************************************************************************)(** Changes are used to log the statements executed during the computation of
a post-state *)openAst.VaropenAst.StmtopenAst.ExpropenMopsa_utilsopenPathtypechange=|Change_empty|Change_blockofstmtlist|Change_seqofchangelist|Change_joinofchange*change|Change_meetofchange*changeletrecpp_changefmt=function|Change_empty->()|Change_blockb->Format.fprintffmt"@[<hv2>{ %a }@]"(Format.pp_print_list~pp_sep:(funfmt()->Format.fprintffmt";@ ")pp_stmt)b|Change_seqseq->Format.fprintffmt"@[<hv2>{ %a }@]"(Format.pp_print_list~pp_sep:(funfmt()->Format.fprintffmt"⨟@ ")pp_change)seq|Change_join(e1,e2)->Format.fprintffmt"@[<hv2>( %a ∨ @ %a )@]"pp_changee1pp_changee2|Change_meet(e1,e2)->Format.fprintffmt"@[<hv2>( %a ∧ @ %a )@]"pp_changee1pp_changee2letreccompare_changee1e2=ife1==e2then0elsematche1,e2with|Change_empty,Change_empty->0|Change_blocks1,Change_blocks2->Compare.listcompare_stmts1s2|Change_seqs1,Change_seqs2->Compare.listcompare_changes1s2|Change_join(e1,e2),Change_join(f1,f2)->Compare.paircompare_changecompare_change(e1,e2)(f1,f2)|Change_meet(e1,e2),Change_meet(f1,f2)->Compare.paircompare_changecompare_change(e1,e2)(f1,f2)|_->comparee1e2letempty_change=Change_emptyletrecis_empty_change=function|Change_empty->true|Change_block_->false|Change_seql->List.for_allis_empty_changel|Change_join(e1,e2)->is_empty_changee1&&is_empty_changee2|Change_meet(e1,e2)->is_empty_changee1&&is_empty_changee2letjoin_changee1e2=ife1==e2thene1elseifis_empty_changee1thene2elseifis_empty_changee2thene1elsematche1,e2with|Change_empty,x->x|x,Change_empty->x|_->Change_join(e1,e2)letmeet_changee1e2=ife1==e2thene1elseifis_empty_changee1thene2elseifis_empty_changee2thene1elsematche1,e2with|Change_empty,x->x|x,Change_empty->x|_->Change_meet(e1,e2)(** Join two changes *)letadd_stmt_to_changes=function|Change_empty->Change_block[s]|Change_blockb->Change_block(s::b)|Change_seql->Change_seq(Change_block[s]::l)|e->Change_seq[Change_block[s];e](** Meet two changes *)letrecconcat_changeoldrecent=ifis_empty_changeoldthenrecentelseifis_empty_changerecentthenoldelsematchold,recentwith|Change_empty,Change_empty->Change_empty|Change_blockb1,Change_blockb2->Change_block(b2@b1)|Change_seql1,Change_seql2->Change_seq(l2@l1)|Change_seql,x->Change_seq(x::l)|x,Change_seql->Change_seq(l@[x])|_->Change_seq[recent;old]typechange_map=changePathMap.tletpp_change_mapfmtmap=ifPathMap.is_emptymapthenFormat.pp_print_stringfmt""elseFormat.fprintffmt"@[<v>%a@]"(Format.pp_print_list~pp_sep:(funfmt()->Format.fprintffmt"@,")(funfmt(path,change)->Format.fprintffmt"%a: %a"pp_pathpathpp_changechange))(PathMap.bindingsmap)letcompare_change_mapm1m2=ifm1==m2then0elsePathMap.comparecompare_changem1m2letempty_change_map=PathMap.emptyletsingleton_change_mappathchange=PathMap.singletonpathchangeletis_empty_change_mapm=PathMap.for_all(fun_e->is_empty_changee)mletconcat_change_mapoldrecent=PathMap.map2zo(funp1e1->e1)(funp2e2->e2)(funpe1e2->concat_changee1e2)oldrecentletjoin_change_mapmap1map2=PathMap.map2zo(funp1e1->e1)(funp1e2->e2)(funpe1e2->join_changee1e2)map1map2letmeet_change_mapmap1map2=PathMap.map2zo(funp1e1->e1)(funp1e2->e2)(funpe1e2->meet_changee1e2)map1map2letget_changepathmap=matchPathMap.find_optpathmapwith|None->Change_empty|Somee->eletadd_stmt_to_change_mapstmtpathmap=letchange=matchPathMap.find_optpathmapwith|None->Change_block[stmt]|Someold->add_stmt_to_changestmtoldinPathMap.addpathchangemap(** {2 Generic merge} *)(** ***************** *)(** Change of a statement in terms of modified and removed variables *)typechange_vars={modified:VarSet.t;removed:VarSet.t;}letcompare_change_varsve1ve2=Compare.pairVarSet.compareVarSet.compare(ve1.modified,ve1.removed)(ve2.modified,ve2.removed)letis_empty_change_varse=VarSet.is_emptye.modified&&VarSet.is_emptye.removed(** Get the changes of a statement *)letrecget_stmt_change_vars~customstmt:change_vars=matchcustomstmtwith|Someve->ve|None->matchskindstmtwith|S_add{ekind=E_var(var,_)}->{modified=VarSet.singletonvar;removed=VarSet.empty}|S_remove{ekind=E_var(var,_)}->{modified=VarSet.empty;removed=VarSet.singletonvar;}|S_assign({ekind=E_var(var,_)},_)->{modified=VarSet.singletonvar;removed=VarSet.empty}|S_assume(e)->{modified=VarSet.empty;removed=VarSet.empty}|S_rename({ekind=E_var(var1,_)},{ekind=E_var(var2,_)})->{modified=VarSet.singletonvar2;removed=VarSet.singletonvar1}|S_expand({ekind=E_var(var,_)},vl)->{modified=VarSet.of_list(List.map(function{ekind=E_var(v,_)}->v|_->assertfalse)vl);removed=VarSet.empty}|S_fold({ekind=E_var(var,_)},vl)->{modified=VarSet.singletonvar;removed=VarSet.of_list(List.map(function{ekind=E_var(v,_)}->v|_->assertfalse)vl)}|S_forget{ekind=E_var(var,_)}->{modified=VarSet.singletonvar;removed=VarSet.empty}|_->Exceptions.panic"generic merge: unsupported statement %a"pp_stmtstmtletrecget_change_vars~custom=function|Change_empty->{modified=VarSet.empty;removed=VarSet.empty}|Change_blockb->(** Fold from the right because changes are stored in reverse order
(head of the list is the last recorded change) *)List.fold_right(funsacc->letchange=get_stmt_change_vars~customsin{modified=VarSet.unionchange.modified(VarSet.diffacc.modifiedchange.removed);removed=VarSet.unionchange.removed(VarSet.diffacc.removedchange.modified);})b{modified=VarSet.empty;removed=VarSet.empty}|Change_seql->(** Fold from the right because changes are stored in reverse order
(head of the list is the last recorded change) *)List.fold_right(funeacc->letchange=get_change_vars~customein{modified=VarSet.unionchange.modified(VarSet.diffacc.modifiedchange.removed);removed=VarSet.unionchange.removed(VarSet.diffacc.removedchange.modified);})l{modified=VarSet.empty;removed=VarSet.empty}|Change_join(e1,e2)->letve1=get_change_vars~custome1andve2=get_change_vars~custome2in{modified=VarSet.unionve1.modifiedve2.modified;removed=VarSet.union(VarSet.diffve1.removedve2.modified)(VarSet.diffve2.removedve1.modified);}|Change_meet(e1,e2)->letve1=get_change_vars~custome1andve2=get_change_vars~custome2in{modified=VarSet.unionve1.modifiedve2.modified;removed=VarSet.union(VarSet.diffve1.removedve2.modified)(VarSet.diffve2.removedve1.modified);}(** Apply changes on an abstract element *)letapply_change_varschange~add~remove~find(other:'a)(this:'a):'a=leta=VarSet.fold(funvacc->tryaddv(findvother)accwith_->Exceptions.panic"generic merge: error while adding variable %a"pp_varv)change.modifiedthisinVarSet.fold(funvacc->tryremovevaccwith_->Exceptions.panic"generic merge: error while removing variable %a"pp_varv)change.removeda(** Generic merge operator for non-relational domains *)letgeneric_merge~add~find~remove?(custom=(funstmt->None))(a1,e1)(a2,e2)=ife1==e2thena1,a1elseletve1=get_change_vars~custome1inletve2=get_change_vars~custome2inifcompare_change_varsve1ve2=0thena1,a2elseleta2'=apply_change_varsve1a1a2~add~remove~findinleta1'=apply_change_varsve2a2a1~add~remove~findina1',a2'letopt_change_tracker_enabled=reffalseletenable_change_tracker()=opt_change_tracker_enabled:=trueletdisable_change_tracker()=opt_change_tracker_enabled:=falseletis_change_tracker_enabled()=!opt_change_tracker_enabledletset_change_tracker_stateb=opt_change_tracker_enabled:=bletwith_change_trackerf=letold=is_change_tracker_enabled()inenable_change_tracker();letret=f()inset_change_tracker_stateold;ret