diff --git a/infer/src/IR/Attributes.ml b/infer/src/IR/Attributes.ml index 0a15fc17f..c17dbd14d 100644 --- a/infer/src/IR/Attributes.ml +++ b/infer/src/IR/Attributes.ml @@ -52,7 +52,7 @@ let replace_statement = ResultsDatabase.register_statement {| INSERT OR REPLACE INTO procedures -SELECT :pname, :proc_name_hum, :akind, :sfile, :pattr +SELECT :pname, :proc_name_hum, :akind, :sfile, :pattr, :cfg FROM ( SELECT NULL FROM ( @@ -64,7 +64,7 @@ FROM ( OR (attr_kind = :akind AND source_file < :sfile) )|} -let replace pname pname_blob akind loc_file attr_blob = +let replace pname pname_blob akind loc_file attr_blob proc_desc = ResultsDatabase.with_registered_statement replace_statement ~f:(fun db replace_stmt -> Sqlite3.bind replace_stmt 1 (* :pname *) pname_blob |> SqliteUtils.check_result_code db ~log:"replace bind pname" ; @@ -77,6 +77,8 @@ let replace pname pname_blob akind loc_file attr_blob = |> SqliteUtils.check_result_code db ~log:"replace bind source file" ; Sqlite3.bind replace_stmt 5 (* :pattr *) attr_blob |> SqliteUtils.check_result_code db ~log:"replace bind proc attributes" ; + Sqlite3.bind replace_stmt 6 (* :cfg *) (Procdesc.SQLite.serialize proc_desc) + |> SqliteUtils.check_result_code db ~log:"replace bind cfg" ; SqliteUtils.result_unit db ~finalize:false ~log:"Attributes.replace" replace_stmt ) @@ -124,13 +126,14 @@ let find ~defined pname_blob = let load pname = Typ.Procname.SQLite.serialize pname |> find ~defined:false -let store (attr : ProcAttributes.t) = +let store ~proc_desc (attr : ProcAttributes.t) = let pkind = proc_kind_of_attr attr in let key = Typ.Procname.SQLite.serialize attr.proc_name in if should_try_to_update key pkind then replace attr.proc_name key pkind (SourceFile.SQLite.serialize attr.loc.Location.file) (ProcAttributes.SQLite.serialize attr) + proc_desc let load_defined pname = Typ.Procname.SQLite.serialize pname |> find ~defined:true diff --git a/infer/src/IR/Attributes.mli b/infer/src/IR/Attributes.mli index b26c33350..9c93073b4 100644 --- a/infer/src/IR/Attributes.mli +++ b/infer/src/IR/Attributes.mli @@ -13,7 +13,7 @@ type attributes_kind val deserialize_attributes_kind : Sqlite3.Data.t -> attributes_kind -val store : ProcAttributes.t -> unit +val store : proc_desc:Procdesc.t option -> ProcAttributes.t -> unit (** Save .attr file for the procedure into the attributes database. *) val load : Typ.Procname.t -> ProcAttributes.t option diff --git a/infer/src/IR/Cfg.ml b/infer/src/IR/Cfg.ml index 9f9718bd2..3a9a50d60 100644 --- a/infer/src/IR/Cfg.ml +++ b/infer/src/IR/Cfg.ml @@ -67,16 +67,16 @@ let load source = |> Option.map ~f:SQLite.deserialize ) -let save_attributes source_file cfg = - let save_proc _ pdesc = - let attributes = Procdesc.get_attributes pdesc in +let store source_file cfg = + let save_proc _ proc_desc = + let attributes = Procdesc.get_attributes proc_desc in let loc = attributes.loc in let attributes' = let loc' = if Location.equal loc Location.dummy then {loc with file= source_file} else loc in {attributes with loc= loc'; translation_unit= source_file} in - Attributes.store attributes' ; - Procdesc.set_attributes pdesc attributes' + Procdesc.set_attributes proc_desc attributes' ; + Attributes.store ~proc_desc:(Some proc_desc) attributes' in Typ.Procname.Hash.iter save_proc cfg diff --git a/infer/src/IR/Cfg.mli b/infer/src/IR/Cfg.mli index a837b5e88..f0cfc03e6 100644 --- a/infer/src/IR/Cfg.mli +++ b/infer/src/IR/Cfg.mli @@ -19,6 +19,10 @@ val load : SourceFile.t -> t option val get_all_defined_proc_names : t -> Typ.Procname.t list (** get all the procedure names that are defined in the current file *) +val store : SourceFile.t -> t -> unit +(** Save the individual [Procdesc.t] and [ProcAttributes.t] to the database for the procedures in + the cfg. *) + (** {2 Functions for manipulating an interprocedural CFG} *) val create : unit -> t @@ -30,9 +34,6 @@ val create_proc_desc : t -> ProcAttributes.t -> Procdesc.t val iter_all_nodes : sorted:bool -> t -> f:(Procdesc.t -> Procdesc.Node.t -> unit) -> unit (** Iterate over all the nodes in the cfg *) -val save_attributes : SourceFile.t -> t -> unit -(** Save the .attr files for the procedures in the cfg. *) - val inline_java_synthetic_methods : t -> unit (** Inline the java synthetic methods in the cfg (in-place) *) diff --git a/infer/src/IR/Procdesc.ml b/infer/src/IR/Procdesc.ml index dbadb8cfb..62c80d813 100644 --- a/infer/src/IR/Procdesc.ml +++ b/infer/src/IR/Procdesc.ml @@ -750,3 +750,8 @@ let is_connected proc_desc = Ok () | Error _ as error -> error + + +module SQLite = SqliteUtils.MarshalledNullableData (struct + type nonrec t = t +end) diff --git a/infer/src/IR/Procdesc.mli b/infer/src/IR/Procdesc.mli index 2e6a1bea7..1b9ec82b1 100644 --- a/infer/src/IR/Procdesc.mli +++ b/infer/src/IR/Procdesc.mli @@ -8,6 +8,8 @@ open! IStd +(** {1 Per-procedure CFG} *) + module NodeKey : sig type t @@ -285,3 +287,7 @@ val has_modify_in_block_attr : t -> Pvar.t -> bool val is_connected : t -> (unit, [`Join | `Other]) Result.t (** checks whether a cfg for the given procdesc is connected or not *) + +(** per-procedure CFGs are stored in the SQLite "procedures" table as NULL if the procedure has no + CFG *) +module SQLite : SqliteUtils.Data with type t = t option diff --git a/infer/src/IR/SourceFiles.ml b/infer/src/IR/SourceFiles.ml index c1dcac549..fd97ba4df 100644 --- a/infer/src/IR/SourceFiles.ml +++ b/infer/src/IR/SourceFiles.ml @@ -53,7 +53,7 @@ let add source_file cfg tenv = OndemandCapture module relies on it - it uses existance of the cfg as a barrier to make sure that all attributes were written to disk (but not necessarily flushed) *) SqliteUtils.with_transaction (ResultsDatabase.get_database ()) ~f:(fun () -> - Cfg.save_attributes source_file cfg ) ; + Cfg.store source_file cfg ) ; ResultsDatabase.with_registered_statement store_statement ~f:(fun db store_stmt -> SourceFile.SQLite.serialize source_file |> Sqlite3.bind store_stmt 1 diff --git a/infer/src/IR/SpecializeProcdesc.ml b/infer/src/IR/SpecializeProcdesc.ml index 73c4b5385..7f5804c26 100644 --- a/infer/src/IR/SpecializeProcdesc.ml +++ b/infer/src/IR/SpecializeProcdesc.ml @@ -172,7 +172,7 @@ let with_formals_types ?(has_clang_model = false) callee_pdesc resolved_pname ar ; is_specialized= true ; translation_unit } in - Attributes.store resolved_attributes ; + Attributes.store ~proc_desc:None resolved_attributes ; let resolved_pdesc = Procdesc.from_proc_attributes resolved_attributes in with_formals_types_proc callee_pdesc resolved_pdesc substitutions @@ -339,5 +339,5 @@ let with_block_args callee_pdesc pname_with_block_args block_args = convert_cfg ~callee_pdesc ~resolved_pdesc ~f_instr_list:(with_block_args_instrs resolved_pdesc substitutions) in - Attributes.store resolved_attributes ; + Attributes.store ~proc_desc:(Some proc_desc) resolved_attributes ; proc_desc diff --git a/infer/src/base/MergeResults.ml b/infer/src/base/MergeResults.ml index d5b0c6e34..65fe25435 100644 --- a/infer/src/base/MergeResults.ml +++ b/infer/src/base/MergeResults.ml @@ -16,7 +16,7 @@ let merge_procedures_table ~db_file = Sqlite3.exec db {| INSERT OR REPLACE INTO procedures -SELECT sub.proc_name, sub.proc_name_hum, sub.attr_kind, sub.source_file, sub.proc_attributes +SELECT sub.proc_name, sub.proc_name_hum, sub.attr_kind, sub.source_file, sub.proc_attributes, sub.cfg FROM ( attached.procedures AS sub LEFT OUTER JOIN procedures AS main diff --git a/infer/src/base/ResultsDatabase.ml b/infer/src/base/ResultsDatabase.ml index a02e88aa1..b14ced37e 100644 --- a/infer/src/base/ResultsDatabase.ml +++ b/infer/src/base/ResultsDatabase.ml @@ -18,7 +18,9 @@ let procedures_schema = , proc_name_hum TEXT , attr_kind INTEGER NOT NULL , source_file TEXT NOT NULL - , proc_attributes BLOB NOT NULL )|} + , proc_attributes BLOB NOT NULL + , cfg BLOB + )|} let source_files_schema = diff --git a/infer/src/base/SqliteUtils.ml b/infer/src/base/SqliteUtils.ml index 81926b1a6..1d5ce15e9 100644 --- a/infer/src/base/SqliteUtils.ml +++ b/infer/src/base/SqliteUtils.ml @@ -119,3 +119,23 @@ struct let serialize x = Sqlite3.Data.BLOB (Marshal.to_string x []) end + +module MarshalledNullableData (D : sig + type t +end) = +struct + type t = D.t option + + let deserialize = function[@warning "-8"] + | Sqlite3.Data.BLOB b -> + Some (Marshal.from_string b 0) + | Sqlite3.Data.NULL -> + None + + + let serialize = function + | None -> + Sqlite3.Data.NULL + | Some x -> + Sqlite3.Data.BLOB (Marshal.to_string x []) +end diff --git a/infer/src/base/SqliteUtils.mli b/infer/src/base/SqliteUtils.mli index 251c4bf66..4efcbe238 100644 --- a/infer/src/base/SqliteUtils.mli +++ b/infer/src/base/SqliteUtils.mli @@ -76,3 +76,8 @@ end module MarshalledData (D : sig type t end) : Data with type t = D.t + +(** A default implementation of the Data API that encodes None as a NULL SQLite value *) +module MarshalledNullableData (D : sig + type t +end) : Data with type t = D.t option diff --git a/infer/src/java/jMain.ml b/infer/src/java/jMain.ml index c53333edc..e72f6b692 100644 --- a/infer/src/java/jMain.ml +++ b/infer/src/java/jMain.ml @@ -73,7 +73,9 @@ let save_tenv tenv = let store_callee_attributes tenv program = let f proc_name cn ms = - Option.iter ~f:Attributes.store (JTrans.create_callee_attributes tenv program cn ms proc_name) + Option.iter + ~f:(Attributes.store ~proc_desc:None) + (JTrans.create_callee_attributes tenv program cn ms proc_name) in JClasspath.iter_missing_callees program ~f