[capture] get rid of call graphs

Summary:
They were constructed for each source file, and then joined into a global call
graph, only to get per-file lists of procedures. A tad wasteful.

Get this list from cfgs instead. Still record them in `exe_env` for now as
changing that code is a whole other beast.

One test falls victim of the flakiness of the analysis of recursive functions.

Reviewed By: jeremydubreil, mbouaziz

Differential Revision: D6324268

fbshipit-source-id: d5ff58f
master
Jules Villard 7 years ago committed by Facebook Github Bot
parent f2029af50a
commit daa5154399

@ -1,281 +0,0 @@
(*
* Copyright (c) 2009 - 2013 Monoidics ltd.
* Copyright (c) 2013 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
(** Module for call graphs *)
open! IStd
module Hashtbl = Caml.Hashtbl
module L = Logging
module F = Format
type node = Typ.Procname.t
type in_out_calls =
{ in_calls: int (** total number of in calls transitively *)
; out_calls: int (** total number of out calls transitively *) }
type node_info =
{ mutable defined: bool (** defined procedure as opposed to just declared *)
; mutable parents: Typ.Procname.Set.t
; mutable children: Typ.Procname.Set.t
; mutable ancestors: Typ.Procname.Set.t option (** ancestors are computed lazily *)
; mutable heirs: Typ.Procname.Set.t option (** heirs are computed lazily *)
; mutable recursive_dependents: Typ.Procname.Set.t option
(** recursive dependents are computed lazily *)
; mutable in_out_calls: in_out_calls option (** calls are computed lazily *) }
(** Type for call graph *)
type t =
{ source: SourceFile.t (** path for the source file *)
; node_map: node_info Typ.Procname.Hash.t (** map from node to node_info *) }
let create source = {source; node_map= Typ.Procname.Hash.create 3}
let add_node g n ~defined =
try
let info = Typ.Procname.Hash.find g.node_map n in
(* defined and disabled only go from false to true
to avoid accidental overwrite to false by calling add_edge *)
if defined then info.defined <- true
with Not_found ->
let info =
{ defined
; parents= Typ.Procname.Set.empty
; children= Typ.Procname.Set.empty
; ancestors= None
; heirs= None
; recursive_dependents= None
; in_out_calls= None }
in
Typ.Procname.Hash.add g.node_map n info
let remove_node_defined g n =
try
let info = Typ.Procname.Hash.find g.node_map n in
info.defined <- false
with Not_found -> ()
let add_defined_node g n = add_node g n ~defined:true
(** Compute the ancestors of the node, if not already computed *)
let compute_ancestors g node =
let todo = ref (Typ.Procname.Set.singleton node) in
let seen = ref Typ.Procname.Set.empty in
let result = ref Typ.Procname.Set.empty in
while not (Typ.Procname.Set.is_empty !todo) do
let current = Typ.Procname.Set.choose !todo in
todo := Typ.Procname.Set.remove current !todo ;
if not (Typ.Procname.Set.mem current !seen) then (
seen := Typ.Procname.Set.add current !seen ;
let info = Typ.Procname.Hash.find g current in
match info.ancestors with
| Some ancestors ->
result := Typ.Procname.Set.union !result ancestors
| None ->
result := Typ.Procname.Set.union !result info.parents ;
todo := Typ.Procname.Set.union !todo info.parents )
done ;
!result
(** Compute the heirs of the node, if not already computed *)
let compute_heirs g node =
let todo = ref (Typ.Procname.Set.singleton node) in
let seen = ref Typ.Procname.Set.empty in
let result = ref Typ.Procname.Set.empty in
while not (Typ.Procname.Set.is_empty !todo) do
let current = Typ.Procname.Set.choose !todo in
todo := Typ.Procname.Set.remove current !todo ;
if not (Typ.Procname.Set.mem current !seen) then (
seen := Typ.Procname.Set.add current !seen ;
let info = Typ.Procname.Hash.find g current in
match info.heirs with
| Some heirs ->
result := Typ.Procname.Set.union !result heirs
| None ->
result := Typ.Procname.Set.union !result info.children ;
todo := Typ.Procname.Set.union !todo info.children )
done ;
!result
(** Compute the ancestors of the node, if not pre-computed already *)
let get_ancestors (g: t) node =
let info = Typ.Procname.Hash.find g.node_map node in
match info.ancestors with
| None ->
let ancestors = compute_ancestors g.node_map node in
info.ancestors <- Some ancestors ;
let size = Typ.Procname.Set.cardinal ancestors in
if size > 1000 then
L.(debug Analysis Medium) "%a has %d ancestors@." Typ.Procname.pp node size ;
ancestors
| Some ancestors ->
ancestors
(** Compute the heirs of the node, if not pre-computed already *)
let get_heirs (g: t) node =
let info = Typ.Procname.Hash.find g.node_map node in
match info.heirs with
| None ->
let heirs = compute_heirs g.node_map node in
info.heirs <- Some heirs ;
let size = Typ.Procname.Set.cardinal heirs in
if size > 1000 then L.(debug Analysis Medium) "%a has %d heirs@." Typ.Procname.pp node size ;
heirs
| Some heirs ->
heirs
let node_defined (g: t) n =
try
let info = Typ.Procname.Hash.find g.node_map n in
info.defined
with Not_found -> false
let add_edge g nfrom nto =
add_node g nfrom ~defined:false ;
add_node g nto ~defined:false ;
let info_from = Typ.Procname.Hash.find g.node_map nfrom in
let info_to = Typ.Procname.Hash.find g.node_map nto in
info_from.children <- Typ.Procname.Set.add nto info_from.children ;
info_to.parents <- Typ.Procname.Set.add nfrom info_to.parents
(** iterate over the elements of a node_map in node order *)
let node_map_iter f g =
let table = ref [] in
Typ.Procname.Hash.iter (fun node info -> table := (node, info) :: !table) g.node_map ;
let cmp ((n1: Typ.Procname.t), _) ((n2: Typ.Procname.t), _) = Typ.Procname.compare n1 n2 in
List.iter ~f:(fun (n, info) -> f n info) (List.sort ~cmp !table)
let get_nodes (g: t) =
let nodes = ref Typ.Procname.Set.empty in
let f node _ = nodes := Typ.Procname.Set.add node !nodes in
node_map_iter f g ; !nodes
let compute_calls g node =
{ in_calls= Typ.Procname.Set.cardinal (get_ancestors g node)
; out_calls= Typ.Procname.Set.cardinal (get_heirs g node) }
(** Compute the calls of the node, if not pre-computed already *)
let get_calls (g: t) node =
let info = Typ.Procname.Hash.find g.node_map node in
match info.in_out_calls with
| None ->
let calls = compute_calls g node in
info.in_out_calls <- Some calls ;
calls
| Some calls ->
calls
let get_all_nodes (g: t) =
let nodes = Typ.Procname.Set.elements (get_nodes g) in
List.map ~f:(fun node -> (node, get_calls g node)) nodes
let node_get_num_ancestors g n = (n, Typ.Procname.Set.cardinal (get_ancestors g n))
let get_edges (g: t) : ((node * int) * (node * int)) list =
let edges = ref [] in
let f node info =
Typ.Procname.Set.iter
(fun nto -> edges := (node_get_num_ancestors g node, node_get_num_ancestors g nto) :: !edges)
info.children
in
node_map_iter f g ; !edges
(** nodes with defined flag, and edges *)
type nodes_and_edges = (node * bool) list * (node * node) list
(** Return the list of nodes, with defined+disabled flags, and the list of edges *)
let get_nodes_and_edges (g: t) : nodes_and_edges =
let nodes = ref [] in
let edges = ref [] in
let do_children node nto = edges := (node, nto) :: !edges in
let f node info =
nodes := (node, info.defined) :: !nodes ;
Typ.Procname.Set.iter (do_children node) info.children
in
node_map_iter f g ; (!nodes, !edges)
(** Return the list of nodes which are defined *)
let get_defined_nodes (g: t) =
let nodes, _ = get_nodes_and_edges g in
let get_node (node, _) = node in
List.map ~f:get_node (List.filter ~f:(fun (_, defined) -> defined) nodes)
(** [extend cg1 gc2] extends [cg1] in place with nodes and edges from [gc2];
undefined nodes become defined if at least one side is. *)
let extend cg_old cg_new =
let nodes, edges = get_nodes_and_edges cg_new in
List.iter ~f:(fun (node, defined) -> add_node cg_old node ~defined) nodes ;
List.iter ~f:(fun (nfrom, nto) -> add_edge cg_old nfrom nto) edges
(** Begin support for serialization *)
let callgraph_serializer : (SourceFile.t * nodes_and_edges) Serialization.serializer =
Serialization.create_serializer Serialization.Key.cg
(** Load a call graph from a file *)
let load_from_file (filename: DB.filename) : t option =
match Serialization.read_from_file callgraph_serializer filename with
| None ->
None
| Some (source, (nodes, edges)) ->
let g = create source in
List.iter ~f:(fun (node, defined) -> if defined then add_defined_node g node) nodes ;
List.iter ~f:(fun (nfrom, nto) -> add_edge g nfrom nto) edges ;
Some g
(** Save a call graph into a file *)
let store_to_file (filename: DB.filename) (call_graph: t) =
Serialization.write_to_file callgraph_serializer filename
~data:(call_graph.source, get_nodes_and_edges call_graph)
let pp_graph_dotty (g: t) fmt =
let nodes_with_calls = get_all_nodes g in
let get_shape (n, _) = if node_defined g n then "box" else "diamond" in
let pp_node fmt (n, _) = F.fprintf fmt "\"%s\"" (Typ.Procname.to_filename n) in
let pp_node_label fmt (n, calls) =
F.fprintf fmt "\"%a | calls=%d %d)\"" Typ.Procname.pp n calls.in_calls calls.out_calls
in
F.fprintf fmt "digraph {@\n" ;
List.iter
~f:(fun nc ->
F.fprintf fmt "%a [shape=box,label=%a,color=%s,shape=%s]@\n" pp_node nc pp_node_label nc
"red" (get_shape nc) )
nodes_with_calls ;
List.iter ~f:(fun (s, d) -> F.fprintf fmt "%a -> %a@\n" pp_node s pp_node d) (get_edges g) ;
F.fprintf fmt "}@."
(** Print the call graph as a dotty file. *)
let save_call_graph_dotty source (g: t) =
let fname_dot =
DB.Results_dir.path_to_filename (DB.Results_dir.Abs_source_dir source) ["call_graph.dot"]
in
let outc = Out_channel.create (DB.filename_to_string fname_dot) in
let fmt = F.formatter_of_out_channel outc in
pp_graph_dotty g fmt ; Out_channel.close outc

@ -1,52 +0,0 @@
(*
* Copyright (c) 2009 - 2013 Monoidics ltd.
* Copyright (c) 2013 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
open! IStd
(** Module for call graphs *)
(** the type of a call graph *)
type t
(** A call graph consists of a set of nodes (Typ.Procname.t), and edges between them.
A node can be defined or undefined (to represent whether we have code for it).
In an edge from [n1] to [n2], indicating that [n1] calls [n2],
[n1] is the parent and [n2] is the child.
Node [n1] is dependent on [n2] if there is a path from [n1] to [n2]
using the child relationship. *)
val add_edge : t -> Typ.Procname.t -> Typ.Procname.t -> unit
(** [add_edge cg f t] adds an edge from [f] to [t] in the call graph [cg].
The nodes are also added as undefined, unless already present. *)
val add_defined_node : t -> Typ.Procname.t -> unit
(** Add a node to the call graph as defined *)
val create : SourceFile.t -> t
(** Create an empty call graph *)
val extend : t -> t -> unit
(** [extend cg1 gc2] extends [cg1] in place with nodes and edges from [gc2];
undefined nodes become defined if at least one side is. *)
val get_defined_nodes : t -> Typ.Procname.t list
(** Return the list of nodes which are defined *)
val load_from_file : DB.filename -> t option
(** Load a call graph from a file *)
val remove_node_defined : t -> Typ.Procname.t -> unit
(** Remove the defined flag from a node, if it exists. *)
val save_call_graph_dotty : SourceFile.t -> t -> unit
(** Print the call graph as a dotty file. *)
val store_to_file : DB.filename -> t -> unit
(** Save a call graph into a file *)

@ -1109,6 +1109,9 @@ module Procname = struct
let serialize pname =
let default () = Sqlite3.Data.TEXT (to_filename pname) in
Base.Hashtbl.find_or_add pname_to_key pname ~default
let clear_cache () = Base.Hashtbl.clear pname_to_key
end
module SQLiteList = SqliteUtils.MarshalledData (struct

@ -392,6 +392,8 @@ module Procname : sig
module SQLite : sig
val serialize : t -> Sqlite3.Data.t
val clear_cache : unit -> unit
end
module SQLiteList : SqliteUtils.Data with type t = t list

@ -17,6 +17,7 @@ module F = Format
let analyze_exe_env_tasks cluster exe_env : Tasks.t =
L.progressbar_file () ;
Specs.clear_spec_tbl () ;
Typ.Procname.SQLite.clear_cache () ;
Random.self_init () ;
Tasks.create
[ (fun () ->

@ -60,7 +60,7 @@ type source_dir = string [@@deriving compare]
(** expose the source dir as a string *)
let source_dir_to_string source_dir = source_dir
(** get the path to an internal file with the given extention (.cg, .tenv) *)
(** get the path to an internal file with the given extention (.tenv, ...) *)
let source_dir_get_internal_file source_dir extension =
let source_dir_name =
append_crc_cutoff (Caml.Filename.remove_extension (Filename.basename source_dir))

@ -95,7 +95,7 @@ val source_dir_to_string : source_dir -> string
(** expose the source dir as a string *)
val source_dir_get_internal_file : source_dir -> string -> filename
(** get the path to an internal file with the given extention (.cg, .tenv) *)
(** get the path to an internal file with the given extention (.tenv, ...) *)
val source_dir_from_source_file : SourceFile.t -> source_dir
(** get the source directory corresponding to a source file *)

@ -28,7 +28,6 @@ type str_node_map = (string, Procdesc.Node.t) Hashtbl.t
type t =
{ translation_unit_context: CFrontend_config.translation_unit_context
; tenv: Tenv.t
; cg: Cg.t
; cfg: Cfg.t
; procdesc: Procdesc.t
; is_objc_method: bool
@ -41,11 +40,10 @@ type t =
; label_map: str_node_map
; vars_to_destroy: Clang_ast_t.decl list StmtMap.t }
let create_context translation_unit_context tenv cg cfg procdesc curr_class return_param_typ
let create_context translation_unit_context tenv cfg procdesc curr_class return_param_typ
is_objc_method outer_context vars_to_destroy =
{ translation_unit_context
; tenv
; cg
; cfg
; procdesc
; curr_class
@ -57,8 +55,6 @@ let create_context translation_unit_context tenv cg cfg procdesc curr_class retu
; vars_to_destroy }
let get_cg context = context.cg
let get_procdesc context = context.procdesc
let rec is_objc_method context =

@ -22,7 +22,6 @@ type str_node_map = (string, Procdesc.Node.t) Caml.Hashtbl.t
type t =
{ translation_unit_context: CFrontend_config.translation_unit_context
; tenv: Tenv.t
; cg: Cg.t
; cfg: Cfg.t
; procdesc: Procdesc.t
; is_objc_method: bool
@ -39,8 +38,6 @@ type t =
val get_procdesc : t -> Procdesc.t
val get_cg : t -> Cg.t
val get_curr_class : t -> curr_class
val get_curr_class_typename : t -> Typ.Name.t
@ -50,7 +47,7 @@ val get_curr_class_decl_ptr : curr_class -> Clang_ast_t.pointer
val is_objc_method : t -> bool
val create_context :
CFrontend_config.translation_unit_context -> Tenv.t -> Cg.t -> Cfg.t -> Procdesc.t -> curr_class
CFrontend_config.translation_unit_context -> Tenv.t -> Cfg.t -> Procdesc.t -> curr_class
-> Typ.t option -> bool -> t option -> Clang_ast_t.decl list StmtMap.t -> t
val add_block_static_var : t -> Typ.Procname.t -> Pvar.t * Typ.t -> unit

@ -20,13 +20,12 @@ let compute_icfg trans_unit_ctx tenv ast =
| Clang_ast_t.TranslationUnitDecl (_, decl_list, _, _) ->
CFrontend_config.global_translation_unit_decls := decl_list ;
L.(debug Capture Verbose) "@\n Start creating icfg@\n" ;
let cg = Cg.create trans_unit_ctx.CFrontend_config.source_file in
let cfg = Cfg.create () in
List.iter
~f:(CFrontend_declImpl.translate_one_declaration trans_unit_ctx tenv cg cfg `DeclTraversal)
~f:(CFrontend_declImpl.translate_one_declaration trans_unit_ctx tenv cfg `DeclTraversal)
decl_list ;
L.(debug Capture Verbose) "@\n Finished creating icfg@\n" ;
(cg, cfg)
cfg
| _ ->
assert false
@ -46,25 +45,21 @@ let do_source_file (translation_unit_context: CFrontend_config.translation_unit_
let source_file = translation_unit_context.CFrontend_config.source_file in
L.(debug Capture Verbose)
"@\n Start building call/cfg graph for '%a'....@\n" SourceFile.pp source_file ;
let call_graph, cfg = compute_icfg translation_unit_context tenv ast in
let cfg = compute_icfg translation_unit_context tenv ast in
L.(debug Capture Verbose)
"@\n End building call/cfg graph for '%a'.@\n" SourceFile.pp source_file ;
(* This part below is a boilerplate in every frontends. *)
(* This could be moved in the cfg_infer module *)
let source_dir = DB.source_dir_from_source_file source_file in
let tenv_file = DB.source_dir_get_internal_file source_dir ".tenv" in
let cg_file = DB.source_dir_get_internal_file source_dir ".cg" in
NullabilityPreanalysis.analysis cfg tenv ;
Cg.store_to_file cg_file call_graph ;
Cfg.store source_file cfg ;
Tenv.sort_fields_tenv tenv ;
Tenv.store_to_file tenv_file tenv ;
if Config.debug_mode then Cfg.check_cfg_connectedness cfg ;
if Config.debug_mode || Config.testing_mode || Config.frontend_tests
|| Option.is_some Config.icfg_dotty_outfile
then (
Dotty.print_icfg_dotty source_file cfg ;
Cg.save_call_graph_dotty source_file call_graph ) ;
then Dotty.print_icfg_dotty source_file cfg ;
L.(debug Capture Verbose) "%a" Cfg.pp_proc_signatures cfg ;
let procedures_translated_summary =
EventLogger.ProceduresTranslatedSummary

@ -68,8 +68,8 @@ let protect ~f ~recover ~pp_context (trans_unit_ctx: CFrontend_config.translatio
module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFrontend = struct
let model_exists procname = Specs.summary_exists_in_models procname && not Config.models_mode
(* Translates the method/function's body into nodes of the cfg. *)
let add_method ?(is_destructor_wrapper= false) trans_unit_ctx tenv cg cfg class_decl_opt procname
(** Translates the method/function's body into nodes of the cfg. *)
let add_method ?(is_destructor_wrapper= false) trans_unit_ctx tenv cfg class_decl_opt procname
body has_return_param is_objc_method outer_context_opt extra_instrs =
L.(debug Capture Verbose)
"@\n@\n>>---------- ADDING METHOD: '%a' ---------<<@\n@\n" Typ.Procname.pp procname ;
@ -86,7 +86,7 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
| procdesc when Procdesc.is_defined procdesc && not (model_exists procname) ->
let vars_to_destroy = CTrans_utils.Scope.compute_vars_to_destroy body in
let context =
CContext.create_context trans_unit_ctx tenv cg cfg procdesc class_decl_opt
CContext.create_context trans_unit_ctx tenv cfg procdesc class_decl_opt
has_return_param is_objc_method outer_context_opt vars_to_destroy
in
let start_node = Procdesc.get_start_node procdesc in
@ -100,8 +100,7 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
let proc_attributes = Procdesc.get_attributes procdesc in
Procdesc.Node.add_locals_ret_declaration start_node proc_attributes
(Procdesc.get_locals procdesc) ;
Procdesc.node_set_succs_exn procdesc start_node meth_body_nodes [] ;
Cg.add_defined_node (CContext.get_cg context) (Procdesc.get_proc_name procdesc)
Procdesc.node_set_succs_exn procdesc start_node meth_body_nodes []
| _ ->
()
| exception Not_found ->
@ -110,7 +109,7 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
protect ~f ~recover ~pp_context trans_unit_ctx
let function_decl trans_unit_ctx tenv cfg cg func_decl block_data_opt =
let function_decl trans_unit_ctx tenv cfg func_decl block_data_opt =
let captured_vars, outer_context_opt =
match block_data_opt with
| Some (outer_context, _, _, captured_vars) ->
@ -129,14 +128,14 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
if CMethod_trans.create_local_procdesc trans_unit_ctx cfg tenv ms [body] captured_vars
false
then
add_method trans_unit_ctx tenv cg cfg CContext.ContextNoCls procname body
add_method trans_unit_ctx tenv cfg CContext.ContextNoCls procname body
return_param_typ_opt false outer_context_opt extra_instrs
| None ->
()
let process_method_decl ?(set_objc_accessor_attr= false) ?(is_destructor= false) trans_unit_ctx
tenv cg cfg curr_class meth_decl ~is_objc =
tenv cfg curr_class meth_decl ~is_objc =
let ms, body_opt, extra_instrs =
CMethod_trans.method_signature_of_decl trans_unit_ctx tenv meth_decl None
in
@ -154,7 +153,7 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
if CMethod_trans.create_local_procdesc ~set_objc_accessor_attr trans_unit_ctx cfg tenv
ms [body] [] is_objc_inst_method
then
add_method trans_unit_ctx tenv cg cfg curr_class procname body return_param_typ_opt
add_method trans_unit_ctx tenv cfg curr_class procname body return_param_typ_opt
is_objc None extra_instrs ~is_destructor_wrapper:true ;
let new_method_name =
Config.clang_inner_destructor_prefix ^ Typ.Procname.get_method procname
@ -170,8 +169,8 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
if CMethod_trans.create_local_procdesc ~set_objc_accessor_attr trans_unit_ctx cfg tenv ms'
[body] [] is_objc_inst_method
then
add_method trans_unit_ctx tenv cg cfg curr_class procname' body return_param_typ_opt
is_objc None extra_instrs ~is_destructor_wrapper:false
add_method trans_unit_ctx tenv cfg curr_class procname' body return_param_typ_opt is_objc
None extra_instrs ~is_destructor_wrapper:false
| None ->
if set_objc_accessor_attr then
ignore
@ -179,7 +178,7 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
[] [] is_objc_inst_method)
let process_property_implementation trans_unit_ctx tenv cg cfg curr_class
let process_property_implementation trans_unit_ctx tenv cfg curr_class
obj_c_property_impl_decl_info =
let property_decl_opt = obj_c_property_impl_decl_info.Clang_ast_t.opidi_property_decl in
match CAst_utils.get_decl_opt_with_decl_ref property_decl_opt with
@ -187,8 +186,8 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
let process_accessor pointer =
match CAst_utils.get_decl_opt_with_decl_ref pointer with
| Some (ObjCMethodDecl _ as dec) ->
process_method_decl ~set_objc_accessor_attr:true trans_unit_ctx tenv cg cfg
curr_class dec ~is_objc:true
process_method_decl ~set_objc_accessor_attr:true trans_unit_ctx tenv cfg curr_class
dec ~is_objc:true
| _ ->
()
in
@ -198,18 +197,18 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
()
let process_one_method_decl trans_unit_ctx tenv cg cfg curr_class dec =
let process_one_method_decl trans_unit_ctx tenv cfg curr_class dec =
let open Clang_ast_t in
match dec with
| CXXMethodDecl _ | CXXConstructorDecl _ | CXXConversionDecl _ ->
process_method_decl trans_unit_ctx tenv cg cfg curr_class dec ~is_objc:false
process_method_decl trans_unit_ctx tenv cfg curr_class dec ~is_objc:false
| CXXDestructorDecl _ ->
process_method_decl trans_unit_ctx tenv cg cfg curr_class dec ~is_objc:false
process_method_decl trans_unit_ctx tenv cfg curr_class dec ~is_objc:false
~is_destructor:true
| ObjCMethodDecl _ ->
process_method_decl trans_unit_ctx tenv cg cfg curr_class dec ~is_objc:true
process_method_decl trans_unit_ctx tenv cfg curr_class dec ~is_objc:true
| ObjCPropertyImplDecl (_, obj_c_property_impl_decl_info) ->
process_property_implementation trans_unit_ctx tenv cg cfg curr_class
process_property_implementation trans_unit_ctx tenv cfg curr_class
obj_c_property_impl_decl_info
| EmptyDecl _ | ObjCIvarDecl _ | ObjCPropertyDecl _ ->
()
@ -220,8 +219,8 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
()
let process_methods trans_unit_ctx tenv cg cfg curr_class decl_list =
List.iter ~f:(process_one_method_decl trans_unit_ctx tenv cg cfg curr_class) decl_list
let process_methods trans_unit_ctx tenv cfg curr_class decl_list =
List.iter ~f:(process_one_method_decl trans_unit_ctx tenv cfg curr_class) decl_list
(** Given REVERSED list of method qualifiers (method_name::class_name::rest_quals), return
@ -287,40 +286,38 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
(* Translate one global declaration *)
let rec translate_one_declaration trans_unit_ctx tenv cg cfg decl_trans_context dec =
let rec translate_one_declaration trans_unit_ctx tenv cfg decl_trans_context dec =
let open Clang_ast_t in
(* each procedure has different scope: start names from id 0 *)
Ident.NameGenerator.reset () ;
let translate dec =
translate_one_declaration trans_unit_ctx tenv cg cfg decl_trans_context dec
in
let translate dec = translate_one_declaration trans_unit_ctx tenv cfg decl_trans_context dec in
( if should_translate_decl trans_unit_ctx dec decl_trans_context then
let dec_ptr = (Clang_ast_proj.get_decl_tuple dec).di_pointer in
match dec with
| FunctionDecl (_, _, _, _) ->
function_decl trans_unit_ctx tenv cfg cg dec None
function_decl trans_unit_ctx tenv cfg dec None
| ObjCInterfaceDecl (_, _, decl_list, _, _) ->
let curr_class = CContext.ContextClsDeclPtr dec_ptr in
ignore
(ObjcInterface_decl.interface_declaration CType_decl.qual_type_to_sil_type tenv dec) ;
process_methods trans_unit_ctx tenv cg cfg curr_class decl_list
process_methods trans_unit_ctx tenv cfg curr_class decl_list
| ObjCProtocolDecl (_, _, decl_list, _, _) ->
let curr_class = CContext.ContextClsDeclPtr dec_ptr in
ignore (ObjcProtocol_decl.protocol_decl CType_decl.qual_type_to_sil_type tenv dec) ;
process_methods trans_unit_ctx tenv cg cfg curr_class decl_list
process_methods trans_unit_ctx tenv cfg curr_class decl_list
| ObjCCategoryDecl (_, _, decl_list, _, _) ->
let curr_class = CContext.ContextClsDeclPtr dec_ptr in
ignore (ObjcCategory_decl.category_decl CType_decl.qual_type_to_sil_type tenv dec) ;
process_methods trans_unit_ctx tenv cg cfg curr_class decl_list
process_methods trans_unit_ctx tenv cfg curr_class decl_list
| ObjCCategoryImplDecl (_, _, decl_list, _, _) ->
let curr_class = CContext.ContextClsDeclPtr dec_ptr in
ignore (ObjcCategory_decl.category_impl_decl CType_decl.qual_type_to_sil_type tenv dec) ;
process_methods trans_unit_ctx tenv cg cfg curr_class decl_list
process_methods trans_unit_ctx tenv cfg curr_class decl_list
| ObjCImplementationDecl (_, _, decl_list, _, _) ->
let curr_class = CContext.ContextClsDeclPtr dec_ptr in
let qual_type_to_sil_type = CType_decl.qual_type_to_sil_type in
ignore (ObjcInterface_decl.interface_impl_declaration qual_type_to_sil_type tenv dec) ;
process_methods trans_unit_ctx tenv cg cfg curr_class decl_list
process_methods trans_unit_ctx tenv cfg curr_class decl_list
| CXXMethodDecl (decl_info, _, _, _, _)
| CXXConstructorDecl (decl_info, _, _, _, _)
| CXXConversionDecl (decl_info, _, _, _, _)
@ -332,7 +329,7 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
match class_decl with
| (Some CXXRecordDecl _ | Some ClassTemplateSpecializationDecl _) when Config.cxx ->
let curr_class = CContext.ContextClsDeclPtr parent_ptr in
process_methods trans_unit_ctx tenv cg cfg curr_class [dec]
process_methods trans_unit_ctx tenv cfg curr_class [dec]
| Some dec ->
L.(debug Capture Verbose)
"Methods of %s skipped@\n"
@ -364,8 +361,8 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
in
let body = Clang_ast_t.DeclStmt (stmt_info, [], [dec]) in
ignore (CMethod_trans.create_local_procdesc trans_unit_ctx cfg tenv ms [body] [] false) ;
add_method trans_unit_ctx tenv cg cfg CContext.ContextNoCls procname body None false
None []
add_method trans_unit_ctx tenv cfg CContext.ContextNoCls procname body None false None
[]
(* Note that C and C++ records are treated the same way
Skip translating implicit struct declarations, unless they have
full definition (which happens with C++ lambdas) *)

@ -29,10 +29,10 @@ end
module type CFrontend = sig
val function_decl :
CFrontend_config.translation_unit_context -> Tenv.t -> Cfg.t -> Cg.t -> Clang_ast_t.decl
CFrontend_config.translation_unit_context -> Tenv.t -> Cfg.t -> Clang_ast_t.decl
-> block_data option -> unit
val translate_one_declaration :
CFrontend_config.translation_unit_context -> Tenv.t -> Cg.t -> Cfg.t -> decl_trans_context
CFrontend_config.translation_unit_context -> Tenv.t -> Cfg.t -> decl_trans_context
-> Clang_ast_t.decl -> unit
end

@ -190,8 +190,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
let open CContext in
(* translation will reset Ident counter, save it's state and restore it afterwards *)
let ident_state = Ident.NameGenerator.get_current () in
F.translate_one_declaration context.translation_unit_context context.tenv context.cg
context.cfg `Translation decl ;
F.translate_one_declaration context.translation_unit_context context.tenv context.cfg
`Translation decl ;
Ident.NameGenerator.set_current ident_state
@ -1009,16 +1009,12 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
let res_trans_to_parent =
PriorityNode.compute_results_to_parent trans_state_pri sil_loc nname si all_res_trans
in
let add_cg_edge callee_pname = Cg.add_edge context.CContext.cg procname callee_pname in
Option.iter ~f:add_cg_edge callee_pname_opt ;
{res_trans_to_parent with exps= res_trans_call.exps}
and cxx_method_construct_call_trans trans_state_pri result_trans_callee params_stmt si
function_type is_cpp_call_virtual extra_res_trans =
let open CContext in
let context = trans_state_pri.context in
let procname = Procdesc.get_proc_name context.procdesc in
let sil_loc = CLocation.get_sil_location si context in
(* first for method address, second for 'this' expression and other parameters *)
assert (List.length result_trans_callee.exps >= 1) ;
@ -1056,7 +1052,6 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
let result_trans_to_parent =
PriorityNode.compute_results_to_parent trans_state_pri sil_loc nname si all_res_trans
in
Cg.add_edge context.CContext.cg procname callee_pname ;
{result_trans_to_parent with exps= res_trans_call.exps}
@ -1244,7 +1239,6 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
let is_virtual =
CMethod_trans.equal_method_call_type method_call_type CMethod_trans.MCVirtual
in
Cg.add_edge context.CContext.cg procname callee_name ;
let call_flags = {CallFlags.default with CallFlags.cf_virtual= is_virtual} in
let method_sil = Exp.Const (Const.Cfun callee_name) in
let res_trans_call =
@ -2607,9 +2601,6 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
let qual_type = expr_info.Clang_ast_t.ei_qual_type in
let block_pname = CProcname.mk_fresh_block_procname procname in
let typ = CType_decl.qual_type_to_sil_type context.tenv qual_type in
(* We need to set the explicit dependency between the newly created block and the *)
(* defining procedure. We add an edge in the call graph.*)
Cg.add_edge context.cg procname block_pname ;
let captured_block_vars = block_decl_info.Clang_ast_t.bdi_captured_variables in
let captureds =
CVar_decl.captured_vars_from_block_info context stmt_info.Clang_ast_t.si_source_range
@ -2618,7 +2609,7 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
let ids_instrs = List.map ~f:assign_captured_var captureds in
let ids, instrs = List.unzip ids_instrs in
let block_data = (context, qual_type, block_pname, captureds) in
F.function_decl context.translation_unit_context context.tenv context.cfg context.cg decl
F.function_decl context.translation_unit_context context.tenv context.cfg decl
(Some block_data) ;
let captured_vars =
List.map2_exn ~f:(fun id (pvar, typ) -> (Exp.Var id, pvar, typ)) ids captureds
@ -2637,9 +2628,6 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
let procname = Procdesc.get_proc_name context.procdesc in
let lambda_pname = CMethod_trans.get_procname_from_cpp_lambda context lei_lambda_decl in
let typ = CType_decl.qual_type_to_sil_type context.tenv qual_type in
(* We need to set the explicit dependency between the newly created lambda and the *)
(* defining procedure. We add an edge in the call graph.*)
Cg.add_edge context.cg procname lambda_pname ;
let make_captured_tuple (pvar, typ) = (Exp.Lvar pvar, pvar, typ) in
let get_captured_pvar_typ decl_ref =
CVar_decl.sil_var_of_captured_var decl_ref context stmt_info.Clang_ast_t.si_source_range

@ -90,9 +90,7 @@ let clean_results_dir () =
ResultsDatabase.database_filename ^ "-shm"
; ResultsDatabase.database_filename ^ "-wal" ]
in
let suffixes_to_delete =
".txt" :: ".csv" :: ".json" :: (if Config.flavors then [] else [".cg"])
in
let suffixes_to_delete = [".txt"; ".csv"; ".json"] in
fun name ->
(* Keep the JSON report *)
not (String.equal (Filename.basename name) Config.report_json)

@ -17,7 +17,7 @@ module NodeTbl = Procdesc.NodeHash
type jump_kind = Next | Jump of int | Exit
(** Translation data *)
type icfg = {tenv: Tenv.t; cg: Cg.t; cfg: Cfg.t}
type icfg = {tenv: Tenv.t; cfg: Cfg.t}
type t =
{ icfg: icfg
@ -42,8 +42,6 @@ let create_context icfg procdesc impl cn source_file program =
; program }
let get_cg context = context.icfg.cg
let get_tenv context = context.icfg.tenv
let set_var_map context var_map = context.var_map <- var_map

@ -22,7 +22,7 @@ module NodeTbl : Caml.Hashtbl.S with type key = Procdesc.Node.t
(** data structure for saving the three structures tht contain the intermediate
representation of a file: the type environment, the control graph and the control
flow graph *)
type icfg = {tenv: Tenv.t; cg: Cg.t; cfg: Cfg.t}
type icfg = {tenv: Tenv.t; cfg: Cfg.t}
(** data structure for storing the context elements. *)
type t = private
@ -43,9 +43,6 @@ val create_context :
val get_tenv : t -> Tenv.t
(** returns the type environment that corresponds to the current file. *)
val get_cg : t -> Cg.t
(** returns the control graph that corresponds to the current file. *)
val add_if_jump : t -> Procdesc.Node.t -> int -> unit
(** adds to the context the line that an if-node will jump to *)

@ -150,14 +150,13 @@ let create_icfg source_file linereader program icfg cn node =
try
(* each procedure has different scope: start names from id 0 *)
Ident.NameGenerator.reset () ;
( match m with
match m with
| Javalib.AbstractMethod am ->
ignore (JTrans.create_am_procdesc source_file program icfg am proc_name)
| Javalib.ConcreteMethod cm when JTrans.is_java_native cm ->
ignore (JTrans.create_native_procdesc source_file program icfg cm proc_name)
| Javalib.ConcreteMethod cm ->
add_cmethod source_file program linereader icfg cm proc_name ) ;
Cg.add_defined_node icfg.JContext.cg proc_name
add_cmethod source_file program linereader icfg cm proc_name
with JBasics.Class_structure_error _ ->
L.internal_error "create_icfg raised JBasics.Class_structure_error on %a@." Typ.Procname.pp
proc_name
@ -192,7 +191,7 @@ let should_capture classes package_opt source_basename node =
In the standard - mode, it translated all the classes that correspond to this
source file. *)
let compute_source_icfg linereader classes program tenv source_basename package_opt source_file =
let icfg = {JContext.cg= Cg.create source_file; JContext.cfg= Cfg.create (); JContext.tenv} in
let icfg = {JContext.cfg= Cfg.create (); tenv} in
let select test procedure cn node =
if test node then try procedure cn node with Bir.Subroutine -> ()
in
@ -203,11 +202,11 @@ let compute_source_icfg linereader classes program tenv source_basename package_
(create_icfg source_file linereader program icfg))
(JClasspath.get_classmap program)
in
(icfg.JContext.cg, icfg.JContext.cfg)
icfg.JContext.cfg
let compute_class_icfg source_file linereader program tenv node =
let icfg = {JContext.cg= Cg.create source_file; JContext.cfg= Cfg.create (); JContext.tenv} in
let icfg = {JContext.cfg= Cfg.create (); tenv} in
( try create_icfg source_file linereader program icfg (Javalib.get_name node) node
with Bir.Subroutine -> () ) ;
(icfg.JContext.cg, icfg.JContext.cfg)
icfg.JContext.cfg

@ -22,10 +22,10 @@ val is_classname_cached : JBasics.class_name -> bool
val compute_source_icfg :
Printer.LineReader.t -> JBasics.ClassSet.t -> JClasspath.program -> Tenv.t -> string
-> string option -> SourceFile.t -> Cg.t * Cfg.t
(** [compute_icfg linereader classes program tenv source_basename source_file] create the call graph and control flow graph for the file [source_file] by translating all the classes in [program] originating from [source_file] *)
-> string option -> SourceFile.t -> Cfg.t
(** [compute_cfg linereader classes program tenv source_basename source_file] create the control flow graph for the file [source_file] by translating all the classes in [program] originating from [source_file] *)
val compute_class_icfg :
SourceFile.t -> Printer.LineReader.t -> JClasspath.program -> Tenv.t
-> JCode.jcode Javalib.interface_or_class -> Cg.t * Cfg.t
-> JCode.jcode Javalib.interface_or_class -> Cfg.t
(** Compute the CFG for a class *)

@ -28,14 +28,10 @@ let init_global_state source_file =
JContext.reset_exn_node_table ()
let store_icfg source_file cg cfg =
let store_icfg source_file cfg =
let source_dir = DB.source_dir_from_source_file source_file in
let cg_file = DB.source_dir_get_internal_file source_dir ".cg" in
Cg.store_to_file cg_file cg ;
Cfg.store source_file cfg ;
if Config.debug_mode || Config.frontend_tests then (
Dotty.print_icfg_dotty source_file cfg ;
Cg.save_call_graph_dotty source_file cg ) ;
if Config.debug_mode || Config.frontend_tests then Dotty.print_icfg_dotty source_file cfg ;
(* NOTE: nothing should be written to source_dir after this *)
DB.mark_file_updated (DB.source_dir_to_string source_dir)
@ -44,11 +40,11 @@ let store_icfg source_file cg cfg =
(* environment are obtained and saved. *)
let do_source_file linereader classes program tenv source_basename package_opt source_file =
L.(debug Capture Medium) "@\nfilename: %a (%s)@." SourceFile.pp source_file source_basename ;
let call_graph, cfg =
let cfg =
JFrontend.compute_source_icfg linereader classes program tenv source_basename package_opt
source_file
in
store_icfg source_file call_graph cfg
store_icfg source_file cfg
let capture_libs linereader program tenv =
@ -61,11 +57,8 @@ let capture_libs linereader program tenv =
| Javalib.JClass _ ->
let fake_source_file = SourceFile.from_abs_path (JFrontend.path_of_cached_classname cn) in
init_global_state fake_source_file ;
let call_graph, cfg =
JFrontend.compute_class_icfg fake_source_file linereader program tenv node
in
store_icfg fake_source_file call_graph cfg ;
JFrontend.cache_classname cn
let cfg = JFrontend.compute_class_icfg fake_source_file linereader program tenv node in
store_icfg fake_source_file cfg ; JFrontend.cache_classname cn
in
JBasics.ClassMap.iter (capture_class tenv) (JClasspath.get_classmap program)

@ -759,7 +759,6 @@ let assume_not_null loc sil_expr =
let instruction (context: JContext.t) pc instr : translation =
let tenv = JContext.get_tenv context in
let cg = JContext.get_cg context in
let program = context.program in
let proc_name = Procdesc.get_proc_name context.procdesc in
let ret_var = Pvar.get_ret_pvar proc_name in
@ -904,8 +903,6 @@ let instruction (context: JContext.t) pc instr : translation =
let instrs = new_instr :: call_instrs @ [set_instr] in
let node_kind = create_node_kind constr_procname in
let node = create_node node_kind instrs in
let caller_procname = Procdesc.get_proc_name context.procdesc in
Cg.add_edge cg caller_procname constr_procname ;
Instr node
| JBir.NewArray (var, vt, expr_list) ->
let builtin_new_array = Exp.Const (Const.Cfun BuiltinDecl.__new_array) in
@ -939,12 +936,9 @@ let instruction (context: JContext.t) pc instr : translation =
in
let node_kind = create_node_kind callee_procname in
let call_node = create_node node_kind (instrs @ call_instrs) in
let caller_procname = Procdesc.get_proc_name context.procdesc in
Cg.add_edge cg caller_procname callee_procname ;
Instr call_node
| JBir.InvokeVirtual (var_opt, obj, call_kind, ms, args)
-> (
let caller_procname = Procdesc.get_proc_name context.procdesc in
let instrs, sil_obj_expr, sil_obj_type = expression context pc obj in
let create_call_node cn invoke_kind =
let callee_procname, call_instrs =
@ -954,7 +948,6 @@ let instruction (context: JContext.t) pc instr : translation =
in
let node_kind = create_node_kind callee_procname in
let call_node = create_node node_kind (instrs @ call_instrs) in
Cg.add_edge cg caller_procname callee_procname ;
call_node
in
let trans_virtual_call original_cn invoke_kind =
@ -988,9 +981,6 @@ let instruction (context: JContext.t) pc instr : translation =
in
let node_kind = create_node_kind callee_procname in
let call_node = create_node node_kind (instrs @ call_instrs) in
let procdesc = context.procdesc in
let caller_procname = Procdesc.get_proc_name procdesc in
Cg.add_edge cg caller_procname callee_procname ;
Instr call_node
| JBir.Check JBir.CheckNullPointer expr when Config.tracing && is_this expr ->
(* TODO #6509339: refactor the boilerplate code in the translation of JVM checks *)

Loading…
Cancel
Save