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.
145 lines
5.1 KiB
145 lines
5.1 KiB
(*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*)
|
|
|
|
open! IStd
|
|
|
|
type init = Procname.t * Procdesc.t
|
|
|
|
let equal_class_opt = [%compare.equal: string option]
|
|
|
|
let final_typestates initializers_current_class tenv typecheck_proc =
|
|
(* Get the private methods, from the same class, directly called by the initializers. *)
|
|
let get_private_called (initializers : init list) : init list =
|
|
let res = ref [] in
|
|
let do_proc (init_pn, init_pd) =
|
|
let filter callee_pn callee_attributes =
|
|
let is_private =
|
|
PredSymb.equal_access callee_attributes.ProcAttributes.access PredSymb.Private
|
|
in
|
|
let same_class =
|
|
let get_class_opt pn =
|
|
match pn with
|
|
| Procname.Java pn_java ->
|
|
Some (Procname.Java.get_class_name pn_java)
|
|
| _ ->
|
|
None
|
|
in
|
|
equal_class_opt (get_class_opt init_pn) (get_class_opt callee_pn)
|
|
in
|
|
is_private && same_class
|
|
in
|
|
let private_called =
|
|
PatternMatch.proc_calls (PatternMatch.lookup_attributes tenv) init_pd filter
|
|
in
|
|
let do_called (callee_pn, _) =
|
|
match Procdesc.load callee_pn with
|
|
| Some callee_pd ->
|
|
res := (callee_pn, callee_pd) :: !res
|
|
| None ->
|
|
()
|
|
in
|
|
List.iter ~f:do_called private_called
|
|
in
|
|
List.iter ~f:do_proc initializers ;
|
|
!res
|
|
in
|
|
(* Get the initializers recursively called by computing a fixpoint.
|
|
Start from the initializers of the current class and the current procedure. *)
|
|
let initializers_recursive : init list =
|
|
let initializers_base_case = initializers_current_class in
|
|
let res = ref [] in
|
|
let seen = ref Procname.Set.empty in
|
|
let mark_seen (initializers : init list) : unit =
|
|
List.iter ~f:(fun (pn, _) -> seen := Procname.Set.add pn !seen) initializers ;
|
|
res := !res @ initializers
|
|
in
|
|
let rec fixpoint initializers_old =
|
|
let initializers_new = get_private_called initializers_old in
|
|
let initializers_new' =
|
|
List.filter ~f:(fun (pn, _) -> not (Procname.Set.mem pn !seen)) initializers_new
|
|
in
|
|
mark_seen initializers_new' ;
|
|
if not (List.is_empty initializers_new') then fixpoint initializers_new'
|
|
in
|
|
mark_seen initializers_base_case ;
|
|
fixpoint initializers_base_case ;
|
|
!res
|
|
in
|
|
(* Get the final typestates of all the initializers. *)
|
|
let final_typestates = ref [] in
|
|
let get_final_typestate (pname, pdesc) =
|
|
match typecheck_proc false pname pdesc None with
|
|
| _, Some final_typestate ->
|
|
final_typestates := (pname, final_typestate) :: !final_typestates
|
|
| _, None ->
|
|
()
|
|
in
|
|
List.iter ~f:get_final_typestate initializers_recursive ;
|
|
List.rev !final_typestates
|
|
|
|
|
|
let pname_and_pdescs_with tenv curr_pname f =
|
|
let res = ref [] in
|
|
let filter pname =
|
|
match PatternMatch.lookup_attributes tenv pname with
|
|
| Some proc_attributes ->
|
|
f (pname, proc_attributes)
|
|
| None ->
|
|
false
|
|
in
|
|
let do_proc pname =
|
|
if filter pname then
|
|
match Procdesc.load pname with Some pdesc -> res := (pname, pdesc) :: !res | None -> ()
|
|
in
|
|
List.iter ~f:do_proc (SourceFiles.get_procs_in_file curr_pname) ;
|
|
List.rev !res
|
|
|
|
|
|
let get_class pn =
|
|
match pn with Procname.Java pn_java -> Some (Procname.Java.get_class_name pn_java) | _ -> None
|
|
|
|
|
|
let is_annotated_initializer tenv proc_name =
|
|
PatternMatch.lookup_attributes tenv proc_name
|
|
|> Option.value_map
|
|
~f:(fun ProcAttributes.{method_annotation= {return}} -> Annotations.ia_is_initializer return)
|
|
~default:false
|
|
|
|
|
|
let is_annotated_initializer_in_chain tenv proc_name =
|
|
PatternMatch.override_exists (is_annotated_initializer tenv) tenv proc_name
|
|
|
|
|
|
(* Should the (non-constructor) function be considered an initializer method *)
|
|
let is_initializer tenv proc_attributes =
|
|
(* Either modelled as initializer or the descendent of such a method *)
|
|
PatternMatch.Java.method_is_initializer tenv proc_attributes
|
|
|| (* Or explicitly marked @Initializer or the descendent of such a method *)
|
|
is_annotated_initializer_in_chain tenv proc_attributes.ProcAttributes.proc_name
|
|
|
|
|
|
(** Typestates after the current procedure and all initializer procedures. *)
|
|
let final_initializer_typestates_lazy tenv curr_pname curr_pdesc typecheck_proc =
|
|
lazy
|
|
(let initializers_current_class =
|
|
pname_and_pdescs_with tenv curr_pname (function pname, proc_attributes ->
|
|
is_initializer tenv proc_attributes
|
|
&& equal_class_opt (get_class pname) (get_class curr_pname) )
|
|
in
|
|
final_typestates ((curr_pname, curr_pdesc) :: initializers_current_class) tenv typecheck_proc
|
|
)
|
|
|
|
|
|
(** Typestates after all constructors. *)
|
|
let final_constructor_typestates_lazy tenv curr_pname typecheck_proc =
|
|
lazy
|
|
(let constructors_current_class =
|
|
pname_and_pdescs_with tenv curr_pname (fun (pname, _) ->
|
|
Procname.is_constructor pname && equal_class_opt (get_class pname) (get_class curr_pname) )
|
|
in
|
|
final_typestates constructors_current_class tenv typecheck_proc )
|