123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206(* Yoann Padioleau
*
* Copyright (C) 2012 Facebook
*
* 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.
*)openCommonopenProlog_codemoduleG=Graph_codemoduleE=Entity_code(*****************************************************************************)(* Prelude *)(*****************************************************************************)(* Generating prolog DB facts from a graph_code.
*
* For more information look at h_program-lang/prolog_code.pl
* and its many predicates.
*)(*****************************************************************************)(* Main entry point *)(*****************************************************************************)(* see hook_use_edge_for_prolog below, a bit complicated those
* hooks of hooks ... it's just that the model of graph_code
* does not allow to do certain things that we want in codequery
* such as tracking the flow of values.
*)lethook_facts=ref[]letbuildg=letres=ref[]inletaddx=Common.pushxresinadd(Misc"% -*- prolog -*-");add(Misc":- discontiguous kind/2, at/3");add(Misc":- discontiguous type/2");add(Misc":- discontiguous docall/2, use/3");add(Misc":- discontiguous special/4");add(Misc":- discontiguous extends/2, implements/2");(* defs *)g|>G.iter_nodes(funn->let(str,kind)=nin(matchkindwith|E.Function|E.Global|E.Constant|E.Type|E.Macro|E.Package|E.Module(* todo? field and constructor have a X.Y.type.fld so should
* we generate for the entity a ([X;Y;type], fld) or ([X;Y], "type.fld")
*)|E.Field|E.Constructor|E.Method|E.ClassConstant|E.Exception->add(Kind(entity_of_strstr,kind))(* todo: interface | trait *)|E.Class->add(Kind(entity_of_strstr,kind))(* less: hmm if only have a proto, e.g. in lib.h should add it no? *)|E.Prototype|E.GlobalExtern->()|E.File|E.Dir->()|(E.TopStmts|E.Other_|E.MultiDirs)->pr2_genn;raiseTodo);(try(* todo: should avoid adding it twice when have both proto and func
* defined
*)letnodeinfo=G.nodeinfonginadd(At(entity_of_strstr,nodeinfo.G.pos.Parse_info.file,nodeinfo.G.pos.Parse_info.line));lett=matchnodeinfo.G.typwith|None->"unknown"|Somes->sinadd(Type(entity_of_strstr,t))withNot_found->()););(* uses *)(* we iter on the Use edges of the graph_code (see graph_code.ml), which
* contains the inheritance tree, call graph, and data graph information.
*)g|>G.iter_use_edges(funn1n2->matchn1,n2with(* less: at some point have to differentiate Extends and Implements
* depending on the _kind, but for now let's simplify and convert
* everything to a regular class inheritance
*)|((s1,E.Class),(s2,E.Class))->add(Extends(s1,s2))|((s1,(E.Function|E.Method)),(s2,(E.Function|E.Prototype|E.Method)))->add(Call(entity_of_strs1,entity_of_strs2))|((s1,(E.Function|E.Method|E.Global|E.Constant)),(s2,(E.Field|E.ClassConstant|E.Global)))->letinfo=G.edgeinfo_opt(n1,n2)G.Usegin(matchinfowith|None->add(UseData(entity_of_strs1,entity_of_strs2,None))|Some{G.read=false;G.write=false}->failwith(spf"use access with neither read or write: %s -> %s"s1s2)|Someinfo->ifinfo.G.readthenadd(UseData(entity_of_strs1,entity_of_strs2,Somefalse));ifinfo.G.writethenadd(UseData(entity_of_strs1,entity_of_strs2,Sometrue));)|_->());(* special uses *)!hook_facts|>List.iteradd;List.rev!res(* old: was for PHP.
| E.Method _ ->
let nodeinfo = G.nodeinfo n g in
let props = nodeinfo.G.props in
props +> List.iter (function
| E.Privacy priv -> add (P.Privacy (P.entity_of_str str, priv));
| _ -> ()
);
| E.Field ->
let (xs, x) = P.entity_of_str str in
if x =~ "\\$\\(.*\\)"
then add (P.Kind ((xs, Common.matched1 x), kind))
else failwith ("field does not contain $: " ^ x)
(* uses *)
| ((s1, E.Class _kind1), (s2, E.Class E.RegularClass)) ->
add (P.Extends (s1, s2))
| ((s1, E.Class _kind1), (s2, E.Class E.Trait)) ->
add (P.Mixins (s1, s2))
| ((s1, E.Class _kind1), (s2, E.Class E.Interface)) ->
add (P.Implements (s1, s2))
*)(* This is for codequery. In C the flow of values goes either
* via assignments of via calls (where the argument is assigned in
* the parameter)
*)typecontext=|NoCtx|CallCtxofGraph_code.node|AssignCtxofGraph_code.nodelethook_use_edge_for_prologctxin_assign(src,dst)g_loc=letkind=snddstin(matchkindwith|E.Global|E.Field->letoldinfoopt=G.edgeinfo_opt(src,dst)G.Useginletinfo=matcholdinfooptwith|Someinfo->info|None->{G.read=false;G.write=false}inletnewinfo=ifin_assignthen{infowithG.write=true}else{infowithG.read=true}inG.add_edgeinfo(src,dst)G.Usenewinfog|_->());letesrc=entity_of_str(fstsrc)inletedst=entity_of_str(fstdst)in(matchctx,kindwith|NoCtx,_->()|AssignCtxfld_node,E.Function->letefld=entity_of_str(fstfld_node)inhook_facts|>Common.push(Special(esrc,efld,edst,"field"))|CallCtxfunc_node,E.Function->letefunc=entity_of_str(fstfunc_node)inhook_facts|>Common.push(Special(esrc,efunc,edst,"function"))|_->())