<commit_before>(*
 * 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 =<commit_msg>[nullable] make the Eradicate flag use_models to be the default mode<commit_after>(*
 * 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. *)

(** 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 =
      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
  in
  let lookup_models_present ann_sig =
      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
  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 =
    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


(** 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 =
{'fraction_non_alphanumeric': 0.0627342495091915, 'fraction_numerical': 0.001784758165268606, 'mean_word_length': 3.3897375636506073, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6788872', 'n_tokens_mistral': 3674, 'n_tokens_neox': 3530, 'n_words': 1162}