Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file common.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392(****************************************************************************)(* *)(* 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/>. *)(* *)(****************************************************************************)(** Common transfer functions for handling function calls *)openMopsaopenAstletname="universal.iterators.interproc.common"letdebugfmt=Debug.debug~channel:namefmt(** Option to limit recursion depth *)letopt_recursion_limit=ref2let()=register_domain_optionname{key="-recursion-limit";doc="Limit of recursive calls";category="Interprocedural Analysis";spec=ArgExt.Set_intopt_recursion_limit;default=string_of_int!opt_recursion_limit;}letopt_split_return_variables_by_range:boolref=reffalselet()=register_domain_optionname{key="-split-returns";category="Interprocedural Analysis";doc="";spec=ArgExt.Setopt_split_return_variables_by_range;default=" split return variables by their location in the program"}letopt_rename_local_variables_on_recursive_call:boolref=reftruelet()=register_shared_option(name^".renaming"){key="-disable-var-renaming-recursive-call";category="Interprocedural Analysis";doc=" disable renaming of local variables when detecting recursive calls";spec=ArgExt.Clearopt_rename_local_variables_on_recursive_call;default=""}(** {2 Return flow token} *)(** ===================== *)typetoken+=|T_returnofrange(** [T_return(l)] represents flows reaching a return statement at
location [l] *)let()=register_token{compare=(funnexttk1tk2->matchtk1,tk2with|T_return(r1),T_return(r2)->compare_ranger1r2|_->nexttk1tk2);print=(funnextfmt->function|T_return(r)->Format.fprintffmt"return[%a]@"pp_ranger|tk->nextfmttk);}(** {2 Return variable} *)(** =================== *)(** Return variable of a function call *)typevar_kind+=V_returnofexpr(* call expression *)*rangeoption(* return range *)(** Registration of the kind of return variables *)let()=register_var{print=(funnextfmtv->matchv.vkindwith|V_return(e,None)->Format.fprintffmt"ret(%a)"pp_expre|V_return(e,Somer)->Format.fprintffmt"ret(%a)@%a)"pp_exprepp_ranger|_->nextfmtv);compare=(funnextv1v2->matchv1.vkind,v2.vkindwith|V_return(e1,ro1),V_return(e2,ro2)->Compare.compose[(fun()->compare_expre1e2);(fun()->compare_rangee1.erangee2.erange);(fun()->(OptionExt.comparecompare_range)ro1ro2);]|_->nextv1v2);}(** Constructor of return variables *)letmk_returncallro=letuniq_name,ro=matchrowith|Somerwhen!opt_split_return_variables_by_range->Format.asprintf"ret(%a)@@%a@@%a"pp_exprcallpp_rangecall.erangepp_ranger,ro|_->Format.asprintf"ret(%a)@@%a"pp_exprcallpp_rangecall.erange,Noneinmkvuniq_name(V_return(call,ro))call.etyp(** {2 Contexts to keep return variable} *)(** =================================== *)moduleReturnKey=GenContextKey(structtype'at=exprletprintppfmtexpr=Format.fprintffmt"Returning call: %a"pp_exprexprend)letreturn_key=ReturnKey.keyletget_last_call_siteflow=letcs=Flow.get_callstackflowinlethd,_=pop_callstackcsinhd.call_range(** {2 Ignore recursion assumption} *)(** =============================== *)typeassumption_kind+=A_ignore_recursion_side_effectofstringlet()=register_assumption{print=(funnextfmt->function|A_ignore_recursion_side_effectf->Format.fprintffmt"ignoring side effects of recursive call to '%a'"(Debug.boldFormat.pp_print_string)f|a->nextfmta);compare=(funnexta1a2->matcha1,a2with|A_ignore_recursion_side_effectf1,A_ignore_recursion_side_effectf2->comparef1f2|_->nexta1a2);}(** {2 Recursion checks} *)(** ==================== *)(** Check that no recursion is happening *)letcheck_recursionf_origf_uniqrangecs=letsite={call_fun_orig_name=f_orig;call_fun_uniq_name=f_uniq;call_range=range}inletreciteri=function|[]->false|site'::tl->ifcompare_callsitesitesite'=0thenifi<!opt_recursion_limittheniter(i+1)tlelsetrueelseiteritliniter0csletcheck_nested_callsfcs=ifcs=[]thenfalseelseList.exists(funcall->call.call_fun_uniq_name=f)(List.tlcs)(** {2 Function inlining} *)(** ===================== *)(** Initialize function parameters *)letinit_fun_paramsfargsrangemanflow=(* Update the call stack *)letflow=Flow.push_callstackf.fun_orig_name~uniq:f.fun_uniq_namerangeflowinletinit_range=tag_rangef.fun_range"init"iniff.fun_parameters=[]then[],f.fun_locvars,f.fun_body,Post.returnflowelseif!opt_rename_local_variables_on_recursive_call&&check_nested_callsf.fun_uniq_name(Flow.get_callstackflow)thenbegindebug"nested calls detected on %s, performing parameters and locvar renaming"f.fun_orig_name;(* Add parameters and local variables to the environment *)letadd_range=(funp->mk_attr_varp(Format.asprintf"%a"pp_rangerange)p.vtyp)inletfunction_vars=f.fun_parameters@f.fun_locvarsinletfun_parameters=List.mapadd_rangef.fun_parametersinletfun_locvars=List.mapadd_rangef.fun_locvarsin(* TODO: do this transformation only if we detect f in the callstack? That could work? *)letnew_body=Visitor.map_stmt(fune->matchekindewith|E_var(v,m)whenList.exists(funv'->compare_varvv'=0)function_vars->Keep{ewithekind=E_var(add_rangev,m)}|_->VisitPartse)(funs->VisitPartss)f.fun_bodyindebug"moved body from:%a@\nto %a@\n"pp_stmtf.fun_bodypp_stmtnew_body;(* Assign arguments to parameters *)(* FIXME: the sub-expressions of arg have a range in the caller
body. Since we have updated the callstack, we should be now
in the callee body. We need a way to rewrite the ranges in
arg! *)letparameters_assign=List.rev@@List.fold_left(funacc(param,arg)->mk_assign(mk_varparaminit_range)arginit_range::mk_add_varparaminit_range::acc)[](List.combinefun_parametersargs)inletinit_block=mk_blockparameters_assigninit_rangein(* Execute body *)fun_parameters,fun_locvars,new_body,man.execinit_blockflowendelsebegin(* Assign arguments to parameters *)(* FIXME: the sub-expressions of arg have a range in the caller
body. Since we have updated the callstack, we should be now
in the callee body. We need a way to rewrite the ranges in
arg! *)letparameters_assign=List.rev@@List.fold_left(funacc(param,arg)->mk_assign(mk_varparaminit_range)arginit_range::mk_add_varparaminit_range::acc)[](List.combinef.fun_parametersargs)inletinit_block=mk_blockparameters_assigninit_rangein(* Execute body *)f.fun_parameters,f.fun_locvars,f.fun_body,man.execinit_blockflowend(** Execute function body and save the return value *)letexec_fun_bodyfparamslocalsbodycall_oexprangemanflow=(* Save the return variable in the context and backup the old one *)letoldreturn,flow1=matchcall_oexpwith|None->None,flow|Somecall->(trySome(find_ctxreturn_key(Flow.get_ctxflow))withNot_found->None),Flow.set_ctx(add_ctxreturn_keycall(Flow.get_ctxflow))flowin(* Clear all return flows *)letflow2=Flow.filter(funtkenv->matchtkwith|T_return_->false|_->true)flow1in(* Execute the body of the function *)letpost2=man.execbodyflow2in(* Restore return and callstack contexts *)letpost3=matcholdreturnwith|None->post2|Someret->Cases.set_ctx(add_ctxreturn_keyret(Cases.get_ctxpost2))post2in(* Restore call stack *)let_,cs=Cases.get_callstackpost3|>Callstack.pop_callstackinletpost4=Cases.set_callstackcspost3inpost4>>%funflow3->(* Copy the new context and report from flow3 to original flow flow1 *)letflow4=Flow.copy_ctxflow3flow1|>Flow.copy_reportflow3in(* Cut the T_cur flow *)letflow4=Flow.removeT_curflow4in(* Retrieve non-cur/return flows in flow3 and put them in flow4 *)letflow5=Flow.fold(funacctkenv->matchtkwith|T_cur|T_return_->acc|_->Flow.addtkenvman.latticeacc)flow4flow3inletmk_returntk_orangerange=matchcall_oexpwith|None->mk_unitrange|Somecall->mk_var(mk_returncalltk_orange)rangein(* Create a separate post-state for each return flow in flow3 *)letremove_locals=(* Remove local variables from the environment. Remove of parameters is
postponed after finishing the statement, to keep relations between
the passed arguments and the return value. *)man.exec(mk_block(List.map(funv->mk_remove_varvrange)locals)range)inletadd_cleanersreturn=Cases.add_cleaners(List.map(funv->mk_remove_varvrange)params@(matchcall_oexpwith|None->[]|Some_->[mk_removereturnrange]))inletevals=Flow.fold(funacctkenv->matchtkwith|T_cur|T_return_->letflow=Flow.setT_curenvman.latticeflow5inletreturn=matchtkwith|T_cur->mk_returnNonerange|T_returntk_range->mk_return(Sometk_range)range|_->assertfalsein(remove_localsflow>>%man.evalreturn|>add_cleanersreturn)::acc|_->acc)[]flow3inEval.join_list~empty:(fun()->letreturn=mk_returnNonerangeinPost.returnflow5>>%remove_locals>>%man.evalreturn|>add_cleanersreturn)evals(** Inline a function call *)letinlinefparamslocalsbodycall_oexprangemanflow=ifcheck_recursionf.fun_orig_namef.fun_uniq_namerange(Flow.get_callstackflow)thenletflow=Flow.add_local_assumption(A_ignore_recursion_side_effectf.fun_orig_name)rangeflowinletpost=matchcall_oexpwith|None->Post.returnflow|Somee->letv=mk_returneNoneinman.exec(mk_add_varvrange)flow>>%man.exec(mk_assign(mk_varvrange)(mk_topv.vtyprange)range)inpost>>%funflow->matchcall_oexpwith|None->Eval.singleton(mk_unitrange)flow~cleaners:(List.map(funv->mk_remove_varvrange)params)|Somee->letv=mk_returneNoneinman.eval(mk_varvrange)flow|>Cases.add_cleaners(mk_remove_varvrange::List.map(funv->mk_remove_varvrange)params)elseexec_fun_bodyfparamslocalsbodycall_oexprangemanflow