123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364(****************************************************************************)(* *)(* 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/>. *)(* *)(****************************************************************************)(** Lattice of partial inversible maps.
Sets of partial maps M ∈ ℘(𝕂 ⇀ 𝕍) from concrete keys set 𝕂 to
concrete values set 𝕍 are abstracted as a set of partial maps ℳ ∈
𝕂 ⇀ (℘(𝕍) ∪ {⊤}).
*)openMopsa_utilsopenBot_topopenTopopenCore.All(** Signature of ordered types with printers *)moduletypeORDER=sigtypetvalcompare:t->t->intvalprint:Print.printer->t->unitendmoduleMake(Key:ORDER)(Value:ORDER)=struct(** Inversible relations between keys and values *)moduleRelation=InvRelation.Make(Key)(Value)(** Set of keys *)moduleKeySet=Relation.DomSet(** Set of values *)moduleValueSet=Relation.CoDomSet(** Inversible maps are represented as a set of relations between keys and
values, in addition to a set of keys mapped to ⊤
*)typemap={relations:Relation.t;top_keys:Relation.DomSet.t}(** Type of inversible maps with ⊤ and ⊥ *)typet=mapwith_bot_top(** ⊥ element *)letbottom:t=BOT(** ⊤ element *)lettop:t=TOP(** Check whether [a] is ⊥ *)letis_bottom(a:t):bool=matchawith|BOT->true|TOP->false|Nbtm->false(** Printing. *)letprintprinter(a:t):unit=matchawith|BOT->pp_stringprinter"⊥"|TOP->pp_stringprinter"⊤"|NbtmwhenRelation.is_emptym.relations&&KeySet.is_emptym.top_keys->pp_stringprinter"∅"|Nbtm->Relation.iter_domain(funkvs->pprintprinter~path:[pkeyKey.printk](pbox(pp_listValue.print~lopen:"{"~lsep:", "~lclose:"}")(ValueSet.elementsvs)))m.relations;KeySet.iter(funk->pp_stringprinter"⊤"~path:[pkeyKey.printk])m.top_keys(** Singleton of empty map *)letempty:t=Nbt{relations=Relation.empty;top_keys=KeySet.empty}(** Remove a set of keys from a relation *)letremove_relation_keys(keys:KeySet.t)(rel:Relation.t):Relation.t=KeySet.foldRelation.remove_imagekeysrel(** Inclusion test. *)letsubset(a1:t)(a2:t):bool=ifa1==a2thentrueelsematcha1,a2with|BOT,_->true|_,BOT->false|_,TOP->true|TOP,_->false|Nbtm1,Nbtm2->(* Remove keys of m1 that valuate to ⊤ in m2 *)letm1'={m1withrelations=remove_relation_keysm2.top_keysm1.relations}inRelation.subsetm1'.relationsm2.relations&&KeySet.subsetm1'.top_keysm2.top_keys(** Join two sets of partial maps. *)letjoin(a1:t)(a2:t):t=ifa1==a2thena1elsematcha1,a2with|BOT,x|x,BOT->x|TOP,_|_,TOP->TOP|Nbtm1,Nbtm2->(* Remove keys that valuate to ⊤ in m1 or m2 *)letm1'={m1withrelations=remove_relation_keysm2.top_keysm1.relations}inletm2'={m2withrelations=remove_relation_keysm1.top_keysm2.relations}inNbt{relations=Relation.unionm1'.relationsm2'.relations;top_keys=KeySet.unionm1.top_keysm2.top_keys;}(** Meet. *)letmeet(a1:t)(a2:t):t=ifa1==a2thena1elsematcha1,a2with|BOT,x|x,BOT->BOT|TOP,x|x,TOP->x|Nbtm1,Nbtm2->(* In addition to bindings that are part of the two relations,
keep the bindings that are in one relation only if the key
belongs to ⊤ keys of the other abstract element.
*)tryNbt(letrelations=Relation.map2zo_domain(funk1vs1->(* Check if k1 is mapped to ⊤ in m2 *)ifKeySet.memk1m2.top_keysthenvs1elseraiseBot.Found_BOT)(funk2vs2->(* Check if k2 is mapped to ⊤ in m1 *)ifKeySet.memk2m1.top_keysthenvs2elseraiseBot.Found_BOT)(funkvs1vs2->letvs=ValueSet.intervs1vs2inifValueSet.is_emptyvsthenraiseBot.Found_BOTelsevs)m1.relationsm2.relationsinlettop_keys=KeySet.interm1.top_keysm2.top_keysin{relations;top_keys})withBot.Found_BOT->bottom(** Widening operator *)letwidenctx(a1:t)(a2:t):t=leta2=joina1a2inifa1==a2thena1elsematcha1,a2with|BOT,x|x,BOT->x|TOP,x|x,TOP->TOP|Nbtm1,Nbtm2->(* Find the keys that belong only to m2, i.e. new relations. These keys will be mapped to ⊤. *)letinstable_keys=Relation.fold2_diff(fun__acc->acc)(funk_acc->KeySet.addkacc)m1.relationsm2.relationsKeySet.emptyin(* Remove instable_keys from m2 relations and add them to top_keys *)Nbt{relations=remove_relation_keysinstable_keysm2.relations;top_keys=KeySet.unionm2.top_keysinstable_keys}(** Find the set of values attached to a key. Raise [Not_found] of the key is not found. *)letfind(k:Key.t)(a:t):ValueSet.twith_top=matchawith|BOT->NtValueSet.empty|TOP->TOP|Nbtm->ifKeySet.memkm.top_keysthenTOPelseNt(Relation.imagekm.relations)(** Find keys attached to value [v] in [a] *)letfind_inverse(v:Value.t)(a:t):KeySet.twith_top=matchawith|BOT->Nt(KeySet.empty)|TOP->TOP|Nbtm->lets1=Relation.inversevm.relationsinlets2=m.top_keysinNt(KeySet.unions1s2)(** Remove all bindings [(k,-)] in [a] *)letremove(k:Key.t)(a:t):t=matchawith|BOT->BOT|TOP->TOP|Nbtm->Nbt{relations=Relation.remove_imagekm.relations;top_keys=KeySet.removekm.top_keys}(** Remove all bindings [(-,v)] in [a] *)letremove_inverse(v:Value.t)(a:t):t=matchawith|BOT->BOT|TOP->TOP|Nbtm->Nbt{mwithrelations=Relation.remove_inversevm.relations}(** [filter f a] keeps all bindings [(k,vs)] in [a] such that [f k vs] is true *)letfilter(f:Key.t->ValueSet.twith_top->bool)(a:t):t=matchawith|BOT->BOT|TOP->TOP|Nbtm->Nbt{relations=Relation.filter_domain(funkvs->fk(Top.Ntvs))m.relations;top_keys=KeySet.filter(funk->fkTop.TOP)m.top_keys}(** [filter_inverse f a] keeps all inverse bindings [(v,ks)] in [a] such that [f v ks] is true *)letfilter_inverse(f:Value.t->KeySet.t->bool)(a:t):t=matchawith|BOT->BOT|TOP->TOP|Nbtm->Nbt{mwithrelations=Relation.filter_codomainfm.relations}(* val filter_inverse : (value -> KeySet.t -> bool) -> t
* (\** [filter_inverse f a] keeps all inverse bindings [(v,ks)] in [a] such that [f v ks] is true *\) *)(** Add bindings [(k,vs)] to [a]. Previous bindings are overwritten. *)letset(k:Key.t)(vs:ValueSet.twith_top)(a:t):t=matchvswith|NtswhenValueSet.is_emptys->BOT|_->matchawith|BOT->BOT|TOP->TOP|Nbtm->matchvswith|TOP->Nbt{top_keys=KeySet.addkm.top_keys;relations=Relation.remove_imagekm.relations}|Ntvs->Nbt{top_keys=KeySet.removekm.top_keys;relations=Relation.set_imagekvsm.relations}(** [add_inverse v ks a] adds the binding [(k,{v} ∪ find k a)] to [a], where [k] ∈ [ks]. *)letadd_inverse(v:Value.t)(ks:KeySet.t)(a:t):t=matchawith|BOT->BOT|TOP->TOP|Nbtm->(* Do not add bindings for keys k ∈ m.top_keys *)letks'=KeySet.diffksm.top_keysinNbt{mwithrelations=Relation.add_inverse_setvks'm.relations}(** Rename key [k] to [k'] *)letrename(k:Key.t)(k':Key.t)(a:t):t=letv=findkainleta=removekainsetk'va(** Rename value [v] to [v'] *)letrename_inverse(v:Value.t)(v':Value.t)(a:t):t=letks=find_inversevainleta=remove_inversevainmatchkswith|TOP->assert(a=TOP);TOP|Ntks->add_inversev'ksa(** Create a map with singleton binding [(k,{v})] *)letsingleton(k:Key.t)(v:Value.t):t=Nbt{top_keys=KeySet.empty;relations=Relation.singletonkv;}(** Check whether a binding [(k,-)] exists in [a] *)letmem(k:Key.t)(a:t):bool=matchawith|BOT->false|TOP->true|Nbtm->Relation.mem_domainkm.relations||KeySet.memkm.top_keys(** Check whether a binding [(-,v)] exists in [a] *)letmem_inverse(v:Value.t)(a:t):bool=matchawith|BOT->false|TOP->true|Nbtm->Relation.mem_codomainvm.relations||not@@Relation.DomSet.is_emptym.top_keys(** [fold f a init] folds function [f] over elements [(k,vs)] *)letfold(f:Key.t->ValueSet.twith_top->'a->'a)(a:t)(init:'a):'a=matchawith|BOT->init|TOP->raiseFound_TOP|Nbtm->KeySet.fold(funkacc->fkTOPacc)m.top_keysinit|>Relation.fold_domain(funkvsacc->fk(Ntvs)acc)m.relations(** Replace bindings [(k,vs)] in [a] with [(k,f vs)] *)letmap(f:ValueSet.twith_top->ValueSet.twith_top)(a:t):t=matchawith|BOT->BOT|TOP->TOP|Nbtm->fold(funkvsacc->setk(fvs)acc)aemptyend