Summary: No reason for this to be in Typ Reviewed By: skcho Differential Revision: D19162727 fbshipit-source-id: d6940637amaster
parent
33352623a5
commit
91fa6a5404
@ -0,0 +1,810 @@
|
||||
(*
|
||||
* 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
|
||||
module Hashtbl = Caml.Hashtbl
|
||||
module F = Format
|
||||
|
||||
(** Level of verbosity of some to_string functions. *)
|
||||
type detail_level = Verbose | Non_verbose | Simple [@@deriving compare, equal]
|
||||
|
||||
let is_verbose v = match v with Verbose -> true | _ -> false
|
||||
|
||||
module Java = struct
|
||||
type kind =
|
||||
| Non_Static
|
||||
(** in Java, procedures called with invokevirtual, invokespecial, and invokeinterface *)
|
||||
| Static (** in Java, procedures called with invokestatic *)
|
||||
[@@deriving compare]
|
||||
|
||||
(* TODO: use Mangled.t here *)
|
||||
type java_type = Typ.Name.Java.Split.t [@@deriving compare]
|
||||
|
||||
let java_void = Typ.Name.Java.Split.make "void"
|
||||
|
||||
(** Type of java procedure names. *)
|
||||
type t =
|
||||
{ method_name: string
|
||||
; parameters: java_type list
|
||||
; class_name: Typ.Name.t
|
||||
; return_type: java_type option (* option because constructors have no return type *)
|
||||
; kind: kind }
|
||||
[@@deriving compare]
|
||||
|
||||
let make class_name return_type method_name parameters kind =
|
||||
{class_name; return_type; method_name; parameters; kind}
|
||||
|
||||
|
||||
let pp_type_verbosity verbosity fmt java_type =
|
||||
Typ.Name.Java.Split.pp_type_verbosity ~verbose:(is_verbose verbosity) fmt java_type
|
||||
|
||||
|
||||
(** Given a list of types, it creates a unique string of types separated by commas *)
|
||||
let rec pp_param_list verbosity fmt inputList =
|
||||
match inputList with
|
||||
| [] ->
|
||||
()
|
||||
| [head] ->
|
||||
pp_type_verbosity verbosity fmt head
|
||||
| head :: rest ->
|
||||
pp_type_verbosity verbosity fmt head ;
|
||||
F.pp_print_string fmt "," ;
|
||||
pp_param_list verbosity fmt rest
|
||||
|
||||
|
||||
let java_type_of_name class_name = Typ.Name.Java.Split.of_string (Typ.Name.name class_name)
|
||||
|
||||
(** It is the same as java_type_to_string_verbosity, but Java return types are optional because of
|
||||
constructors without type *)
|
||||
let pp_return_type verbosity fmt j =
|
||||
match j.return_type with None -> () | Some typ -> pp_type_verbosity verbosity fmt typ
|
||||
|
||||
|
||||
let get_class_name j = Typ.Name.name j.class_name
|
||||
|
||||
let get_class_type_name j = j.class_name
|
||||
|
||||
let get_simple_class_name j = Typ.Name.Java.Split.(j |> get_class_name |> of_string |> type_name)
|
||||
|
||||
let get_package j = Typ.Name.Java.Split.(j |> get_class_name |> of_string |> package)
|
||||
|
||||
let get_method j = j.method_name
|
||||
|
||||
let replace_method_name method_name j = {j with method_name}
|
||||
|
||||
let replace_parameters parameters j = {j with parameters}
|
||||
|
||||
let replace_return_type ret_type j = {j with return_type= Some ret_type}
|
||||
|
||||
let get_parameters j = j.parameters
|
||||
|
||||
(** Prints a string of a java procname with the given level of verbosity *)
|
||||
let pp ?(withclass = false) verbosity fmt j =
|
||||
match verbosity with
|
||||
| Verbose | Non_verbose ->
|
||||
(* if verbose, then package.class.method(params): rtype,
|
||||
else rtype package.class.method(params)
|
||||
verbose is used for example to create unique filenames, non_verbose to create reports *)
|
||||
let pp_class_name verbosity fmt j =
|
||||
pp_type_verbosity verbosity fmt (Typ.Name.Java.split_typename j.class_name)
|
||||
in
|
||||
let separator =
|
||||
match (j.return_type, verbosity) with None, _ -> "" | Some _, Verbose -> ":" | _ -> " "
|
||||
in
|
||||
if not (equal_detail_level verbosity Verbose) then
|
||||
F.fprintf fmt "%a%s" (pp_return_type verbosity) j separator ;
|
||||
F.fprintf fmt "%a.%s(%a)" (pp_class_name verbosity) j j.method_name
|
||||
(pp_param_list verbosity) j.parameters ;
|
||||
if equal_detail_level verbosity Verbose then
|
||||
F.fprintf fmt "%s%a" separator (pp_return_type verbosity) j
|
||||
| Simple ->
|
||||
(* methodname(...) or without ... if there are no parameters *)
|
||||
let pp_class_prefix ~withclass verbosity fmt j =
|
||||
if withclass then
|
||||
F.fprintf fmt "%a." (pp_type_verbosity verbosity)
|
||||
(Typ.Name.Java.split_typename j.class_name)
|
||||
in
|
||||
let params = match j.parameters with [] -> "" | _ -> "..." in
|
||||
let pp_method_name ~withclass verbosity fmt j =
|
||||
if String.equal j.method_name "<init>" then
|
||||
F.pp_print_string fmt (get_simple_class_name j)
|
||||
else F.fprintf fmt "%a%s" (pp_class_prefix ~withclass verbosity) j j.method_name
|
||||
in
|
||||
F.fprintf fmt "%a(%s)" (pp_method_name ~withclass verbosity) j params
|
||||
|
||||
|
||||
let get_return_typ pname_java =
|
||||
let rec java_from_string = function
|
||||
| "" | "void" ->
|
||||
Typ.void
|
||||
| "int" ->
|
||||
Typ.int
|
||||
| "byte" ->
|
||||
Typ.java_byte
|
||||
| "short" ->
|
||||
Typ.java_short
|
||||
| "boolean" ->
|
||||
Typ.boolean
|
||||
| "char" ->
|
||||
Typ.char
|
||||
| "long" ->
|
||||
Typ.long
|
||||
| "float" ->
|
||||
Typ.float
|
||||
| "double" ->
|
||||
Typ.double
|
||||
| typ_str when String.contains typ_str '[' ->
|
||||
let stripped_typ = String.sub typ_str ~pos:0 ~len:(String.length typ_str - 2) in
|
||||
Typ.mk (Tptr (Typ.mk_array (java_from_string stripped_typ), Pk_pointer))
|
||||
| typ_str ->
|
||||
Typ.mk (Tstruct (Typ.Name.Java.from_string typ_str))
|
||||
in
|
||||
let typ = java_from_string (F.asprintf "%a" (pp_return_type Verbose) pname_java) in
|
||||
match typ.desc with Tstruct _ -> Typ.mk (Tptr (typ, Pk_pointer)) | _ -> typ
|
||||
|
||||
|
||||
let is_close {method_name} = String.equal method_name "close"
|
||||
|
||||
let constructor_method_name = "<init>"
|
||||
|
||||
let class_initializer_method_name = "<clinit>"
|
||||
|
||||
let is_class_initializer {method_name} = String.equal method_name class_initializer_method_name
|
||||
|
||||
let get_class_initializer class_name =
|
||||
{ method_name= class_initializer_method_name
|
||||
; parameters= []
|
||||
; class_name
|
||||
; return_type= Some java_void
|
||||
; kind= Static }
|
||||
|
||||
|
||||
let is_constructor {method_name} = String.equal method_name constructor_method_name
|
||||
|
||||
let is_anonymous_inner_class_constructor {class_name} =
|
||||
Typ.Name.Java.is_anonymous_inner_class_name class_name
|
||||
|
||||
|
||||
let is_static {kind} = match kind with Static -> true | _ -> false
|
||||
|
||||
let is_lambda {method_name} = String.is_prefix ~prefix:"lambda$" method_name
|
||||
|
||||
let is_generated {method_name} = String.is_prefix ~prefix:"$" method_name
|
||||
|
||||
let is_access_method {method_name} =
|
||||
match String.rsplit2 method_name ~on:'$' with
|
||||
| Some ("access", s) ->
|
||||
let is_int =
|
||||
try
|
||||
ignore (int_of_string s) ;
|
||||
true
|
||||
with Failure _ -> false
|
||||
in
|
||||
is_int
|
||||
| _ ->
|
||||
false
|
||||
|
||||
|
||||
let is_autogen_method {method_name} = String.contains method_name '$'
|
||||
|
||||
(** Check if the proc name has the type of a java vararg. Note: currently only checks that the
|
||||
last argument has type Object[]. *)
|
||||
let is_vararg {parameters} =
|
||||
(* FIXME this looks wrong due to the dot in the type name *)
|
||||
List.last parameters
|
||||
|> Option.exists ~f:(fun java_type ->
|
||||
String.equal "java.lang.Object[]" (Typ.Name.Java.Split.type_name java_type) )
|
||||
|
||||
|
||||
let is_external java_pname =
|
||||
let package = get_package java_pname in
|
||||
Option.exists ~f:Config.java_package_is_external package
|
||||
end
|
||||
|
||||
module Parameter = struct
|
||||
(** Type for parameters in clang procnames, [Some name] means the parameter is of type pointer to
|
||||
struct, with [name] being the name of the struct, [None] means the parameter is of some other
|
||||
type. *)
|
||||
type clang_parameter = Typ.Name.t option [@@deriving compare]
|
||||
|
||||
(** Type for parameters in procnames, for java and clang. *)
|
||||
type t = JavaParameter of Java.java_type | ClangParameter of clang_parameter
|
||||
[@@deriving compare]
|
||||
|
||||
let of_typ typ =
|
||||
match typ.Typ.desc with Typ.Tptr ({desc= Tstruct name}, Pk_pointer) -> Some name | _ -> None
|
||||
|
||||
|
||||
let pp_parameters fmt parameters =
|
||||
if List.exists ~f:Option.is_some parameters then
|
||||
(* the tests rely on the fact that we discard non-pointer-to-struct types for some reason,
|
||||
hence the slight re-implementation of [Pp.seq] to avoid building the list of [Some] items
|
||||
explicitly *)
|
||||
let rec pp_parameters_aux fmt = function
|
||||
| [] ->
|
||||
()
|
||||
| [Some param] ->
|
||||
F.pp_print_string fmt (Typ.Name.to_string param)
|
||||
| None :: parameters ->
|
||||
pp_parameters_aux fmt parameters
|
||||
| (Some _ as param_some) :: None :: parameters ->
|
||||
pp_parameters_aux fmt (param_some :: parameters)
|
||||
| Some param :: (Some _ :: _ as parameters) ->
|
||||
F.fprintf fmt "%s," (Typ.Name.to_string param) ;
|
||||
pp_parameters_aux fmt parameters
|
||||
in
|
||||
F.fprintf fmt "(%a)" pp_parameters_aux parameters
|
||||
|
||||
|
||||
let clang_param_of_name class_name : clang_parameter = Some class_name
|
||||
end
|
||||
|
||||
module ObjC_Cpp = struct
|
||||
type kind =
|
||||
| CPPMethod of {mangled: string option}
|
||||
| CPPConstructor of {mangled: string option; is_constexpr: bool}
|
||||
| CPPDestructor of {mangled: string option}
|
||||
| ObjCClassMethod
|
||||
| ObjCInstanceMethod
|
||||
| ObjCInternalMethod
|
||||
[@@deriving compare]
|
||||
|
||||
type t =
|
||||
{ class_name: Typ.Name.t
|
||||
; kind: kind
|
||||
; method_name: string
|
||||
; parameters: Parameter.clang_parameter list
|
||||
; template_args: Typ.template_spec_info }
|
||||
[@@deriving compare]
|
||||
|
||||
let make class_name method_name kind template_args parameters =
|
||||
{class_name; method_name; kind; template_args; parameters}
|
||||
|
||||
|
||||
let get_class_name objc_cpp = Typ.Name.name objc_cpp.class_name
|
||||
|
||||
let get_class_type_name objc_cpp = objc_cpp.class_name
|
||||
|
||||
let get_class_qualifiers objc_cpp = Typ.Name.qual_name objc_cpp.class_name
|
||||
|
||||
let objc_method_kind_of_bool is_instance =
|
||||
if is_instance then ObjCInstanceMethod else ObjCClassMethod
|
||||
|
||||
|
||||
let is_objc_constructor method_name =
|
||||
String.equal method_name "new" || String.is_prefix ~prefix:"init" method_name
|
||||
|
||||
|
||||
let is_objc_kind = function
|
||||
| ObjCClassMethod | ObjCInstanceMethod | ObjCInternalMethod ->
|
||||
true
|
||||
| _ ->
|
||||
false
|
||||
|
||||
|
||||
let is_objc_method {kind} = is_objc_kind kind
|
||||
|
||||
let is_objc_dealloc method_name = String.equal method_name "dealloc"
|
||||
|
||||
let is_destructor = function
|
||||
| {kind= CPPDestructor _} ->
|
||||
true
|
||||
| name ->
|
||||
is_objc_dealloc name.method_name
|
||||
|
||||
|
||||
let is_inner_destructor ({method_name} as pname) =
|
||||
is_destructor pname && String.is_prefix ~prefix:Config.clang_inner_destructor_prefix method_name
|
||||
|
||||
|
||||
let is_constexpr = function {kind= CPPConstructor {is_constexpr= true}} -> true | _ -> false
|
||||
|
||||
let is_cpp_lambda {method_name} = String.is_substring ~substring:"operator()" method_name
|
||||
|
||||
let pp_verbose_kind fmt = function
|
||||
| CPPMethod {mangled} | CPPDestructor {mangled} ->
|
||||
F.fprintf fmt "(%s)" (Option.value ~default:"" mangled)
|
||||
| CPPConstructor {mangled; is_constexpr} ->
|
||||
F.fprintf fmt "{%s%s}"
|
||||
(Option.value ~default:"" mangled)
|
||||
(if is_constexpr then "|constexpr" else "")
|
||||
| ObjCClassMethod ->
|
||||
F.pp_print_string fmt "class"
|
||||
| ObjCInstanceMethod ->
|
||||
F.pp_print_string fmt "instance"
|
||||
| ObjCInternalMethod ->
|
||||
F.pp_print_string fmt "internal"
|
||||
|
||||
|
||||
let pp verbosity fmt osig =
|
||||
match verbosity with
|
||||
| Simple ->
|
||||
F.pp_print_string fmt osig.method_name
|
||||
| Non_verbose ->
|
||||
F.fprintf fmt "%s::%s" (Typ.Name.name osig.class_name) osig.method_name
|
||||
| Verbose ->
|
||||
F.fprintf fmt "%s::%s%a%a" (Typ.Name.name osig.class_name) osig.method_name
|
||||
Parameter.pp_parameters osig.parameters pp_verbose_kind osig.kind
|
||||
|
||||
|
||||
let get_parameters osig = osig.parameters
|
||||
|
||||
let replace_parameters new_parameters osig = {osig with parameters= new_parameters}
|
||||
end
|
||||
|
||||
module C = struct
|
||||
(** Type of c procedure names. *)
|
||||
type t =
|
||||
{ name: QualifiedCppName.t
|
||||
; mangled: string option
|
||||
; parameters: Parameter.clang_parameter list
|
||||
; template_args: Typ.template_spec_info }
|
||||
[@@deriving compare]
|
||||
|
||||
let c name mangled parameters template_args =
|
||||
{name; mangled= Some mangled; parameters; template_args}
|
||||
|
||||
|
||||
let from_string name =
|
||||
{ name= QualifiedCppName.of_qual_string name
|
||||
; mangled= None
|
||||
; parameters= []
|
||||
; template_args= NoTemplate }
|
||||
|
||||
|
||||
let pp verbosity fmt {name; mangled; parameters} =
|
||||
let plain = QualifiedCppName.to_qual_string name in
|
||||
match verbosity with
|
||||
| Simple ->
|
||||
F.fprintf fmt "%s()" plain
|
||||
| Non_verbose ->
|
||||
F.pp_print_string fmt plain
|
||||
| Verbose ->
|
||||
let pp_mangled fmt = function None -> () | Some s -> F.fprintf fmt "{%s}" s in
|
||||
F.fprintf fmt "%s%a%a" plain Parameter.pp_parameters parameters pp_mangled mangled
|
||||
|
||||
|
||||
let get_parameters c = c.parameters
|
||||
|
||||
let replace_parameters new_parameters c = {c with parameters= new_parameters}
|
||||
end
|
||||
|
||||
module Block = struct
|
||||
(** Type of Objective C block names. *)
|
||||
type block_name = string [@@deriving compare]
|
||||
|
||||
type t = {name: block_name; parameters: Parameter.clang_parameter list} [@@deriving compare]
|
||||
|
||||
let make name parameters = {name; parameters}
|
||||
|
||||
let pp verbosity fmt bsig =
|
||||
match verbosity with
|
||||
| Simple ->
|
||||
F.pp_print_string fmt "block"
|
||||
| Non_verbose ->
|
||||
F.pp_print_string fmt bsig.name
|
||||
| Verbose ->
|
||||
F.fprintf fmt "%s%a" bsig.name Parameter.pp_parameters bsig.parameters
|
||||
|
||||
|
||||
let get_parameters block = block.parameters
|
||||
|
||||
let replace_parameters new_parameters block = {block with parameters= new_parameters}
|
||||
end
|
||||
|
||||
(** Type of procedure names. *)
|
||||
type t =
|
||||
| Java of Java.t
|
||||
| C of C.t
|
||||
| Linters_dummy_method
|
||||
| Block of Block.t
|
||||
| ObjC_Cpp of ObjC_Cpp.t
|
||||
| WithBlockParameters of t * Block.block_name list
|
||||
[@@deriving compare]
|
||||
|
||||
let equal = [%compare.equal: t]
|
||||
|
||||
(** hash function for procname *)
|
||||
let hash = Hashtbl.hash
|
||||
|
||||
let with_block_parameters base blocks = WithBlockParameters (base, blocks)
|
||||
|
||||
let is_java = function Java _ -> true | _ -> false
|
||||
|
||||
(* TODO: deprecate this unfortunately named function and use is_clang instead *)
|
||||
let is_c_method = function ObjC_Cpp _ -> true | _ -> false
|
||||
|
||||
let is_c_function = function C _ -> true | _ -> false
|
||||
|
||||
let is_clang = function ObjC_Cpp name -> ObjC_Cpp.is_objc_method name | name -> is_c_function name
|
||||
|
||||
let is_java_lift f = function Java java_pname -> f java_pname | _ -> false
|
||||
|
||||
let is_java_access_method = is_java_lift Java.is_access_method
|
||||
|
||||
let is_java_class_initializer = is_java_lift Java.is_class_initializer
|
||||
|
||||
let is_objc_method procname =
|
||||
match procname with ObjC_Cpp name -> ObjC_Cpp.is_objc_method name | _ -> false
|
||||
|
||||
|
||||
let block_name_of_procname procname =
|
||||
match procname with
|
||||
| Block block ->
|
||||
block.name
|
||||
| _ ->
|
||||
Logging.die InternalError "Only to be called with Objective-C block names"
|
||||
|
||||
|
||||
let empty_block = Block {name= ""; parameters= []}
|
||||
|
||||
(** Replace the class name component of a procedure name. In case of Java, replace package and class
|
||||
name. *)
|
||||
let rec replace_class t (new_class : Typ.Name.t) =
|
||||
match t with
|
||||
| Java j ->
|
||||
Java {j with class_name= new_class}
|
||||
| ObjC_Cpp osig ->
|
||||
ObjC_Cpp {osig with class_name= new_class}
|
||||
| WithBlockParameters (base, blocks) ->
|
||||
WithBlockParameters (replace_class base new_class, blocks)
|
||||
| C _ | Block _ | Linters_dummy_method ->
|
||||
t
|
||||
|
||||
|
||||
let get_class_type_name = function
|
||||
| Java java_pname ->
|
||||
Some (Java.get_class_type_name java_pname)
|
||||
| ObjC_Cpp objc_pname ->
|
||||
Some (ObjC_Cpp.get_class_type_name objc_pname)
|
||||
| _ ->
|
||||
None
|
||||
|
||||
|
||||
let get_class_name = function
|
||||
| Java java_pname ->
|
||||
Some (Java.get_class_name java_pname)
|
||||
| ObjC_Cpp objc_pname ->
|
||||
Some (ObjC_Cpp.get_class_name objc_pname)
|
||||
| _ ->
|
||||
None
|
||||
|
||||
|
||||
let is_method_in_objc_protocol t =
|
||||
match t with ObjC_Cpp osig -> Typ.Name.is_objc_protocol osig.class_name | _ -> false
|
||||
|
||||
|
||||
let rec objc_cpp_replace_method_name t (new_method_name : string) =
|
||||
match t with
|
||||
| ObjC_Cpp osig ->
|
||||
ObjC_Cpp {osig with method_name= new_method_name}
|
||||
| WithBlockParameters (base, blocks) ->
|
||||
WithBlockParameters (objc_cpp_replace_method_name base new_method_name, blocks)
|
||||
| C _ | Block _ | Linters_dummy_method | Java _ ->
|
||||
t
|
||||
|
||||
|
||||
(** Return the method/function of a procname. *)
|
||||
let rec get_method = function
|
||||
| ObjC_Cpp name ->
|
||||
name.method_name
|
||||
| WithBlockParameters (base, _) ->
|
||||
get_method base
|
||||
| C {name} ->
|
||||
QualifiedCppName.to_qual_string name
|
||||
| Block {name} ->
|
||||
name
|
||||
| Java j ->
|
||||
j.method_name
|
||||
| Linters_dummy_method ->
|
||||
"Linters_dummy_method"
|
||||
|
||||
|
||||
(** Return whether the procname is a block procname. *)
|
||||
let is_objc_block = function Block _ -> true | _ -> false
|
||||
|
||||
(** Return the language of the procedure. *)
|
||||
let get_language = function
|
||||
| ObjC_Cpp _ ->
|
||||
Language.Clang
|
||||
| C _ ->
|
||||
Language.Clang
|
||||
| Block _ ->
|
||||
Language.Clang
|
||||
| Linters_dummy_method ->
|
||||
Language.Clang
|
||||
| WithBlockParameters _ ->
|
||||
Language.Clang
|
||||
| Java _ ->
|
||||
Language.Java
|
||||
|
||||
|
||||
(** [is_constructor pname] returns true if [pname] is a constructor *)
|
||||
let is_constructor = function
|
||||
| Java js ->
|
||||
String.equal js.method_name Java.constructor_method_name
|
||||
| ObjC_Cpp {kind= CPPConstructor _} ->
|
||||
true
|
||||
| ObjC_Cpp {kind; method_name} when ObjC_Cpp.is_objc_kind kind ->
|
||||
ObjC_Cpp.is_objc_constructor method_name
|
||||
| _ ->
|
||||
false
|
||||
|
||||
|
||||
(** [is_infer_undefined pn] returns true if [pn] is a special Infer undefined proc *)
|
||||
let is_infer_undefined pn =
|
||||
match pn with
|
||||
| Java j ->
|
||||
let regexp = Str.regexp_string "com.facebook.infer.builtins.InferUndefined" in
|
||||
Str.string_match regexp (Java.get_class_name j) 0
|
||||
| _ ->
|
||||
(* TODO: add cases for obj-c, c, c++ *)
|
||||
false
|
||||
|
||||
|
||||
let get_global_name_of_initializer = function
|
||||
| C {name}
|
||||
when String.is_prefix ~prefix:Config.clang_initializer_prefix
|
||||
(QualifiedCppName.to_qual_string name) ->
|
||||
let name_str = QualifiedCppName.to_qual_string name in
|
||||
let prefix_len = String.length Config.clang_initializer_prefix in
|
||||
Some (String.sub name_str ~pos:prefix_len ~len:(String.length name_str - prefix_len))
|
||||
| _ ->
|
||||
None
|
||||
|
||||
|
||||
(** Very verbose representation of an existing Procname.t *)
|
||||
let rec pp_unique_id fmt = function
|
||||
| Java j ->
|
||||
Java.pp Verbose fmt j
|
||||
| C osig ->
|
||||
C.pp Verbose fmt osig
|
||||
| ObjC_Cpp osig ->
|
||||
ObjC_Cpp.pp Verbose fmt osig
|
||||
| Block bsig ->
|
||||
Block.pp Verbose fmt bsig
|
||||
| WithBlockParameters (base, []) ->
|
||||
pp_unique_id fmt base
|
||||
| WithBlockParameters (base, (_ :: _ as blocks)) ->
|
||||
pp_unique_id fmt base ;
|
||||
F.pp_print_string fmt "_" ;
|
||||
Pp.seq ~sep:"_" F.pp_print_string fmt blocks
|
||||
| Linters_dummy_method ->
|
||||
F.pp_print_string fmt "Linters_dummy_method"
|
||||
|
||||
|
||||
let to_unique_id proc_name = F.asprintf "%a" pp_unique_id proc_name
|
||||
|
||||
(** Convert a proc name to a string for the user to see *)
|
||||
let rec pp fmt = function
|
||||
| Java j ->
|
||||
Java.pp Non_verbose fmt j
|
||||
| C osig ->
|
||||
C.pp Non_verbose fmt osig
|
||||
| ObjC_Cpp osig ->
|
||||
ObjC_Cpp.pp Non_verbose fmt osig
|
||||
| Block bsig ->
|
||||
Block.pp Non_verbose fmt bsig
|
||||
| WithBlockParameters (base, []) ->
|
||||
pp fmt base
|
||||
| WithBlockParameters (base, (_ :: _ as blocks)) ->
|
||||
pp fmt base ;
|
||||
F.pp_print_string fmt "_" ;
|
||||
Pp.seq ~sep:"_" F.pp_print_string fmt blocks
|
||||
| Linters_dummy_method ->
|
||||
pp_unique_id fmt Linters_dummy_method
|
||||
|
||||
|
||||
let to_string proc_name = F.asprintf "%a" pp proc_name
|
||||
|
||||
(** Convenient representation of a procname for external tools (e.g. eclipse plugin) *)
|
||||
let rec pp_simplified_string ?(withclass = false) fmt = function
|
||||
| Java j ->
|
||||
Java.pp ~withclass Simple fmt j
|
||||
| C osig ->
|
||||
C.pp Simple fmt osig
|
||||
| ObjC_Cpp osig ->
|
||||
ObjC_Cpp.pp Simple fmt osig
|
||||
| Block bsig ->
|
||||
Block.pp Simple fmt bsig
|
||||
| WithBlockParameters (base, _) ->
|
||||
pp_simplified_string fmt base
|
||||
| Linters_dummy_method ->
|
||||
pp_unique_id fmt Linters_dummy_method
|
||||
|
||||
|
||||
let to_simplified_string ?withclass proc_name =
|
||||
F.asprintf "%a" (pp_simplified_string ?withclass) proc_name
|
||||
|
||||
|
||||
let from_string_c_fun func = C (C.from_string func)
|
||||
|
||||
let java_inner_class_prefix_regex = Str.regexp "\\$[0-9]+"
|
||||
|
||||
let hashable_name proc_name =
|
||||
match proc_name with
|
||||
| Java pname -> (
|
||||
(* Strip autogenerated anonymous inner class numbers in order to keep the bug hash
|
||||
invariant when introducing new anonymous classes *)
|
||||
let name = F.asprintf "%a" (Java.pp ~withclass:true Simple) pname in
|
||||
match Str.search_forward java_inner_class_prefix_regex name 0 with
|
||||
| _ ->
|
||||
Str.global_replace java_inner_class_prefix_regex "$_" name
|
||||
| exception Caml.Not_found ->
|
||||
name )
|
||||
| ObjC_Cpp m when ObjC_Cpp.is_objc_method m ->
|
||||
(* In Objective C, the list of parameters is part of the method name. To prevent the bug
|
||||
hash to change when a parameter is introduced or removed, only the part of the name
|
||||
before the first colon is used for the bug hash *)
|
||||
let name = F.asprintf "%a" (pp_simplified_string ~withclass:true) proc_name in
|
||||
List.hd_exn (String.split_on_chars name ~on:[':'])
|
||||
| _ ->
|
||||
(* Other cases for C and C++ method names *)
|
||||
F.asprintf "%a" (pp_simplified_string ~withclass:true) proc_name
|
||||
|
||||
|
||||
let rec get_parameters procname =
|
||||
let clang_param_to_param clang_params =
|
||||
List.map ~f:(fun par -> Parameter.ClangParameter par) clang_params
|
||||
in
|
||||
match procname with
|
||||
| Java j ->
|
||||
List.map ~f:(fun par -> Parameter.JavaParameter par) (Java.get_parameters j)
|
||||
| C osig ->
|
||||
clang_param_to_param (C.get_parameters osig)
|
||||
| ObjC_Cpp osig ->
|
||||
clang_param_to_param (ObjC_Cpp.get_parameters osig)
|
||||
| Block bsig ->
|
||||
clang_param_to_param (Block.get_parameters bsig)
|
||||
| WithBlockParameters (base, _) ->
|
||||
get_parameters base
|
||||
| Linters_dummy_method ->
|
||||
[]
|
||||
|
||||
|
||||
let rec replace_parameters new_parameters procname =
|
||||
let params_to_java_params params =
|
||||
List.map
|
||||
~f:(fun param ->
|
||||
match param with
|
||||
| Parameter.JavaParameter par ->
|
||||
par
|
||||
| _ ->
|
||||
Logging.(die InternalError)
|
||||
"Expected Java parameters in Java procname, but got Clang parameters" params )
|
||||
params
|
||||
in
|
||||
let params_to_clang_params params =
|
||||
List.map
|
||||
~f:(fun param ->
|
||||
match param with
|
||||
| Parameter.ClangParameter par ->
|
||||
par
|
||||
| _ ->
|
||||
Logging.(die InternalError)
|
||||
"Expected Clang parameters in Clang procname, but got Java parameters" params )
|
||||
params
|
||||
in
|
||||
match procname with
|
||||
| Java j ->
|
||||
Java (Java.replace_parameters (params_to_java_params new_parameters) j)
|
||||
| C osig ->
|
||||
C (C.replace_parameters (params_to_clang_params new_parameters) osig)
|
||||
| ObjC_Cpp osig ->
|
||||
ObjC_Cpp (ObjC_Cpp.replace_parameters (params_to_clang_params new_parameters) osig)
|
||||
| Block bsig ->
|
||||
Block (Block.replace_parameters (params_to_clang_params new_parameters) bsig)
|
||||
| WithBlockParameters (base, blocks) ->
|
||||
WithBlockParameters (replace_parameters new_parameters base, blocks)
|
||||
| Linters_dummy_method ->
|
||||
procname
|
||||
|
||||
|
||||
let parameter_of_name procname class_name =
|
||||
match procname with
|
||||
| Java _ ->
|
||||
Parameter.JavaParameter (Java.java_type_of_name class_name)
|
||||
| _ ->
|
||||
Parameter.ClangParameter (Parameter.clang_param_of_name class_name)
|
||||
|
||||
|
||||
let describe f pn =
|
||||
let name = hashable_name pn in
|
||||
match String.lsplit2 ~on:'<' name with
|
||||
| Some (name_without_template, _template_part) ->
|
||||
F.pp_print_string f name_without_template
|
||||
| None ->
|
||||
F.pp_print_string f name
|
||||
|
||||
|
||||
module Hashable = struct
|
||||
type nonrec t = t
|
||||
|
||||
let equal = equal
|
||||
|
||||
let hash = hash
|
||||
end
|
||||
|
||||
module Hash = Hashtbl.Make (Hashable)
|
||||
|
||||
module Map = PrettyPrintable.MakePPMap (struct
|
||||
type nonrec t = t
|
||||
|
||||
let compare = compare
|
||||
|
||||
let pp = pp
|
||||
end)
|
||||
|
||||
module Set = PrettyPrintable.MakePPSet (struct
|
||||
type nonrec t = t
|
||||
|
||||
let compare = compare
|
||||
|
||||
let pp = pp
|
||||
end)
|
||||
|
||||
let get_qualifiers pname =
|
||||
match pname with
|
||||
| C {name} ->
|
||||
name
|
||||
| ObjC_Cpp objc_cpp ->
|
||||
ObjC_Cpp.get_class_qualifiers objc_cpp
|
||||
|> QualifiedCppName.append_qualifier ~qual:objc_cpp.method_name
|
||||
| _ ->
|
||||
QualifiedCppName.empty
|
||||
|
||||
|
||||
(** Convert a proc name to a filename *)
|
||||
let to_filename ?crc_only pname =
|
||||
(* filenames for clang procs are REVERSED qualifiers with '#' as separator *)
|
||||
let pp_rev_qualified fmt pname =
|
||||
let rev_qualifiers = get_qualifiers pname |> QualifiedCppName.to_rev_list in
|
||||
Pp.seq ~sep:"#" F.pp_print_string fmt rev_qualifiers
|
||||
in
|
||||
let proc_id =
|
||||
match pname with
|
||||
| C {parameters; mangled} ->
|
||||
let pp_mangled fmt = function None -> () | Some mangled -> F.fprintf fmt "#%s" mangled in
|
||||
F.asprintf "%a%a%a" pp_rev_qualified pname Parameter.pp_parameters parameters pp_mangled
|
||||
mangled
|
||||
| ObjC_Cpp objc_cpp ->
|
||||
F.asprintf "%a%a#%a" pp_rev_qualified pname Parameter.pp_parameters objc_cpp.parameters
|
||||
ObjC_Cpp.pp_verbose_kind objc_cpp.kind
|
||||
| _ ->
|
||||
F.asprintf "%a" pp_unique_id pname
|
||||
in
|
||||
Escape.escape_filename @@ DB.append_crc_cutoff ?crc_only proc_id
|
||||
|
||||
|
||||
module SQLite = struct
|
||||
module T = struct
|
||||
type nonrec t = t
|
||||
|
||||
let compare = compare
|
||||
|
||||
let hash = hash
|
||||
|
||||
let sexp_of_t p = Sexp.Atom (F.asprintf "%a" pp p)
|
||||
end
|
||||
|
||||
module Serializer = SqliteUtils.MarshalledDataForComparison (T)
|
||||
|
||||
let pname_to_key = Base.Hashtbl.create (module T)
|
||||
|
||||
let serialize pname =
|
||||
let default () = Serializer.serialize pname in
|
||||
Base.Hashtbl.find_or_add pname_to_key pname ~default
|
||||
|
||||
|
||||
let deserialize = Serializer.deserialize
|
||||
|
||||
let clear_cache () = Base.Hashtbl.clear pname_to_key
|
||||
end
|
||||
|
||||
module SQLiteList = SqliteUtils.MarshalledDataNOTForComparison (struct
|
||||
type nonrec t = t list
|
||||
end)
|
@ -0,0 +1,316 @@
|
||||
(*
|
||||
* 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
|
||||
module F = Format
|
||||
|
||||
(** Module for Procedure Names. *)
|
||||
|
||||
(** Type of java procedure names. *)
|
||||
module Java : sig
|
||||
type kind =
|
||||
| Non_Static
|
||||
(** in Java, procedures called with invokevirtual, invokespecial, and invokeinterface *)
|
||||
| Static (** in Java, procedures called with invokestatic *)
|
||||
|
||||
type t [@@deriving compare]
|
||||
|
||||
type java_type = Typ.Name.Java.Split.t
|
||||
|
||||
val constructor_method_name : string
|
||||
|
||||
val class_initializer_method_name : string
|
||||
|
||||
val compare_java_type : java_type -> java_type -> int
|
||||
|
||||
val make : Typ.Name.t -> java_type option -> string -> java_type list -> kind -> t
|
||||
(** Create a Java procedure name from its class_name method_name args_type_name return_type_name
|
||||
method_kind. *)
|
||||
|
||||
val replace_method_name : string -> t -> t
|
||||
(** Replace the method name of an existing java procname. *)
|
||||
|
||||
val replace_parameters : java_type list -> t -> t
|
||||
(** Replace the parameters of a java procname. *)
|
||||
|
||||
val replace_return_type : java_type -> t -> t
|
||||
(** Replace the method of a java procname. *)
|
||||
|
||||
val get_class_name : t -> string
|
||||
(** Return the fully qualified class name of a java procedure name (package + class name) *)
|
||||
|
||||
val get_class_type_name : t -> Typ.Name.t
|
||||
(** Return the class name as a typename of a java procedure name. *)
|
||||
|
||||
val get_simple_class_name : t -> string
|
||||
(** Return the simple class name of a java procedure name (i.e. name without the package info). *)
|
||||
|
||||
val get_package : t -> string option
|
||||
(** Return the package name of a java procedure name. *)
|
||||
|
||||
val get_method : t -> string
|
||||
(** Return the method name of a java procedure name. *)
|
||||
|
||||
val get_parameters : t -> java_type list
|
||||
(** Return the parameters of a java procedure name. *)
|
||||
|
||||
val get_return_typ : t -> Typ.t
|
||||
(** Return the return type of [pname_java]. return Tvoid if there's no return type *)
|
||||
|
||||
val is_constructor : t -> bool
|
||||
(** Whether the method is constructor *)
|
||||
|
||||
val is_access_method : t -> bool
|
||||
(** Check if the procedure name is an acess method (e.g. access$100 used to access private members
|
||||
from a nested class. *)
|
||||
|
||||
val is_autogen_method : t -> bool
|
||||
(** Check if the procedure name is of an auto-generated method containing '$'. *)
|
||||
|
||||
val is_anonymous_inner_class_constructor : t -> bool
|
||||
(** Check if the procedure name is an anonymous inner class constructor. *)
|
||||
|
||||
val is_close : t -> bool
|
||||
(** Check if the method name is "close". *)
|
||||
|
||||
val is_static : t -> bool
|
||||
(** Check if the java procedure is static. *)
|
||||
|
||||
val is_vararg : t -> bool
|
||||
(** Check if the proc name has the type of a java vararg. Note: currently only checks that the
|
||||
last argument has type Object[]. *)
|
||||
|
||||
val is_lambda : t -> bool
|
||||
(** Check if the proc name comes from a lambda expression *)
|
||||
|
||||
val is_generated : t -> bool
|
||||
(** Check if the proc name comes from generated code *)
|
||||
|
||||
val is_class_initializer : t -> bool
|
||||
(** Check if this is a class initializer. *)
|
||||
|
||||
val get_class_initializer : Typ.Name.t -> t
|
||||
(** Given a java class, generate the procname of its static initializer. *)
|
||||
|
||||
val is_external : t -> bool
|
||||
(** Check if the method belongs to one of the specified external packages *)
|
||||
end
|
||||
|
||||
module Parameter : sig
|
||||
(** Type for parameters in clang procnames, [Some name] means the parameter is of type pointer to
|
||||
struct, with [name] being the name of the struct, [None] means the parameter is of some other
|
||||
type. *)
|
||||
type clang_parameter = Typ.Name.t option [@@deriving compare]
|
||||
|
||||
(** Type for parameters in procnames, for java and clang. *)
|
||||
type t = JavaParameter of Java.java_type | ClangParameter of clang_parameter
|
||||
[@@deriving compare]
|
||||
|
||||
val of_typ : Typ.t -> clang_parameter
|
||||
end
|
||||
|
||||
module ObjC_Cpp : sig
|
||||
type kind =
|
||||
| CPPMethod of {mangled: string option}
|
||||
| CPPConstructor of {mangled: string option; is_constexpr: bool}
|
||||
| CPPDestructor of {mangled: string option}
|
||||
| ObjCClassMethod
|
||||
| ObjCInstanceMethod
|
||||
| ObjCInternalMethod
|
||||
[@@deriving compare]
|
||||
|
||||
(** Type of Objective C and C++ procedure names: method signatures. *)
|
||||
type t =
|
||||
{ class_name: Typ.Name.t
|
||||
; kind: kind
|
||||
; method_name: string
|
||||
; parameters: Parameter.clang_parameter list
|
||||
; template_args: Typ.template_spec_info }
|
||||
[@@deriving compare]
|
||||
|
||||
val make :
|
||||
Typ.Name.t -> string -> kind -> Typ.template_spec_info -> Parameter.clang_parameter list -> t
|
||||
(** Create an objc procedure name from a class_name and method_name. *)
|
||||
|
||||
val get_class_name : t -> string
|
||||
|
||||
val get_class_type_name : t -> Typ.Name.t [@@warning "-32"]
|
||||
|
||||
val get_class_qualifiers : t -> QualifiedCppName.t
|
||||
|
||||
val objc_method_kind_of_bool : bool -> kind
|
||||
(** Create ObjC method type from a bool is_instance. *)
|
||||
|
||||
val is_objc_constructor : string -> bool
|
||||
(** Check if this is a constructor method in Objective-C. *)
|
||||
|
||||
val is_objc_dealloc : string -> bool
|
||||
(** Check if this is a dealloc method in Objective-C. *)
|
||||
|
||||
val is_destructor : t -> bool
|
||||
(** Check if this is a dealloc method. *)
|
||||
|
||||
val is_inner_destructor : t -> bool
|
||||
(** Check if this is a frontend-generated "inner" destructor (see D5834555/D7189239) *)
|
||||
|
||||
val is_constexpr : t -> bool
|
||||
(** Check if this is a constexpr function. *)
|
||||
|
||||
val is_cpp_lambda : t -> bool
|
||||
(** Return whether the procname is a cpp lambda. *)
|
||||
end
|
||||
|
||||
module C : sig
|
||||
(** Type of c procedure names. *)
|
||||
type t = private
|
||||
{ name: QualifiedCppName.t
|
||||
; mangled: string option
|
||||
; parameters: Parameter.clang_parameter list
|
||||
; template_args: Typ.template_spec_info }
|
||||
|
||||
val c :
|
||||
QualifiedCppName.t -> string -> Parameter.clang_parameter list -> Typ.template_spec_info -> t
|
||||
(** Create a C procedure name from plain and mangled name. *)
|
||||
end
|
||||
|
||||
module Block : sig
|
||||
(** Type of Objective C block names. *)
|
||||
type block_name = string
|
||||
|
||||
type t = {name: block_name; parameters: Parameter.clang_parameter list} [@@deriving compare]
|
||||
|
||||
val make : block_name -> Parameter.clang_parameter list -> t
|
||||
end
|
||||
|
||||
(** Type of procedure names. WithBlockParameters is used for creating an instantiation of a method
|
||||
that contains block parameters and it's called with concrete blocks. For example:
|
||||
[foo(Block block) {block();}] [bar() {foo(my_block)}] is executed as
|
||||
[foo_my_block() {my_block(); }] where foo_my_block is created with WithBlockParameters (foo,
|
||||
[my_block]) *)
|
||||
type t =
|
||||
| Java of Java.t
|
||||
| C of C.t
|
||||
| Linters_dummy_method
|
||||
| Block of Block.t
|
||||
| ObjC_Cpp of ObjC_Cpp.t
|
||||
| WithBlockParameters of t * Block.block_name list
|
||||
[@@deriving compare]
|
||||
|
||||
val block_name_of_procname : t -> Block.block_name
|
||||
|
||||
val equal : t -> t -> bool
|
||||
|
||||
val get_class_type_name : t -> Typ.Name.t option
|
||||
|
||||
val get_class_name : t -> string option
|
||||
|
||||
val get_parameters : t -> Parameter.t list
|
||||
|
||||
val replace_parameters : Parameter.t list -> t -> t
|
||||
|
||||
val parameter_of_name : t -> Typ.Name.t -> Parameter.t
|
||||
|
||||
val is_java_access_method : t -> bool
|
||||
|
||||
val is_java_class_initializer : t -> bool
|
||||
|
||||
val is_objc_method : t -> bool
|
||||
|
||||
module Hash : Caml.Hashtbl.S with type key = t
|
||||
(** Hash tables with proc names as keys. *)
|
||||
|
||||
module Map : PrettyPrintable.PPMap with type key = t
|
||||
(** Maps from proc names. *)
|
||||
|
||||
module Set : PrettyPrintable.PPSet with type elt = t
|
||||
(** Sets of proc names. *)
|
||||
|
||||
module SQLite : sig
|
||||
val serialize : t -> Sqlite3.Data.t
|
||||
|
||||
val deserialize : Sqlite3.Data.t -> t
|
||||
|
||||
val clear_cache : unit -> unit
|
||||
end
|
||||
|
||||
module SQLiteList : SqliteUtils.Data with type t = t list
|
||||
|
||||
val empty_block : t
|
||||
(** Empty block name. *)
|
||||
|
||||
val get_language : t -> Language.t
|
||||
(** Return the language of the procedure. *)
|
||||
|
||||
val get_method : t -> string
|
||||
(** Return the method/function of a procname. *)
|
||||
|
||||
val is_objc_block : t -> bool
|
||||
(** Return whether the procname is a block procname. *)
|
||||
|
||||
val is_c_method : t -> bool
|
||||
(** Return true this is an Objective-C/C++ method name. *)
|
||||
|
||||
val is_clang : t -> bool
|
||||
(** Return true if this is a C, C++, or Objective-C procedure name *)
|
||||
|
||||
val is_constructor : t -> bool
|
||||
(** Check if this is a constructor. *)
|
||||
|
||||
val is_java : t -> bool
|
||||
(** Check if this is a Java procedure name. *)
|
||||
|
||||
val with_block_parameters : t -> Block.block_name list -> t
|
||||
(** Create a procedure name instantiated with block parameters from a base procedure name and a list
|
||||
of block procedure names (the arguments). *)
|
||||
|
||||
val objc_cpp_replace_method_name : t -> string -> t
|
||||
|
||||
val is_infer_undefined : t -> bool
|
||||
(** Check if this is a special Infer undefined procedure. *)
|
||||
|
||||
val get_global_name_of_initializer : t -> string option
|
||||
(** Return the name of the global for which this procedure is the initializer if this is an
|
||||
initializer, None otherwise. *)
|
||||
|
||||
val pp : Format.formatter -> t -> unit
|
||||
(** Pretty print a proc name for the user to see. *)
|
||||
|
||||
val to_string : t -> string
|
||||
(** Convert a proc name into a string for the user to see. *)
|
||||
|
||||
val describe : Format.formatter -> t -> unit
|
||||
(** to use in user messages *)
|
||||
|
||||
val replace_class : t -> Typ.Name.t -> t
|
||||
(** Replace the class name component of a procedure name. In case of Java, replace package and class
|
||||
name. *)
|
||||
|
||||
val is_method_in_objc_protocol : t -> bool
|
||||
|
||||
val pp_simplified_string : ?withclass:bool -> F.formatter -> t -> unit
|
||||
(** Pretty print a proc name as an easy string for the user to see in an IDE. *)
|
||||
|
||||
val to_simplified_string : ?withclass:bool -> t -> string
|
||||
(** Convert a proc name into an easy string for the user to see in an IDE. *)
|
||||
|
||||
val from_string_c_fun : string -> t
|
||||
(** Convert a string to a c function name. *)
|
||||
|
||||
val hashable_name : t -> string
|
||||
(** Convert the procedure name in a format suitable for computing the bug hash. *)
|
||||
|
||||
val pp_unique_id : F.formatter -> t -> unit
|
||||
(** Print a proc name as a unique identifier. *)
|
||||
|
||||
val to_unique_id : t -> string
|
||||
(** Convert a proc name into a unique identifier. *)
|
||||
|
||||
val to_filename : ?crc_only:bool -> t -> string
|
||||
(** Convert a proc name to a filename or only to its crc. *)
|
||||
|
||||
val get_qualifiers : t -> QualifiedCppName.t
|
||||
(** get qualifiers of C/objc/C++ method/function *)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue