123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157(* 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.
*)openCommonmoduleE=Entity_codemoduleG=Graph_code(*****************************************************************************)(* Prelude *)(*****************************************************************************)(*
* Graph_code uses the 'node' type for keys into different hashtbl (see
* commons/graph.ml). This is convenient when building such a graph
* in the different graph_code_xxx.ml files. But it also leads to
* many hashtbl operations when working on a Graph_code.
* This module introduces a new 'graph' type optimized to use arrays
* instead of hashtbl. Then certain operations like getting the list
* of children (Has) or list of dependent (Use) is very fast.
*)(*****************************************************************************)(* Types *)(*****************************************************************************)typegraph={name_to_i:(Graph_code.node,int)Hashtbl.t;i_to_name:Graph_code.nodearray;has_children:(intlist)array;use:(intlist)array;}(*****************************************************************************)(* Helpers *)(*****************************************************************************)lethashtbl_findhn=tryHashtbl.findhnwithNot_found->pr2_gen("PB:",n);raiseNot_found(*****************************************************************************)(* API *)(*****************************************************************************)letnb_nodesg=Array.lengthg.i_to_name(*****************************************************************************)(* Converting *)(*****************************************************************************)let(convert2:Graph_code.graph->graph)=fung->letn=G.nb_nodesginleth={name_to_i=Hashtbl.create(n/2);i_to_name=Array.maken("",E.Dir);has_children=Array.maken[];use=Array.maken[];}inleti=ref0ing|>G.iter_nodes(funnode->Hashtbl.addh.name_to_inode!i;h.i_to_name.(!i)<-node;incri;);g|>G.iter_nodes(funnode->leti=hashtbl_findh.name_to_inodeing|>G.succnodeG.Has|>List.iter(funnode2->letj=hashtbl_findh.name_to_inode2inh.has_children.(i)<-j::h.has_children.(i););g|>G.succnodeG.Use|>List.iter(funnode2->(matchnode2with(* ugly: less important dependency *)(* | _, E.Constant | _, E.ClassConstant -> () *)|_->letj=hashtbl_findh.name_to_inode2inh.use.(i)<-j::h.use.(i);)););hletconverta=Common.profile_code"Graph_code_opti.convert"(fun()->convert2a)(*****************************************************************************)(* Adapters *)(*****************************************************************************)letchildrenng=g.has_children.(hashtbl_findg.name_to_in)|>List.map(funi->g.i_to_name.(i))(* todo? does it include n? *)letall_childrenng=letrecauxi=letxs=g.has_children.(i)inifnullxsthen[i]elsei::(xs|>List.map(funi->auxi)|>List.flatten)inaux(hashtbl_findg.name_to_in)|>List.map(funi->g.i_to_name.(i))lethas_nodeng=Hashtbl.memg.name_to_in(*****************************************************************************)(* Adjust *)(*****************************************************************************)(* put polluting entries under an intermediate "parent/..." entry
* less: use extensible array so faster?
*)letadjust_graph_pack_some_children_under_dotdotdotparentto_packg=letdotdotdot=fstparent^"/..."inletnew_node=(dotdotdot,E.MultiDirs)inif(has_nodenew_nodeg)thenfailwith(spf"already a node with '%s' for a name"dotdotdot);letnew_idx=Array.lengthg.i_to_nameinletto_pack_idx=to_pack|>List.map(funn->hashtbl_findg.name_to_in)inletnew_g={name_to_i=Hashtbl.copyg.name_to_i;i_to_name=Array.appendg.i_to_name[|new_node|];has_children=Array.appendg.has_children[|to_pack_idx|];use=Array.appendg.use[|[]|];}inHashtbl.addnew_g.name_to_inew_nodenew_idx;letidx_parent=hashtbl_findnew_g.name_to_iparentinletidx_packs=to_pack_idx|>Common.hashset_of_listinnew_g.has_children.(idx_parent)<-(* bugfix: don't forget to add new_idx *)new_idx::new_g.has_children.(idx_parent)|>Common.exclude(funi->Hashtbl.memidx_packsi);new_g,new_node