You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
6.8 KiB
211 lines
6.8 KiB
(*
|
|
* Copyright (c) 2013-present, Facebook, Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*)
|
|
|
|
open! IStd
|
|
module Hashtbl = Caml.Hashtbl
|
|
open ModelTables
|
|
module L = Logging
|
|
|
|
(** Module for standard library models. *)
|
|
|
|
(* use model annotations for library functions *)
|
|
let use_models = true
|
|
|
|
(** Module for inference of parameter and return annotations. *)
|
|
module Inference = struct
|
|
let enabled = false
|
|
|
|
let get_dir () = Filename.concat Config.results_dir "eradicate"
|
|
|
|
let field_get_dir_fname fn =
|
|
let fname = Typ.Fieldname.to_string fn in
|
|
(get_dir (), fname)
|
|
|
|
|
|
let field_is_marked fn =
|
|
let dir, fname = field_get_dir_fname fn in
|
|
DB.read_file_with_lock dir fname <> None
|
|
|
|
|
|
let proc_get_ret_dir_fname pname =
|
|
let fname = Typ.Procname.to_filename pname ^ "_ret" in
|
|
(get_dir (), fname)
|
|
|
|
|
|
let proc_get_param_dir_fname pname =
|
|
let fname = Typ.Procname.to_filename pname ^ "_params" in
|
|
(get_dir (), fname)
|
|
|
|
|
|
let update_count_str s_old =
|
|
let n =
|
|
if String.is_empty s_old then 0
|
|
else try int_of_string s_old with Failure _ -> L.die InternalError "int_of_string %s" s_old
|
|
in
|
|
string_of_int (n + 1)
|
|
|
|
|
|
let update_boolvec_str s_ size index bval =
|
|
let s = if String.is_empty s_ then Bytes.make size '0' else Bytes.of_string s_ in
|
|
Bytes.set s index (if bval then '1' else '0') ;
|
|
Bytes.to_string s
|
|
|
|
|
|
let mark_file update_str dir fname =
|
|
DB.update_file_with_lock dir fname update_str ;
|
|
match DB.read_file_with_lock dir fname with
|
|
| Some buf ->
|
|
L.internal_error "Read %s: %s@." fname buf
|
|
| None ->
|
|
L.internal_error "Read %s: None@." fname
|
|
|
|
|
|
let mark_file_count = mark_file update_count_str
|
|
|
|
(** Mark the field @Nullable indirectly by writing to a global file. *)
|
|
let field_add_nullable_annotation fn =
|
|
let dir, fname = field_get_dir_fname fn in
|
|
mark_file_count dir fname
|
|
|
|
|
|
(** Mark the return type @Nullable indirectly by writing to a global file. *)
|
|
let proc_mark_return_nullable pn =
|
|
let dir, fname = proc_get_ret_dir_fname pn in
|
|
mark_file_count dir fname
|
|
|
|
|
|
(** Return true if the return type is marked @Nullable in the global file *)
|
|
let proc_return_is_marked pname =
|
|
let dir, fname = proc_get_ret_dir_fname pname in
|
|
DB.read_file_with_lock dir fname <> None
|
|
|
|
|
|
(** Mark the n-th parameter @Nullable indirectly by writing to a global file. *)
|
|
let proc_add_parameter_nullable pn n tot =
|
|
let dir, fname = proc_get_param_dir_fname pn in
|
|
let update_str s = update_boolvec_str s tot n true in
|
|
mark_file update_str dir fname
|
|
|
|
|
|
(** Return None if the parameters are not marked, or a vector of marked parameters *)
|
|
let proc_parameters_marked pn =
|
|
let dir, fname = proc_get_param_dir_fname pn in
|
|
match DB.read_file_with_lock dir fname with
|
|
| None ->
|
|
None
|
|
| Some buf ->
|
|
let boolvec = ref [] in
|
|
String.iter ~f:(fun c -> boolvec := Char.equal c '1' :: !boolvec) buf ;
|
|
Some (List.rev !boolvec)
|
|
end
|
|
|
|
(* Inference *)
|
|
|
|
let match_method_name pn name =
|
|
match pn with
|
|
| Typ.Procname.Java pn_java ->
|
|
String.equal (Typ.Procname.Java.get_method pn_java) name
|
|
| _ ->
|
|
false
|
|
|
|
|
|
let table_has_procedure table proc_name =
|
|
let proc_id = Typ.Procname.to_unique_id proc_name in
|
|
try
|
|
ignore (Hashtbl.find table proc_id) ;
|
|
true
|
|
with Caml.Not_found -> false
|
|
|
|
|
|
(** Return the annotated signature of the procedure, taking into account models. *)
|
|
let get_modelled_annotated_signature proc_attributes =
|
|
let proc_name = proc_attributes.ProcAttributes.proc_name in
|
|
let annotated_signature = AnnotatedSignature.get proc_attributes in
|
|
let proc_id = Typ.Procname.to_unique_id proc_name in
|
|
let infer_parameters ann_sig =
|
|
let mark_par =
|
|
if Inference.enabled then Inference.proc_parameters_marked proc_name else None
|
|
in
|
|
match mark_par with
|
|
| None ->
|
|
ann_sig
|
|
| Some bs ->
|
|
let mark = (false, bs) in
|
|
AnnotatedSignature.mark proc_name AnnotatedSignature.Nullable ann_sig mark
|
|
in
|
|
let infer_return ann_sig =
|
|
let mark_r = Inference.enabled && Inference.proc_return_is_marked proc_name in
|
|
if mark_r then AnnotatedSignature.mark_return AnnotatedSignature.Nullable ann_sig else ann_sig
|
|
in
|
|
let lookup_models_nullable ann_sig =
|
|
if use_models then
|
|
try
|
|
let mark = Hashtbl.find annotated_table_nullable proc_id in
|
|
AnnotatedSignature.mark proc_name AnnotatedSignature.Nullable ann_sig mark
|
|
with Caml.Not_found -> ann_sig
|
|
else ann_sig
|
|
in
|
|
let lookup_models_present ann_sig =
|
|
if use_models then
|
|
try
|
|
let mark = Hashtbl.find annotated_table_present proc_id in
|
|
AnnotatedSignature.mark proc_name AnnotatedSignature.Present ann_sig mark
|
|
with Caml.Not_found -> ann_sig
|
|
else ann_sig
|
|
in
|
|
annotated_signature |> lookup_models_nullable |> lookup_models_present |> infer_return
|
|
|> infer_parameters
|
|
|
|
|
|
(** Return true when the procedure has been modelled for nullable. *)
|
|
let is_modelled_nullable proc_name =
|
|
if use_models then
|
|
let proc_id = Typ.Procname.to_unique_id proc_name in
|
|
try
|
|
ignore (Hashtbl.find annotated_table_nullable proc_id) ;
|
|
true
|
|
with Caml.Not_found -> false
|
|
else false
|
|
|
|
|
|
(** Check if the procedure is one of the known Preconditions.checkNotNull. *)
|
|
let is_check_not_null proc_name =
|
|
table_has_procedure check_not_null_table proc_name || match_method_name proc_name "checkNotNull"
|
|
|
|
|
|
(** Parameter number for a procedure known to be a checkNotNull *)
|
|
let get_check_not_null_parameter proc_name =
|
|
let proc_id = Typ.Procname.to_unique_id proc_name in
|
|
try Hashtbl.find check_not_null_parameter_table proc_id with Caml.Not_found ->
|
|
(* Assume the check is on the first parameter unless modeled otherwise *)
|
|
1
|
|
|
|
|
|
(** Check if the procedure is one of the known Preconditions.checkState. *)
|
|
let is_check_state proc_name = table_has_procedure check_state_table proc_name
|
|
|
|
(** Check if the procedure is one of the known Preconditions.checkArgument. *)
|
|
let is_check_argument proc_name = table_has_procedure check_argument_table proc_name
|
|
|
|
(** Check if the procedure does not return. *)
|
|
let is_noreturn proc_name = table_has_procedure noreturn_table proc_name
|
|
|
|
(** Check if the procedure is Optional.get(). *)
|
|
let is_optional_get proc_name = table_has_procedure optional_get_table proc_name
|
|
|
|
(** Check if the procedure is Optional.isPresent(). *)
|
|
let is_optional_isPresent proc_name = table_has_procedure optional_isPresent_table proc_name
|
|
|
|
(** Check if the procedure returns true on null. *)
|
|
let is_true_on_null proc_name = table_has_procedure true_on_null_table proc_name
|
|
|
|
(** Check if the procedure is Map.containsKey(). *)
|
|
let is_containsKey proc_name = table_has_procedure containsKey_table proc_name
|
|
|
|
(** Check if the procedure is Map.put(). *)
|
|
let is_mapPut proc_name = table_has_procedure mapPut_table proc_name
|