123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230(**************************************************************************)(* *)(* This file is part of the Frama-C's Lannotate plug-in. *)(* *)(* Copyright (C) 2012-2025 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* alternatives) *)(* *)(* 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, version 2.1. *)(* *)(* It 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. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file LICENSE) *)(* *)(**************************************************************************)openCil_typesletfilen=ref""letget_file_name()=!filentypeannotation=int*string*exp*locationtypeannotator={name:string;help:string;apply:(unit->int)->(annotation->unit)->Cil_types.file->unit;}moduletypeANNOTATOR=sigvalname:stringvalhelp:stringvalapply:(Cil_types.exp->Cil_types.explist->Cil_types.location->Cil_types.stmt)->Cil_types.file->unitendmoduletypeANNOTATOR_WITH_EXTRA_TAGS=sigvalname:stringvalhelp:stringvalapply:(extra:stringlist->Cil_types.exp->Cil_types.explist->Cil_types.location->Cil_types.stmt)->Cil_types.file->unitendmoduletypeS=sigvalself:annotatorvalapply:?id:(unit->int)->?collect:(annotation->unit)->Cil_types.file->unitendletannotators=Hashtbl.create10letnextId=ref1letnextBindingId=ref1letnextIdHl=ref1letgetCurrentLabelId()=!nextId-1letgetCurrentBindingId()=!nextBindingId-1letgetCurrentHLId()=!nextIdHl-1letnext()=letid=!nextIdinincrnextId;idletnext_binding()=letid=!nextBindingIdinincrnextBindingId;idletnext_hl()=letid=!nextIdHlinincrnextIdHl;string_of_intidletnocollect_=()let()=Cil_builtins.add_special_builtin"pc_label";Cil_builtins.add_special_builtin"pc_label_bindings";Cil_builtins.add_special_builtin"mutated"letpc_label=refNoneletpc_label_bindings=refNoneletmutated=refNoneletlabel_function_vinfo=refNoneletinit_builtins()=pc_label:=Some(Cil.makeGlobalVar"pc_label"Cil_const.(mk_tfunvoidTypeNonefalse));pc_label_bindings:=Some(Cil.makeGlobalVar"pc_label_bindings"Cil_const.(mk_tfunvoidTypeNonefalse));mutated:=Some(Cil.makeGlobalVar"mutated"Cil_const.(mk_tfunintTypeNonefalse));label_function_vinfo:=!pc_labelletannotate_withannotator?(id=next)?(collect=nocollect)ast=Options.feedback"apply annotations for %s@."annotator.name;annotator.applyidcollectastletannotatefilenamenames?(id=next)?(collect=nocollect)ast=filen:=filename;letfname=tryletann=Hashtbl.findannotatorsnameinannotate_with~id~collectannastwithNot_found->Options.warning"unknown annotators `%s`"nameinList.iterfnames;ifOptions.Visibility.get()thenVisibility.to_visibilityast!pc_labelletprint_helpfmt=letannotators=Hashtbl.fold(fun_kvacc->v::acc)annotators[]inletannotators=List.sort(funab->comparea.nameb.name)annotatorsinletwidth=List.fold_left(funaccann->max(String.lengthann.name)acc)0annotatorsinletfann=Format.fprintffmt"%-*s @[%s@]@."widthann.nameann.helpinList.iterfannotatorsletmk_labelidcollecttagcondmvarsloc=letid=id()inletcond=ifOptions.Simplify.get()thenSimplify.simplify_expcondelsecondinlettag=if(Option.get!label_function_vinfo).vname="pc_label"&&Options.Visibility.get()then"V_"^tagelsetagincollect(id,tag,cond,loc);lettagExp=Ast_const.Exp_builder.mk(Const(CStrtag))inletidExp=Ast_const.Exp_builder.integeridinUtils.mk_call!label_function_vinfo(List.concat[[cond;idExp;tagExp];mvars])letmk_applyapplynameidcollectast=apply(mk_labelidcollectname)ast;Ast.mark_as_changed()letmk_compute_extrasapplynameidcollectast=letmk_label'~extracondloc=lettag=String.concat" "(name::extra)inmk_labelidcollecttagcondlocinapplymk_label'astletregister_annotatorann=Options.debug"register %s annotator"ann.name;Hashtbl.replaceannotatorsann.nameannmoduleRegister(A:ANNOTATOR)=structletself={name=A.name;help=A.help;apply=mk_applyA.applyA.name}letapply=annotate_withselflet()=register_annotatorselfendmoduleRegisterWithExtraTags(A:ANNOTATOR_WITH_EXTRA_TAGS)=structletself={name=A.name;help=A.help;apply=mk_compute_extrasA.applyA.name}letapply=annotate_withselflet()=register_annotatorselfendmoduleCalledFunsState=State_builder.Option_ref(Cil_datatype.Kf.Set)(structletname="Annotators.CalledFuns"letdependencies=[Callgraph.Cg.self;Globals.Functions.self]end)(** Return a set of functions containing to_check and all the functions
syntactically called from them. *)letallCallees(to_check:Cil_datatype.Kf.Set.t)=letrecfoldOnCalleesaccto_check=matchto_checkwith|[]->acc|kf::tl->letacc=Cil_datatype.Kf.Set.addkfaccin(* Adding kf's callees to the functions to check. *)letmore_to_check=reftlinCallgraph.Uses.iter_on_callees(funk->ifnot(Cil_datatype.Kf.Set.memkacc)thenmore_to_check:=k::!more_to_check)kf;foldOnCalleesacc!more_to_checkinfoldOnCalleesCil_datatype.Kf.Set.empty(Cil_datatype.Kf.Set.elementsto_check)letgetCallees()=CalledFunsState.memo(fun()->Options.DoWithCalled.get()|>allCallees)letshouldInstrumentFunfun_varinfo=letdo_names=Cil_datatype.Kf.Set.union(Options.DoFunctionNames.get())(getCallees())inletskip_names=Options.SkipFunctionNames.get()inletf(kf:Cil_datatype.Kf.Set.elt)=(Cil_datatype.Kf.vikf).vname=fun_varinfo.vnameinnot(Cil_datatype.Kf.Set.existsfskip_names)&&(* TODO filter builtin functions *)ifCil_datatype.Kf.Set.is_emptydo_namesthennot(fun_varinfo.vinline)||Options.Inline.get()elseCil_datatype.Kf.Set.existsfdo_namesletshouldInstrumentVarvarinfo=letdo_names=Options.DoVariableNames.get()inletskip_names=Options.SkipVariableNames.get()inletf(vname:Datatype.String.Set.elt)=vname=varinfo.vnameinnot(Datatype.String.Set.existsfskip_names)&&(* TODO filter builtin functions *)ifnot(Datatype.String.Set.is_emptydo_names)thenDatatype.String.Set.existsfdo_nameselsetrue