123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223(* Yoann Padioleau
*
* Copyright (C) 2009, 2010, 2011 Facebook
* Copyright (C) 2019 r2c
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation, with the
* special exception on linking described in file license.txt.
*
* This library 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 file
* license.txt for more details.
*)openAst_genericmoduleA=Ast_generic(*****************************************************************************)(* Prelude *)(*****************************************************************************)(* A Control Flow Graph (CFG) for AST generic.
*
* Note that this is just for intra-procedural analysis. The CFG covers
* just one function. For inter-procedural analysis you may want to look
* at pfff/graph_code/ (or invest in learning Datalog).
*
* history:
* - CFG for C for coccinelle
* - CFG for PHP for checkModule at Facebook
* - CFG for AST generic for scheck at r2c
*)(*****************************************************************************)(* Types *)(*****************************************************************************)typenode={(* later: For now we just have node_kind, but with some data-flow
* analysis or with temporal logic we may want to add extra information
* in each CFG nodes.
* alt: We could also record such extra information in an external table
* that maps Ograph_extended.nodei, that is nodeid, to some information.
*)n:node_kind;(* for error report *)i:Parse_info.toption;}andnode_kind=(* special fake cfg nodes *)|Enter|Exit(* alt: An alternative is to store such information in the edges, but
* experience shows it's easier to encode it via regular nodes
*)|TrueNode|FalseNode|Join|IfHeaderofexpr|WhileHeaderofexpr|DoHeader|DoWhileTailofexpr|ForHeader|ForeachHeaderofpattern*expr|OtherStmtWithStmtHeaderofother_stmt_with_stmt_operator*expr|SwitchHeaderofexpr|SwitchEnd|Case|Default|Returnofexpr|Breakofexproption|Continueofexproption|TryHeader|CatchStart|Catch|TryEnd|Throwofexpr(* statements without multiple outgoing or ingoing edges, such as
* expression statements.
*)|SimpleNodeofsimple_node(* not used for now, was used in coccinelle:
| BlockStart of tok (* { *)
| BlockEnd of tok (* } *)
| Else
| Elsif
*)(* mostly a copy-paste of a subset of Ast.stmt *)andsimple_node=(* later: some expr includes Exit, Eval, Include, etc which
* also have an influence on the control flow ...
* We may want to uplift those constructors here and have
* a better expr type
*)|ExprStmtofexpr|DefStmtofdefinition|DirectiveStmtofdirective|Assertofexpr*exproption|OtherStmtofother_stmt_operator*anylist(* not part of Ast.stmt but useful to have in CFG for
* dataflow analysis purpose *)|Parameterofparameter(* For now there is just one kind of edge.
* later: we may have more, see the ShadowNode idea of Julia Lawall?
*)typeedge=Directtypeflow=(node,edge)Ograph_extended.ograph_mutabletypenodei=Ograph_extended.nodei(*****************************************************************************)(* String of *)(*****************************************************************************)(* This is useful in graphviz and in dataflow analysis result tables
* to just get a quick idea of what a node is (without too much details).
*)letshort_string_of_node_kindnkind=matchnkindwith|Enter->"<enter>"|Exit->"<exit>"|TrueNode->"<TRUE path>"|FalseNode->"<FALSE path>"|Join->"<join>"|IfHeader_->"if(...)"|WhileHeader_->"while(...)"|DoHeader->"do"|DoWhileTail_->"while(...);"|ForHeader->"for(...)"|ForeachHeader_->"foreach(...)"|OtherStmtWithStmtHeader_->"<otherstmtheader>"|Return_->"return ...;"|Continue_->"continue ...;"|Break_->"break ...;"|SwitchHeader_->"switch(...)"|SwitchEnd->"<endswitch>"|Case->"case: ..."|Default->"default:"|TryHeader->"try"|CatchStart->"<catchstart>"|Catch->"catch(...)"|TryEnd->"<endtry>"|Throw_->"throw ...;"|SimpleNodex->(matchxwith|ExprStmt_->"<expt_stmt>;"|DefStmt_->"<def>"|DirectiveStmt_->"<directive>"|Assert_->"<assert>"|Parameter_->"<param>"|OtherStmt_->"<other_stmt>")letshort_string_of_nodenode=short_string_of_node_kindnode.n(*****************************************************************************)(* Converters *)(*****************************************************************************)letsimple_node_of_stmt_optstmt=matchstmtwith|A.ExprStmte->Some(ExprStmte)|A.Assert(e1,e2)->Some(Assert(e1,e2))|A.DefStmtx->Some(DefStmtx)|A.DirectiveStmtx->Some(DirectiveStmtx)|A.OtherStmt(a,b)->Some(OtherStmt(a,b))|(A.Block_|A.If(_,_,_)|A.While(_,_)|A.DoWhile(_,_)|A.For(_,_)|A.Switch(_,_)|A.Return_|A.Continue_|A.Break_|A.Label(_,_)|A.Goto_|A.Throw_|A.Try(_,_,_)|A.OtherStmtWithStmt_)->Noneletany_of_simple_node=function|ExprStmte->A.S(A.ExprStmte)|Assert(e1,e2)->A.S(A.Assert(e1,e2))|DefStmtx->A.S(A.DefStmtx)|DirectiveStmtx->A.S(A.DirectiveStmtx)|OtherStmt(a,b)->A.S(A.OtherStmt(a,b))|Parameterx->A.Pax(*****************************************************************************)(* Accessors *)(*****************************************************************************)letfind_nodefcfg=cfg#nodes#tolist|>Common.find_some(fun(nodei,node)->iffnodethenSomenodeielseNone)letfind_exitcfg=find_node(funnode->node.n=Exit)cfgletfind_entercfg=find_node(funnode->node.n=Enter)cfg(* using internally graphviz dot and ghostview on X11 *)let(display_flow:flow->unit)=funflow->flow|>Ograph_extended.print_ograph_mutable_generic~s_of_node:(fun(_nodei,node)->short_string_of_node_kindnode.n,None,None)