123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822(****************************************************************************)(* *)(* 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/>. *)(* *)(****************************************************************************)(** Inter-procedural iterator of stubs by inlining. *)openMopsaopenSig.Abstraction.StatelessopenUniversal.AstopenAstopenAlarms(******************)(** Trace markers *)(******************)typemarker+=M_stub_caseofstub_func*caselet()=register_marker{marker_print=(funnextfmt->function|M_stub_case(stub,case)->Format.fprintffmt"stub-case (%s.%s)"stub.stub_func_namecase.case_label|m->nextfmtm);marker_compare=(funnextm1m2->matchm1,m2with|M_stub_case(stub1,case1),M_stub_case(stub2,case2)->Compare.pairString.compareString.compare(stub1.stub_func_name,case1.case_label)(stub2.stub_func_name,case2.case_label)|_->nextm1m2);marker_print_name=(funnext->function|M_stub_case_->"stub-case"|m->nextm);marker_name="stub-case"}(********************)(** Abstract domain *)(********************)moduleDomain=structincludeGenStatelessDomainId(structletname="stubs.iterators.body"end)letchecks=[CHK_STUB_CONDITION](** Initialization of environments *)(** ============================== *)letinitprogmanflow=None(** {2 Command-line options} *)(** ************************ *)letopt_stub_ignored_cases:stringlistref=ref[](** List of ignored stub cases *)let()=register_builtin_option{key="-stub-ignore-case";doc=" list of stub cases to ignore";category="Stubs";spec=String(ArgExt.set_string_list_lifteropt_stub_ignored_cases,ArgExt.empty);default="";}(** Check whether a case is ignored *)letis_case_ignoredstubcase:bool=matchstubwith|None->false|Somes->List.mem(s.stub_func_name^"."^case.case_label)!opt_stub_ignored_cases(** Evaluation of expressions *)(** ========================= *)(** Negate a formula *)letrecnegate_formula(f:formulawith_range):formulawith_range=matchf.contentwith|F_expre->with_range(F_expr(mk_notee.erange))f.range|F_binop(IMPLIES,f1,f2)->with_range(F_binop(AND,f1,negate_formulaf2))f.range|F_binop(op,f1,f2)->with_range(F_binop(negate_log_binopop,negate_formulaf1,negate_formulaf2))f.range|F_notf->f|F_forall(var,set,ff)->with_range(F_exists(var,set,negate_formulaff))f.range|F_exists(var,set,ff)->with_range(F_forall(var,set,negate_formulaff))f.range|F_in(e,S_interval(l,u))->with_range(F_expr(mk_binop(mk_binopeO_ltl~etyp:T_boolf.range)O_log_or(mk_binopeO_gtu~etyp:T_boolf.range)~etyp:T_boolf.range))f.range|F_in(e,S_resourceres)->with_range(F_expr(mk_not(mk_stub_resource_memeresf.range)f.range))f.range|F_otherwise_->panic_atf.range"negation of 'otherwise' formulas not possible"|F_if(c,f1,f2)->with_range(F_if(c,negate_formulaf1,negate_formulaf2))f.range(** Translate a formula into prenex normal form *)letrecformula_to_prenexf=matchf.contentwith|F_exprcond->[],cond|F_binop(AND,f1,f2)->letquants1,cond1=formula_to_prenexf1inletquants2,cond2=formula_to_prenexf2inquants1@quants2,mk_log_andcond1cond2f.range|F_binop(OR,f1,f2)->letquants1,cond1=formula_to_prenexf1inletquants2,cond2=formula_to_prenexf2inquants1@quants2,mk_log_orcond1cond2f.range|F_binop(IMPLIES,f1,f2)->letquants1,cond1=formula_to_prenex(negate_formulaf1)inletquants2,cond2=formula_to_prenexf2inquants1@quants2,mk_log_orcond1cond2f.range|F_notff->formula_to_prenex(negate_formulaff)|F_in(e,S_interval(l,u))->[],mk_ineluf.range|F_in(e,S_resourceres)->[],mk_stub_resource_memeresf.range|F_forall(v,s,ff)->letquants,cond=formula_to_prenexffin(FORALL,v,s)::quants,cond|F_exists(v,s,ff)->letquants,cond=formula_to_prenexffin(EXISTS,v,s)::quants,cond|F_otherwise(ff,e)->letquants,cond=formula_to_prenexffinquants,mk_stub_otherwisecond(Somee)f.range|F_if(c,f1,f2)->letquants,cond=formula_to_prenexcinletquants1,cond1=formula_to_prenexf1inletquants2,cond2=formula_to_prenexf2inquants@quants1@quants2,mk_stub_ifcondcond1cond2f.range(* Function to get variables in a condition (and avoid the alarm expression in `otherwise` *)letrecvars_of_conditioncond=fold_expr(funacce->matchekindewith|E_var(v,_)->Keep(VarSet.addvacc)|E_stub_otherwise(ee,_)->Keep(VarSet.union(vars_of_conditionee)acc)|_->VisitPartsacc)(funaccs->assertfalse)VarSet.emptycond(* Check if a variable is used in an expression *)letvar_in_exprve=exists_expr(funee->matchekindeewith|E_var(vv,_)->compare_varvvv=0|_->false)(funs->false)e(* Function to remove unnecessary quantifiers not used in an expression *)letremove_unnecessary_quantifiersquantse=letvars=vars_of_conditionein(* A quantified var is necessary if (i) it used in the condition, or (ii)
used in the bounds of an other necessary quantifier *)letquants=List.map(fun((_,v,_)asq)->(q,VarSet.memvvars))quantsinletreciter=function|[]->[]|(_,true)ashd::tl->(* Already used quantifier => keep it *)hd::itertl|((_,v,_)asq,false)ashd::tl->(* Check if this unused quantifier is present in the bounds of an
already used quantifier *)ifList.exists(function|((_,_,S_interval(lo,hi)),true)->var_in_exprvlo||var_in_exprvhi|_->false)tlthen(q,true)::itertlelsehd::itertlin(* Iterate [iter] until no new used quantifier is found *)letrecfpquants=letquants'=iterquantsinifList.exists(fun((_,b1),(_,b2))->b1!=b2)(List.combinequantsquants')thenfpquants'elsequantsinfpquants|>(* Keep only used quantifiers *)List.filter(fun(_,b)->b)|>List.mapfst(** Translate a prenex encoding (i.e. quantifiers and a condition) into an expression *)letrecprenex_to_exprquantscondrange=ifquants=[]thencondelsematchekindcondwith|E_binop(O_log_and,e1,e2)->letquants1=remove_unnecessary_quantifiersquantse1inletquants2=remove_unnecessary_quantifiersquantse2inlete1'=prenex_to_exprquants1e1e1.erangeinlete2'=prenex_to_exprquants2e2e2.erangeinmk_log_ande1'e2'range|E_binop(O_log_or,e1,e2)->letquants1=remove_unnecessary_quantifiersquantse1inletquants2=remove_unnecessary_quantifiersquantse2inlete1'=prenex_to_exprquants1e1e1.erangeinlete2'=prenex_to_exprquants2e2e2.erangeinmk_log_ore1'e2'range|E_stub_if(c,e1,e2)->letquants'=remove_unnecessary_quantifiersquantscinifquants'=[]thenlete1'=prenex_to_exprquantse1e1.erangeinlete2'=prenex_to_exprquantse2e2.erangein{condwithekind=E_stub_if(c,e1',e2')}elsemk_stub_quantified_formulaquantscondrange|E_stub_otherwise(e,a)->mk_stub_otherwise(prenex_to_exprquantsee.erange)arange|_->letquants'=remove_unnecessary_quantifiersquantscondinifquants'=[]thencondelsemk_stub_quantified_formulaquants'condrange(** Evaluate a formula *)leteval_formula(cond_to_stmt:expr->range->stmt)(f:formulawith_range)manflow=(* Write formula in prenex normal form *)letquants,cond=formula_to_prenexfin(* Translate the prenex encoding into an expression *)letcond'=prenex_to_exprquantscondf.rangein(* Constrain the environment with the obtained condition *)man.exec(cond_to_stmtcond'f.range)flow(** Initialize the parameters of the stubbed function *)letrecinit_paramsargsparamsrangemanflow=matchparams,argswith|[],_->Post.returnflow|param::tl_params,arg::tl_args->man.exec(mk_add_varparamrange)flow>>%man.exec(mk_assign(mk_varparamrange)argrange)>>%init_paramstl_argstl_paramsrangeman|_,[]->panic"stubs: insufficent number of arguments"(** Remove parameters from the returned flow *)letremove_paramsparamsrangemanflow=man.exec(mk_block(List.map(funparam->mk_remove_varparamrange)params)range)flow(** Evaluate the formula of the `assumes` section *)letexec_assumesassumesmanflow=eval_formulamk_assumeassumes.contentmanflow(** Evaluate the formula of the `requires` section *)letexec_requiresreqmanflow=eval_formulamk_stub_requiresreq.contentmanflow(** Execute an allocation of a new resource *)letexec_local_newvresrangemanflow:'apost=(* Evaluation the allocation request *)man.eval(mk_stub_alloc_resourceresrange)flow>>$funaddrflow->(* Assign the address to the variable *)man.exec(mk_assign(mk_varvrange)addrrange)flow(** Execute a function call *)(* FIXME: check the purity of f *)letexec_local_callvfargsrangemanflow=man.exec(mk_assign(mk_varvrange)(mk_expr(E_call(f,args))~etyp:v.vtyprange)range)flow(** Execute the `local` section *)letexec_locallmanflow=matchl.content.lvalwith|L_newres->exec_local_newl.content.lvarresl.rangemanflow|L_call(f,args)->exec_local_calll.content.lvarfargsl.rangemanflowletexec_ensuresereturnmanflow=(* Replace E_stub_return expression with the fresh return variable *)letf=matchreturnwith|None->e.content|Somev->visit_expr_in_formula(fune->matchekindewith|E_stub_return->Keep{ewithekind=E_var(v,None)}|_->VisitPartse)e.contentin(* Evaluate ensure body and return flows that verify it *)eval_formulamk_assumefmanflowletexec_assignsassignsmanflow=letstmt=mk_stub_assignsassigns.content.assign_targetassigns.content.assign_offsetassigns.rangeinmatchassigns.content.assign_offsetwith|[]->man.execstmtflow|(l,u)::tl->(* Check that offsets intervals are not empty *)letrange=tag_rangeassigns.range"condition"inletcond=List.fold_left(funacc(l,u)->log_andacc(lelurange)assigns.range)(lelurange)tlinassumecond~fthen:(funflow->man.execstmtflow)~felse:(funflow->Post.returnflow)manflow(** Remove locals *)letclean_postlocalsrangemanflow=letblock=List.fold_left(funblockl->mk_remove_varl.content.lvarrange::block)[]localsinman.exec(mk_blockblockrange)flowletexec_freefreemanflow=lete=free.contentinletstmt=mk_stub_freeefree.rangeinman.execstmtflowletexec_messagemsgmanflow=ifFlow.getT_curman.latticeflow|>man.lattice.is_bottomthenPost.returnflowelsematchmsg.content.message_kindwith|WARN->Exceptions.warn_atmsg.range"%s"msg.content.message_body;Post.returnflow|UNSOUND->Post.return@@Flow.add_local_assumption(Soundness.A_stub_soundness_messagemsg.content.message_body)msg.rangeflow(** Execute a leaf section *)letexec_leafleafreturnmanflow:'apost=matchleafwith|S_locallocal->exec_locallocalmanflow|S_assumesassumes->exec_assumesassumesmanflow|S_requiresrequires->exec_requiresrequiresmanflow|S_assignsassigns->exec_assignsassignsmanflow|S_ensuresensures->exec_ensuresensuresreturnmanflow|S_freefree->exec_freefreemanflow|S_messagemsg->exec_messagemsgmanflow(** Execute the body of a case section *)letexec_case?(stub=None)casereturnmanflow:'apost=letpost=matchstubwith|None->Post.returnflow|Somestub->man.exec(mk_add_marker(M_stub_case(stub,case))case.case_range)flowinletpost=List.fold_left(funaccleaf->acc>>%funflow->exec_leafleafreturnmanflow)postcase.case_bodyinpost>>%(* Clean case post state *)clean_postcase.case_localscase.case_rangeman(** Execute the body of a stub *)letexec_body?(stub=None)bodyreturnrangeman(flow:'aflow)=(* Execute leaf sections *)letpost=List.fold_left(funpostsection->matchsectionwith|S_leafleaf->post>>%funflow->exec_leafleafreturnmanflow|_->post)(Post.returnflow)bodyinpost>>%funflow->(* Execute case sections separately *)letflows,ctx=List.fold_left(fun(acc,ctx)section->matchsectionwith|S_casecasewhennot(is_case_ignoredstubcase)->letflow=Flow.set_ctxctxflowinletflow'=exec_case~stubcasereturnmanflowinflow'::acc,Cases.get_ctxflow'|_->acc,ctx)([],Flow.get_ctxflow)bodyinletflows=List.map(Cases.set_ctxctx)flowsin(* Join flows *)(* FIXME: when the cases do not define a partitioning, we need
to do something else *)Cases.join_listflows~empty:(fun()->Post.returnflow)letprepare_all_assignsassignsrangemanflow=(* Check if there are assigned variables *)ifassigns=[]thenPost.returnflowelseman.exec(mk_stub_prepare_all_assignsassignsrange)flowletclean_all_assignsassignsrangemanflow=(* Check if there are assigned variables *)ifassigns=[]thenPost.returnflowelseman.exec(mk_stub_clean_all_assignsassignsrange)flow(** Evaluate a call to a stub *)leteval_stub_callstubargsreturnrangemanflow=(* Update the callstack *)letcs=Flow.get_callstackflowinletflow=Flow.push_callstackstub.stub_func_namerangeflowin(* Initialize parameters *)init_paramsargsstub.stub_func_paramsrangemanflow>>%funflow->(* Prepare assignments *)prepare_all_assignsstub.stub_func_assigns(tag_rangestub.stub_func_range"prepare")manflow>>%funflow->(* Create the return variable *)(matchreturnwith|None->Post.returnflow|Somev->man.exec(mk_add_varvrange)flow)>>%funflow->(* Evaluate the body of the stb *)exec_body~stub:(Somestub)stub.stub_func_bodyreturnrangemanflow>>%funflow->(* Clean locals *)clean_poststub.stub_func_locals(tag_rangestub.stub_func_range"clean")manflow>>%funflow->(* Clean assignments *)clean_all_assignsstub.stub_func_assigns(tag_rangestub.stub_func_range"clean")manflow>>%funflow->(* Restore the callstack *)letflow=Flow.set_callstackcsflowinletclean_range=tag_rangerange"clean"inletcleaners=List.map(funparam->mk_remove_varparamclean_range)stub.stub_func_paramsinmatchreturnwith|None->Eval.singleton(mk_unitrange)flow~cleaners|Somev->man.eval(mk_varvrange)flow|>Cases.add_cleaners(mk_remove_varvrange::cleaners)(** Evaluate an otherwise expression *)leteval_otherwisecondalarmrangemanflow=assumecondmanflow~fthen:(funflow->safe_stub_conditioncond.erangemanflow|>Eval.singleton(mk_truerange))~felse:(funflow->matchalarmwith|Somee->man.evaleflow|None->raise_stub_invalid_requirement~bottom:falsecondrangemanflow|>Eval.singleton(mk_falserange))(* Remove flows where a quantification interval is empty *)letdiscard_empty_quantification_intervalsquantscondrangemanflow=letremove_quant_varsvarsevl=ifvars=[]thenevlelseevl>>$funeflow->List.fold_left(funaccv->acc>>%man.exec(mk_remove_varvrange))(Post.returnflow)vars>>%funflow->Eval.singletoneflowinletreciteraddedlflow=matchlwith|[]->man.eval~route:(Belowname)(mk_stub_quantified_formulaquantscondrange)flow|>remove_quant_varsadded|(_,_,S_resource_)::tl->iteraddedtlflow|(FORALL,v,S_interval(lo,hi))::tl->assume(mk_lelohirange)manflow~fthen:(funflow->man.exec(mk_add_varvrange)flow>>%man.exec(mk_assume(mk_in(mk_varvrange)lohirange)range)>>%iter(v::added)tl)~felse:(funflow->Eval.singleton(mk_truerange)flow|>remove_quant_varsadded)|(EXISTS,v,S_interval(lo,hi))::tl->assume(mk_lelohirange)manflow~fthen:(funflow->man.exec(mk_add_varvrange)flow>>%man.exec(mk_assume(mk_in(mk_varvrange)lohirange)range)>>%iter(v::added)tl)~felse:(funflow->Eval.singleton(mk_falserange)flow|>remove_quant_varsadded)initer[]quantsflow(** Check if a condition contains an otherwise expression *)letrecotherwise_in_conditioncond=matchekindcondwith|E_stub_otherwise_->true|E_binop((O_log_and|O_log_or),cond1,cond2)->otherwise_in_conditioncond1||otherwise_in_conditioncond2|E_stub_quantified_formula(_,qcond)->otherwise_in_conditionqcond|E_stub_if(_,fthen,felse)->otherwise_in_conditionfthen||otherwise_in_conditionfelse|_->false(** Remove newly introduced checks *)letremove_new_checksoldflow=letreport=fold2zo_report(fundiag1acc->acc)(fundiag2acc->remove_diagnosticdiag2acc)(fundiag1diag2acc->acc)(Flow.get_reportold)(Flow.get_reportflow)(Flow.get_reportflow)inFlow.set_reportreportflow(** Move newly introduced checks to a new range *)letmove_new_checksrangeoldflow=letreport=fold2zo_report(fundiag1acc->acc)(fundiag2acc->remove_diagnosticdiag2acc|>add_diagnostic{diag2withdiag_range=range})(fundiag1diag2acc->acc)(Flow.get_reportold)(Flow.get_reportflow)(Flow.get_reportflow)inFlow.set_reportreportflow(** Entry point of expression evaluations *)letevalexpmanflow=matchekindexpwith|E_stub_call(stub,args)->ifman.lattice.is_bottom(Flow.getT_curman.latticeflow)thenCases.emptyflow|>OptionExt.returnelse(* Create the return variable *)letreturn=matchstub.stub_func_return_typewith|None->None|Somet->Some(Universal.Iterators.Interproc.Common.mk_returnexp)ineval_stub_callstubargsreturnexp.erangemanflow|>OptionExt.return|E_stub_otherwise(cond,alarm)->eval_otherwisecondalarmexp.erangemanflow|>OptionExt.return|E_stub_raisemsg->raise_stub_alarm~bottom:falsemsgexp.erangemanflow|>Eval.singleton(mk_falseexp.erange)|>OptionExt.return|E_binop(O_log_and,e1,e2)whenotherwise_in_conditione1&&otherwise_in_conditione2->(* To evaluate a requirement e1 ∧ e2, we evaluate e1 and e2 and then we
lift checks on e1 and e2 to e1 ∧ e2:
- If both e1 and e2 are valid, then we mark e1 ∧ e2 as safe.
We also remove checks on e1 and e2, since they are redundant with
the check on e1 ∧ e2.
- If e1 or e2 is invalid, we move the alarms to the range of e1 ∧ e2, but we keep the same alarm message.
*)letflow0=flowinassumee1manflow~fthen:(funflow->assumee2manflow~fthen:(funflow->remove_new_checksflow0flow|>safe_stub_conditionexp.erangeman|>Eval.singleton(mk_trueexp.erange))~felse:(funflow->move_new_checksexp.erangeflow0flow|>Eval.singleton(mk_falseexp.erange)))~felse:(funflow->move_new_checksexp.erangeflow0flow|>Eval.singleton(mk_falseexp.erange))|>OptionExt.return|E_binop(O_log_or,e1,e2)whenotherwise_in_conditione1&&otherwise_in_conditione2->(* To evaluate a requirement e1 ∨ e2, we evaluate e1 and e2 and then we
lift checks on e1 and e2 to e1 ∨ e2:
- If e1 or e2 is valid, then we mark e1 ∨ e2 as safe.
We also remove checks on e1 and e2, since they are redundant with
the check on e1 ∨ e2.
- If e1 and e2 are invalid, we move the alarms to the range of e1 ∨ e2, but we keep the same alarm message.
*)letflow0=flowinassumee1manflow~fthen:(funflow->remove_new_checksflow0flow|>safe_stub_conditionexp.erangeman|>Eval.singleton(mk_trueexp.erange))~felse:(funflow->assumee2manflow~fthen:(funflow->remove_new_checksflow0flow|>safe_stub_conditionexp.erangeman|>Eval.singleton(mk_trueexp.erange))~felse:(funflow->move_new_checksexp.erangeflow0flow|>Eval.singleton(mk_falseexp.erange)))|>OptionExt.return|E_stub_quantified_formula(quants,cond)whenList.exists(function(_,_,S_interval_)->true|_->false)quants->discard_empty_quantification_intervalsquantscondexp.erangemanflow|>OptionExt.return|E_stub_if(c,f1,f2)->assumecmanflow~fthen:(man.evalf1)~felse:(man.evalf2)|>OptionExt.return|_->None(** Computation of post-conditions *)(** ============================== *)(** Execute a global stub directive *)letexec_directivestubrangemanflow=(* Prepare assignments *)prepare_all_assignsstub.stub_directive_assignsstub.stub_directive_rangemanflow>>%funflow->(* Evaluate the body of the stub *)exec_bodystub.stub_directive_bodyNonerangemanflow>>%funflow->(* Clean locals *)clean_poststub.stub_directive_localsstub.stub_directive_rangemanflow>>%funflow->(* Clean assignments *)clean_all_assignsstub.stub_directive_assignsstub.stub_directive_rangemanflow(** Normalize a requirement condition by adding missing otherwise decorations *)letrecnormalize_requirement_conditioncond=matchekindcondwith|E_stub_otherwise_->cond|E_binop(O_log_and,e1,e2)->lete1'=normalize_requirement_conditione1inlete2'=normalize_requirement_conditione2inmk_log_ande1'e2'cond.erange|E_binop(O_log_or,e1,e2)->lete1'=normalize_requirement_conditione1inlete2'=normalize_requirement_conditione2inmk_log_ore1'e2'cond.erange|E_stub_quantified_formula(quants,{ekind=E_stub_otherwise(qcond,alarm)})->mk_stub_otherwise(mk_stub_quantified_formulaquantsqcondcond.erange)alarmcond.erange|E_stub_if(c,e1,e2)->lete1'=normalize_requirement_conditione1inlete2'=normalize_requirement_conditione2in{condwithekind=E_stub_if(c,e1',e2')}|_->mk_stub_otherwisecondNonecond.erange(** Check a stub requirement *)letexec_requirescondrangemanflow=(* Normalize the condition so that all sub-conditions are decorated with an
adequate `otherwise` expression *)letcond'=normalize_requirement_conditioncondin(* Evaluate the condition. Note that the evaluation of otherwise expression
is responsible for raising the alarm if a condition is not satisified. *)man.evalcond'flow>>$funrflow->matchekindrwith|E_constant(C_booltrue)->Post.returnflow|E_constant(C_boolfalse)->Flow.removeT_curflow|>Post.return|_->assertfalseletexecstmtmanflow=matchskindstmtwith|S_stub_directive(stub)->exec_directivestubstmt.srangemanflow|>OptionExt.return|S_stub_requirescond->exec_requirescondstmt.srangemanflow|>OptionExt.return|_->None(** Handler of queries *)(** ================== *)letaskquerymanflow=None(** Pretty printer *)(** ============== *)letprint_exprmanflowprinterexp=()endlet()=register_stateless_domain(moduleDomain)