123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2023 *)(* 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 licenses/LGPLv2.1). *)(* *)(**************************************************************************)openPrinter_tagopenCil_typesopenCil_datatypeletupdate_column=ref(fun_->())letadd_tagbuffer(name,tag_prop)startstop=lettag=Gtk_helper.make_tagbuffer~nametag_propinGtk_helper.apply_tagbuffertagstartstopletstudia_start_tag=("startstudia",[`UNDERLINE`DOUBLE])letshow_direct_tag=("show_direct",[`BACKGROUND"#FFca63"])letshow_indirect_tag=("show_indirect",[`BACKGROUND"#FFdb74"])letempty_tag=("",[])letask_for_lval(main_ui:Design.main_window_extension_points)kf=lettxt=Gtk_helper.input_string~parent:main_ui#main_window~title:"Input lvalue expression"""inmatchtxtwithNone|Some""->None|Sometxt->tryletterm_lval=Logic_parse_string.term_lvalkftxtinSome(txt,term_lval)withe->main_ui#error"[ask for lval] '%s' invalid expression: %s@."txt(Printexc.to_stringe);None(** [kf_stmt_opt] is used if we want to ask the lval to the user in a popup *)letget_lval_optmain_uikflocalizable=matchlocalizablewith|PLval(Some_kf,(Kstmt_stmt),lv)->letlv_txt=Pretty_utils.to_stringPrinter.pp_lvallvinlettlv=Logic_utils.lval_to_term_lvallvinSome(lv_txt,tlv)|PTermLval(Some_kf,(Kstmt_stmt),_,tlv)->lettlv_txt=Pretty_utils.to_stringPrinter.pp_term_lvaltlvinSome(tlv_txt,tlv)|_->matchask_for_lvalmain_uikfwith|None->None|Some(lv_txt,lv)->Some(lv_txt,lv)leteval_tlval=lettyp_lval_to_zone_gui=Datatype.func2Stmt.tyTerm.tyLocations.Zone.tyinDynamic.get~plugin:"Value""tlval_to_zone_gui"typ_lval_to_zone_guimoduleKfs_containing_highlighted_stmt=Kernel_function.Make_Table(Datatype.String.Set)(structletname="Studia.Kf_containing_highlighted_stmt"letsize=7letdependencies=[(*Dependencies are managed manually by Make_StmtSetState*)]end)letdefault_icon_name="gtk-apply"moduleMake_StmtMapState(Info:sigvalname:stringend)=structmoduleD=DatatypeincludeState_builder.Ref(Stmt.Map.Make(Datatype.String.Set))(structletname=Info.nameletdependencies=[Eva.Analysis.self]letdefault()=Stmt.Map.emptyend)letsets=sets;Kfs_containing_highlighted_stmt.clear();Stmt.Map.iter(funstmts->letkf=Kernel_function.find_englobing_kfstmtinletprev=tryKfs_containing_highlighted_stmt.findkfwithNot_found->D.String.Set.emptyinletunion=D.String.Set.unionprevsinKfs_containing_highlighted_stmt.replacekfunion)s;!update_column`Contentsend(*
module type StudiaCmdSig = sig
type input
val help : string
val compute : Kernel_function.t -> Cil_types.stmt -> input -> string
(** Returns a text to be displayed in the GUI *)
val tag_stmt : Cil_types.stmt -> (string * GText.tag_property list)
val clear: unit -> unit
end
*)moduleWritesOrReads=struct(* type input = term_lval *)moduleState=Make_StmtMapState(structletname="Studia.Highlighter.WritesOrRead"end)letclear()=State.clear()lethelp_writes=("[writes] "^"highlight the statements that writes to the location pointed to \
by D at L")lethelp_reads=("[reads] "^"highlight the statements that reads the location pointed to \
by D at L")letindirect_icon_name="gtk-jump-to"letadd_iconmapstmticon=letupdate_iconspre=Some(Datatype.String.Set.(addicon(Option.value~default:emptypre)))inStmt.Map.updatestmtupdate_iconsmapletadd_readmap=function|Reads.Directstmt->add_iconmapstmtdefault_icon_name|Indirectstmt->add_iconmapstmtindirect_icon_nameletadd_writemap=function|Writes.Assignstmt|CallDirectstmt->add_iconmapstmtdefault_icon_name|CallIndirectstmt->add_iconmapstmtindirect_icon_name|GlobalInit_->map|FormalInit(_,callsites)->letcalls=List.flatten(List.mapsndcallsites)inList.fold_left(funmapstmt->add_iconmapstmtdefault_icon_name)mapcallsletcomputeop_kfstmttlv=lett=Logic_const.term(TLvaltlv)(Cil.typeOfTermLvaltlv)inletz=eval_tlvalstmttinletr,s=matchopwith|`Reads->List.fold_leftadd_readStmt.Map.empty(Reads.computez),"Reads"|`Writes->List.fold_leftadd_writeStmt.Map.empty(Writes.computez),"Writes"inState.setr;Options.feedback"%s computed"s;ifStmt.Map.is_emptyrthens^" computed; no statement found."elses^" computed"lettag_stmtstmt=trylets=Stmt.Map.findstmt(State.get())inifDatatype.String.Set.memdefault_icon_namesthenshow_direct_tagelseshow_indirect_tagwithNot_found->empty_tagendlethelp(main_ui:Design.main_window_extension_points)=main_ui#pretty_information"%s@."WritesOrReads.help_writes;main_ui#pretty_information"%s@."WritesOrReads.help_reads;main_ui#pretty_information"%s@.""Reset : reset the internal state for all the previous commands."moduleStudiaState=State_builder.Option_ref(Stmt)(structletname="Studia.Highlighter.StudiaState"letdependencies=[Eva.Analysis.self]end)letreset()=StudiaState.clear();WritesOrReads.clear();Kfs_containing_highlighted_stmt.clear();!update_column`Contentsletcallbackop(main_ui:Design.main_window_extension_points)(kf,stmt,localizable)=letcomputefarg=letmsg=fkfstmtarginifmsg<>""thenmain_ui#pretty_information"%s@."msginbeginmatchget_lval_optmain_uikflocalizablewith|None->reset()|Some(lval_txt,lval)->beginlettxt=Format.asprintf"[studia] query %s"lval_txtinStudiaState.setstmt;main_ui#pretty_information"%s@."txt;compute(WritesOrReads.computeop)lvalend;end;main_ui#rehighlight()lethighlighter(buffer:Design.reactive_buffer)localizable~start~stop=tryletstart_s=StudiaState.get()inletput_tagtag=matchtagwith|("",[])->()|_->add_tagbuffer#buffertagstartstopinmatchlocalizablewith|PStmt(_,stmt)->ifstart_s.sid=stmt.sidthenput_tagstudia_start_tag;put_tag(WritesOrReads.tag_stmtstmt);|_->()withNot_found->()letcheck_value(main_ui:Design.main_window_extension_points)=Eva.Analysis.is_computed()||letanswer=GToolbox.question_box~title:("Eva Needed")~buttons:["Run";"Cancel"]("Eva has to be run first.\nThis can take some time.\n"^"Do you want to run Eva now ?")inanswer=1&&matchmain_ui#full_protect~cancelable:trueEva.Analysis.computewith|Some_->true|None->false(** To add a sensitive/unsensitive menu item to a [factory].
The menu item is insensitive when [arg_opt = None],
else, when the item is selected, the callback is called with the argument.
If [uses_value] is true, check if the value analysis has been computed.
*)letadd_item(main_ui:Design.main_window_extension_points)~uses_valuemenunamearg_optcallback=(* add the menu item *)letitem=GMenu.menu_item~label:name()inmenu#additem;matcharg_optwith|None->(* item must not be sensitive *)item#misc#set_sensitivefalse|Somearg->(* add callback to the menu item *)letcallback()=ifnotuses_value||check_valuemain_uithencallbackargelse()in(* The following code circumvents a bug where submenu items are not properly
activated, by also binding the callback to the button_press event.
See http://stackoverflow.com/questions/5221326/submenu-item-does-not-call-function-with-working-solution/15309826
For mysterious reasons, this bug only happens in the contextual menu of
the Information panel, but not in the source panel. So we need to avoid
creating two input windows for the source panel menu. *)letonly_once=reftrueinletcallback()=if!only_oncethenbeginonly_once:=false;callback();only_once:=trueendin(* Needed anyway for keyboard selection. *)ignore(item#connect#activate~callback);(* Needed for the menu in the Information panel. *)ignore(item#event#connect#button_press~callback:(funevt->ifGdkEvent.Button.buttonevt=1then(callback();true)elsefalse))letselector(popup_factory:GMenu.menuGMenu.factory)(main_ui:Design.main_window_extension_points)~buttonlocalizable=ifbutton=3thenbeginletsubmenu=popup_factory#add_submenu"Studia"inletsubmenu_factory=newGMenu.factorysubmenuinletarg=match(Pretty_source.kf_of_localizablelocalizable,Pretty_source.ki_of_localizablelocalizable)with|Somekf,Kstmtstmt->Some(kf,stmt,localizable)|Some_,Kglobal|None,_->Noneinletadd_menu_itemnamecallback=add_itemmain_ui~uses_value:truesubmenunamearg(funarg->main_ui#protect~cancelable:true(fun()->callbackmain_uiarg))inadd_menu_item"Writes"(callback`Writes);add_menu_item"Reads"(callback`Reads);ignore(submenu_factory#add_separator());add_itemmain_ui~uses_value:falsesubmenu"Reset All"(Some())(fun_->reset();main_ui#rehighlight());ignore(submenu_factory#add_separator());add_itemmain_ui~uses_value:falsesubmenu"Help"(Some())(fun_->helpmain_ui);endletfiletree_decoratemain_ui=main_ui#file_tree#append_pixbuf_column~title:"Studia"(funglobs->leticons=function|GFun({svar=v},_)->(tryKfs_containing_highlighted_stmt.find(Globals.Functions.getv)withNot_found->Datatype.String.Set.empty)|_->Datatype.String.Set.emptyinletids=ifKfs_containing_highlighted_stmt.length()<>0thenleticons=List.fold_left(funaccglob->Datatype.String.Set.union(iconsglob)acc)Datatype.String.Set.emptyglobsinifDatatype.String.Set.is_emptyiconsthenDatatype.String.Set.singleton""elseiconselseDatatype.String.Set.singleton""inleticons=ifDatatype.String.Set.memdefault_icon_nameidsthen[default_icon_name]elseDatatype.String.Set.elements(Datatype.String.Set.removedefault_icon_nameids)inList.map(funicon->`STOCK_IDicon)icons)(fun_->Kfs_containing_highlighted_stmt.length()<>0)letmainmain_ui=main_ui#register_source_selectorselector;main_ui#register_source_highlighterhighlighter;update_column:=(filetree_decoratemain_ui)let()=Design.register_extensionmain