|
|
|
(*
|
|
|
|
* Copyright (c) 2009-2013, Monoidics ltd.
|
|
|
|
* 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.
|
|
|
|
*)
|
|
|
|
|
|
|
|
[@@@ocamlformat "parse-docstrings = false"]
|
|
|
|
|
|
|
|
(** The Smallfoot Intermediate Language: Types *)
|
|
|
|
|
|
|
|
open! IStd
|
|
|
|
module Hashtbl = Caml.Hashtbl
|
|
|
|
module L = Logging
|
|
|
|
module F = Format
|
|
|
|
|
|
|
|
module IntegerWidths = struct
|
|
|
|
type t = {char_width: int; short_width: int; int_width: int; long_width: int; longlong_width: int}
|
|
|
|
[@@deriving compare]
|
|
|
|
|
|
|
|
let java = {char_width= 16; short_width= 16; int_width= 32; long_width= 64; longlong_width= 64}
|
|
|
|
|
|
|
|
module SQLite = SqliteUtils.MarshalledNullableDataNOTForComparison (struct
|
|
|
|
type nonrec t = t
|
|
|
|
end)
|
|
|
|
|
|
|
|
let load_statement =
|
|
|
|
ResultsDatabase.register_statement
|
|
|
|
"SELECT integer_type_widths FROM source_files WHERE source_file = :k"
|
|
|
|
|
|
|
|
|
|
|
|
let load source =
|
|
|
|
ResultsDatabase.with_registered_statement load_statement ~f:(fun db load_stmt ->
|
|
|
|
SourceFile.SQLite.serialize source
|
|
|
|
|> Sqlite3.bind load_stmt 1
|
|
|
|
|> SqliteUtils.check_result_code db ~log:"load bind source file" ;
|
|
|
|
SqliteUtils.result_single_column_option ~finalize:false ~log:"Typ.IntegerWidths.load" db
|
|
|
|
load_stmt
|
|
|
|
|> Option.bind ~f:SQLite.deserialize )
|
|
|
|
end
|
|
|
|
|
|
|
|
(** Kinds of integers *)
|
|
|
|
type ikind =
|
|
|
|
| IChar (** [char] *)
|
|
|
|
| ISChar (** [signed char] *)
|
|
|
|
| IUChar (** [unsigned char] *)
|
|
|
|
| IBool (** [bool] *)
|
|
|
|
| IInt (** [int] *)
|
|
|
|
| IUInt (** [unsigned int] *)
|
|
|
|
| IShort (** [short] *)
|
|
|
|
| IUShort (** [unsigned short] *)
|
|
|
|
| ILong (** [long] *)
|
|
|
|
| IULong (** [unsigned long] *)
|
|
|
|
| ILongLong (** [long long] (or [_int64] on Microsoft Visual C) *)
|
|
|
|
| IULongLong (** [unsigned long long] (or [unsigned int64_] on Microsoft Visual C) *)
|
|
|
|
| I128 (** [__int128_t] *)
|
|
|
|
| IU128 (** [__uint128_t] *)
|
|
|
|
[@@deriving compare]
|
|
|
|
|
|
|
|
let equal_ikind = [%compare.equal: ikind]
|
|
|
|
|
|
|
|
let ikind_to_string = function
|
|
|
|
| IChar ->
|
|
|
|
"char"
|
|
|
|
| ISChar ->
|
|
|
|
"signed char"
|
|
|
|
| IUChar ->
|
|
|
|
"unsigned char"
|
|
|
|
| IBool ->
|
|
|
|
"_Bool"
|
|
|
|
| IInt ->
|
|
|
|
"int"
|
|
|
|
| IUInt ->
|
|
|
|
"unsigned int"
|
|
|
|
| IShort ->
|
|
|
|
"short"
|
|
|
|
| IUShort ->
|
|
|
|
"unsigned short"
|
|
|
|
| ILong ->
|
|
|
|
"long"
|
|
|
|
| IULong ->
|
|
|
|
"unsigned long"
|
|
|
|
| ILongLong ->
|
|
|
|
"long long"
|
|
|
|
| IULongLong ->
|
|
|
|
"unsigned long long"
|
|
|
|
| I128 ->
|
|
|
|
"__int128_t"
|
|
|
|
| IU128 ->
|
|
|
|
"__uint128_t"
|
|
|
|
|
|
|
|
|
|
|
|
let width_of_ikind {IntegerWidths.char_width; short_width; int_width; long_width; longlong_width} =
|
|
|
|
function
|
|
|
|
| IBool ->
|
|
|
|
8
|
|
|
|
| ISChar | IChar | IUChar ->
|
|
|
|
char_width
|
|
|
|
| IShort | IUShort ->
|
|
|
|
short_width
|
|
|
|
| IInt | IUInt ->
|
|
|
|
int_width
|
|
|
|
| ILong | IULong ->
|
|
|
|
long_width
|
|
|
|
| ILongLong | IULongLong ->
|
|
|
|
longlong_width
|
|
|
|
| I128 | IU128 ->
|
|
|
|
128
|
|
|
|
|
|
|
|
|
|
|
|
let ikind_is_unsigned = function
|
|
|
|
| IBool | IUChar | IUShort | IUInt | IULong | IULongLong | IU128 ->
|
|
|
|
true
|
|
|
|
| ISChar | IChar | IShort | IInt | ILong | ILongLong | I128 ->
|
|
|
|
false
|
|
|
|
|
|
|
|
|
|
|
|
let range_of_ikind =
|
|
|
|
let range bits ~unsigned =
|
|
|
|
if unsigned then Z.(~$0, shift_left ~$1 bits - ~$1)
|
|
|
|
else
|
|
|
|
let bound = Z.(shift_left ~$1) (bits - 1) in
|
|
|
|
Z.(~-bound, bound - ~$1)
|
|
|
|
in
|
|
|
|
fun integer_widths x ->
|
|
|
|
let bits_for_range = match x with IBool -> 1 | _ -> width_of_ikind integer_widths x in
|
|
|
|
range bits_for_range ~unsigned:(ikind_is_unsigned x)
|
|
|
|
|
|
|
|
|
|
|
|
let ikind_is_char = function IChar | ISChar | IUChar -> true | _ -> false
|
|
|
|
|
|
|
|
(** Kinds of floating-point numbers *)
|
|
|
|
type fkind = FFloat (** [float] *) | FDouble (** [double] *) | FLongDouble (** [long double] *)
|
|
|
|
[@@deriving compare]
|
|
|
|
|
|
|
|
let equal_fkind = [%compare.equal: fkind]
|
|
|
|
|
|
|
|
let fkind_to_string = function
|
|
|
|
| FFloat ->
|
|
|
|
"float"
|
|
|
|
| FDouble ->
|
|
|
|
"double"
|
|
|
|
| FLongDouble ->
|
|
|
|
"long double"
|
|
|
|
|
|
|
|
|
|
|
|
(** kind of pointer *)
|
|
|
|
type ptr_kind =
|
|
|
|
| Pk_pointer (** C/C++, Java, Objc standard/__strong pointer *)
|
|
|
|
| Pk_reference (** C++ reference *)
|
|
|
|
| Pk_objc_weak (** Obj-C __weak pointer *)
|
|
|
|
| Pk_objc_unsafe_unretained (** Obj-C __unsafe_unretained pointer *)
|
|
|
|
| Pk_objc_autoreleasing (** Obj-C __autoreleasing pointer *)
|
|
|
|
[@@deriving compare]
|
|
|
|
|
|
|
|
let equal_ptr_kind = [%compare.equal: ptr_kind]
|
|
|
|
|
|
|
|
let ptr_kind_string = function
|
|
|
|
| Pk_reference ->
|
|
|
|
"&"
|
|
|
|
| Pk_pointer ->
|
|
|
|
"*"
|
|
|
|
| Pk_objc_weak ->
|
|
|
|
"__weak *"
|
|
|
|
| Pk_objc_unsafe_unretained ->
|
|
|
|
"__unsafe_unretained *"
|
|
|
|
| Pk_objc_autoreleasing ->
|
|
|
|
"__autoreleasing *"
|
|
|
|
|
|
|
|
|
|
|
|
module T = struct
|
|
|
|
type type_quals = {is_const: bool; is_restrict: bool; is_volatile: bool} [@@deriving compare]
|
|
|
|
|
|
|
|
(** types for sil (structured) expressions *)
|
|
|
|
type t = {desc: desc; quals: type_quals} [@@deriving compare]
|
|
|
|
|
|
|
|
and desc =
|
|
|
|
| Tint of ikind (** integer type *)
|
|
|
|
| Tfloat of fkind (** float type *)
|
|
|
|
| Tvoid (** void type *)
|
|
|
|
| Tfun (** function type *)
|
|
|
|
| Tptr of t * ptr_kind (** pointer type *)
|
|
|
|
| Tstruct of name (** structured value type name *)
|
|
|
|
| TVar of string (** type variable (ie. C++ template variables) *)
|
|
|
|
| Tarray of {elt: t; length: IntLit.t option; stride: IntLit.t option}
|
|
|
|
(** array type with statically fixed length and stride *)
|
|
|
|
[@@deriving compare]
|
|
|
|
|
|
|
|
and name =
|
|
|
|
| CStruct of QualifiedCppName.t
|
|
|
|
| CUnion of QualifiedCppName.t
|
|
|
|
| CppClass of QualifiedCppName.t * template_spec_info
|
|
|
|
| JavaClass of Mangled.t
|
|
|
|
| ObjcClass of QualifiedCppName.t
|
|
|
|
| ObjcProtocol of QualifiedCppName.t
|
|
|
|
[@@deriving compare]
|
|
|
|
|
|
|
|
and template_arg = TType of t | TInt of Int64.t | TNull | TNullPtr | TOpaque
|
|
|
|
[@@deriving compare]
|
|
|
|
|
|
|
|
and template_spec_info =
|
|
|
|
| NoTemplate
|
|
|
|
| Template of {mangled: string option; args: template_arg list}
|
|
|
|
[@@deriving compare]
|
|
|
|
|
|
|
|
let equal_desc = [%compare.equal: desc]
|
|
|
|
|
|
|
|
let equal_name = [%compare.equal: name]
|
|
|
|
|
|
|
|
let equal_quals = [%compare.equal: type_quals]
|
|
|
|
|
|
|
|
let equal = [%compare.equal: t]
|
|
|
|
|
|
|
|
let rec equal_ignore_quals t1 t2 = equal_desc_ignore_quals t1.desc t2.desc
|
|
|
|
|
|
|
|
and equal_desc_ignore_quals d1 d2 =
|
|
|
|
match (d1, d2) with
|
|
|
|
| Tint ikind1, Tint ikind2 ->
|
|
|
|
equal_ikind ikind1 ikind2
|
|
|
|
| Tfloat fkind1, Tfloat fkind2 ->
|
|
|
|
equal_fkind fkind1 fkind2
|
|
|
|
| Tvoid, Tvoid ->
|
|
|
|
true
|
|
|
|
| Tptr (t1, ptr_kind1), Tptr (t2, ptr_kind2) ->
|
|
|
|
equal_ptr_kind ptr_kind1 ptr_kind2 && equal_ignore_quals t1 t2
|
|
|
|
| Tarray {elt= t1}, Tarray {elt= t2} ->
|
|
|
|
equal_ignore_quals t1 t2
|
|
|
|
| _, _ ->
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
|
|
|
include T
|
|
|
|
|
|
|
|
let mk_type_quals ?default ?is_const ?is_restrict ?is_volatile () =
|
|
|
|
let default_ = {is_const= false; is_restrict= false; is_volatile= false} in
|
|
|
|
let mk_aux ?(default = default_) ?(is_const = default.is_const)
|
|
|
|
?(is_restrict = default.is_restrict) ?(is_volatile = default.is_volatile) () =
|
|
|
|
{is_const; is_restrict; is_volatile}
|
|
|
|
in
|
|
|
|
mk_aux ?default ?is_const ?is_restrict ?is_volatile ()
|
|
|
|
|
|
|
|
|
|
|
|
let is_const {is_const} = is_const
|
|
|
|
|
|
|
|
let is_restrict {is_restrict} = is_restrict
|
|
|
|
|
|
|
|
let is_volatile {is_volatile} = is_volatile
|
|
|
|
|
|
|
|
let is_weak_pointer t = match t.desc with Tptr (_, Pk_objc_weak) -> true | _ -> false
|
|
|
|
|
|
|
|
let is_strong_pointer t = match t.desc with Tptr (_, Pk_pointer) -> true | _ -> false
|
|
|
|
|
|
|
|
let mk ?default ?quals desc : t =
|
|
|
|
let default_ = {desc; quals= mk_type_quals ()} in
|
|
|
|
let mk_aux ?(default = default_) ?(quals = default.quals) desc = {desc; quals} in
|
|
|
|
mk_aux ?default ?quals desc
|
|
|
|
|
|
|
|
|
|
|
|
let mk_array ?default ?quals ?length ?stride elt : t =
|
|
|
|
mk ?default ?quals (Tarray {elt; length; stride})
|
|
|
|
|
|
|
|
|
|
|
|
let void = mk Tvoid
|
|
|
|
|
|
|
|
let void_star = mk (Tptr (mk Tvoid, Pk_pointer))
|
|
|
|
|
|
|
|
let uint = mk (Tint IUInt)
|
|
|
|
|
|
|
|
let get_ikind_opt {desc} = match desc with Tint ikind -> Some ikind | _ -> None
|
|
|
|
|
|
|
|
(* TODO: size_t should be implementation-dependent. *)
|
|
|
|
let size_t = IULong
|
|
|
|
|
|
|
|
let escape pe = if Pp.equal_print_kind pe.Pp.kind Pp.HTML then Escape.escape_xml else ident
|
|
|
|
|
|
|
|
(** Pretty print a type with all the details, using the C syntax. *)
|
|
|
|
let rec pp_full pe f typ =
|
|
|
|
let pp_quals f {quals} =
|
|
|
|
if is_const quals then F.pp_print_string f " const " ;
|
|
|
|
if is_restrict quals then F.pp_print_string f " __restrict " ;
|
|
|
|
if is_volatile quals then F.pp_print_string f " volatile "
|
|
|
|
in
|
|
|
|
let pp_desc f {desc} =
|
|
|
|
match desc with
|
|
|
|
| Tstruct tname ->
|
|
|
|
(pp_name_c_syntax pe) f tname
|
|
|
|
| TVar name ->
|
|
|
|
F.pp_print_string f name
|
|
|
|
| Tint ik ->
|
|
|
|
F.pp_print_string f (ikind_to_string ik)
|
|
|
|
| Tfloat fk ->
|
|
|
|
F.pp_print_string f (fkind_to_string fk)
|
|
|
|
| Tvoid ->
|
|
|
|
F.pp_print_string f "void"
|
|
|
|
| Tfun ->
|
|
|
|
F.pp_print_string f "_fn_"
|
|
|
|
| Tptr (({desc= Tarray _ | Tfun} as typ), pk) ->
|
|
|
|
F.fprintf f "%a(%s)" (pp_full pe) typ (ptr_kind_string pk |> escape pe)
|
|
|
|
| Tptr (typ, pk) ->
|
|
|
|
F.fprintf f "%a%s" (pp_full pe) typ (ptr_kind_string pk |> escape pe)
|
|
|
|
| Tarray {elt; length; stride} ->
|
|
|
|
let pp_int_opt fmt = function
|
|
|
|
| Some x ->
|
|
|
|
IntLit.pp fmt x
|
|
|
|
| None ->
|
|
|
|
F.pp_print_char fmt '_'
|
|
|
|
in
|
|
|
|
F.fprintf f "%a[%a*%a]" (pp_full pe) elt pp_int_opt length pp_int_opt stride
|
|
|
|
in
|
|
|
|
F.fprintf f "%a%a" pp_desc typ pp_quals typ
|
|
|
|
|
|
|
|
|
|
|
|
and pp_name_c_syntax pe f = function
|
|
|
|
| CStruct name | CUnion name | ObjcClass name | ObjcProtocol name ->
|
|
|
|
QualifiedCppName.pp f name
|
|
|
|
| CppClass (name, template_spec) ->
|
|
|
|
F.fprintf f "%a%a" QualifiedCppName.pp name (pp_template_spec_info pe) template_spec
|
|
|
|
| JavaClass name ->
|
|
|
|
Mangled.pp f name
|
|
|
|
|
|
|
|
|
|
|
|
and pp_template_spec_info pe f = function
|
|
|
|
| NoTemplate ->
|
|
|
|
()
|
|
|
|
| Template {args} ->
|
|
|
|
let pp_arg_opt f = function
|
|
|
|
| TType typ ->
|
|
|
|
pp_full pe f typ
|
|
|
|
| TInt i ->
|
|
|
|
Int64.pp f i
|
|
|
|
| TNull ->
|
|
|
|
F.pp_print_string f "null"
|
|
|
|
| TNullPtr ->
|
|
|
|
F.pp_print_string f "NullPtr"
|
|
|
|
| TOpaque ->
|
|
|
|
F.pp_print_string f "Opaque"
|
|
|
|
in
|
|
|
|
F.fprintf f "%s%a%s" (escape pe "<") (Pp.comma_seq pp_arg_opt) args (escape pe ">")
|
|
|
|
|
|
|
|
|
|
|
|
(** Pretty print a type. Do nothing by default. *)
|
|
|
|
let pp pe f te = if Config.print_types then pp_full pe f te else ()
|
|
|
|
|
|
|
|
let to_string typ =
|
|
|
|
let pp fmt = pp_full Pp.text fmt typ in
|
|
|
|
F.asprintf "%t" pp
|
|
|
|
|
|
|
|
|
|
|
|
module Name = struct
|
|
|
|
type t = name [@@deriving compare]
|
|
|
|
|
|
|
|
let equal = [%compare.equal: t]
|
|
|
|
|
|
|
|
let hash = Hashtbl.hash
|
|
|
|
|
|
|
|
let qual_name = function
|
|
|
|
| CStruct name | CUnion name | ObjcClass name | ObjcProtocol name ->
|
|
|
|
name
|
|
|
|
| CppClass (name, templ_args) ->
|
|
|
|
let template_suffix = F.asprintf "%a" (pp_template_spec_info Pp.text) templ_args in
|
|
|
|
QualifiedCppName.append_template_args_to_last name ~args:template_suffix
|
|
|
|
| JavaClass _ ->
|
|
|
|
QualifiedCppName.empty
|
|
|
|
|
|
|
|
|
|
|
|
let unqualified_name = function
|
|
|
|
| CStruct name | CUnion name | ObjcClass name | ObjcProtocol name ->
|
|
|
|
name
|
|
|
|
| CppClass (name, _) ->
|
|
|
|
name
|
|
|
|
| JavaClass _ ->
|
|
|
|
QualifiedCppName.empty
|
|
|
|
|
|
|
|
|
|
|
|
let get_template_spec_info = function CppClass (_, templ_args) -> Some templ_args | _ -> None
|
|
|
|
|
|
|
|
let name n =
|
|
|
|
match n with
|
|
|
|
| CStruct _ | CUnion _ | CppClass _ | ObjcClass _ | ObjcProtocol _ ->
|
|
|
|
qual_name n |> QualifiedCppName.to_qual_string
|
|
|
|
| JavaClass name ->
|
|
|
|
Mangled.to_string name
|
|
|
|
|
|
|
|
|
|
|
|
let pp fmt tname =
|
|
|
|
let prefix = function
|
|
|
|
| CStruct _ ->
|
|
|
|
"struct"
|
|
|
|
| CUnion _ ->
|
|
|
|
"union"
|
|
|
|
| CppClass _ | JavaClass _ | ObjcClass _ ->
|
|
|
|
"class"
|
|
|
|
| ObjcProtocol _ ->
|
|
|
|
"protocol"
|
|
|
|
in
|
|
|
|
F.fprintf fmt "%s %a" (prefix tname) (pp_name_c_syntax Pp.text) tname
|
|
|
|
|
|
|
|
|
|
|
|
let to_string = F.asprintf "%a" pp
|
|
|
|
|
|
|
|
let is_class = function CppClass _ | JavaClass _ | ObjcClass _ -> true | _ -> false
|
|
|
|
|
|
|
|
let is_union = function CUnion _ -> true | _ -> false
|
|
|
|
|
|
|
|
let is_objc_protocol name = match name with ObjcProtocol _ -> true | _ -> false
|
|
|
|
|
|
|
|
let is_same_type t1 t2 =
|
|
|
|
match (t1, t2) with
|
|
|
|
| CStruct _, CStruct _
|
|
|
|
| CUnion _, CUnion _
|
|
|
|
| CppClass _, CppClass _
|
|
|
|
| JavaClass _, JavaClass _
|
|
|
|
| ObjcClass _, ObjcClass _
|
|
|
|
| ObjcProtocol _, ObjcProtocol _ ->
|
|
|
|
true
|
|
|
|
| _ ->
|
|
|
|
false
|
|
|
|
|
|
|
|
|
|
|
|
module C = struct
|
|
|
|
let from_qual_name qual_name = CStruct qual_name
|
|
|
|
|
|
|
|
let from_string name_str = QualifiedCppName.of_qual_string name_str |> from_qual_name
|
|
|
|
|
|
|
|
let union_from_qual_name qual_name = CUnion qual_name
|
|
|
|
end
|
|
|
|
|
|
|
|
module Java = struct
|
|
|
|
module Split = struct
|
|
|
|
(** e.g. {type_name="int"; package=None} for primitive types
|
|
|
|
* or {type_name="PrintWriter"; package=Some "java.io"} for objects.
|
|
|
|
*)
|
|
|
|
type t = {package: string option; type_name: string}
|
|
|
|
|
|
|
|
let make ?package type_name = {type_name; package}
|
|
|
|
|
|
|
|
(** Given a package.class_name string, it looks for the latest dot and split the string
|
|
|
|
in two (package, class_name) *)
|
|
|
|
let of_string package_classname =
|
|
|
|
match String.rsplit2 package_classname ~on:'.' with
|
|
|
|
| Some (package, type_name) ->
|
|
|
|
{type_name; package= Some package}
|
|
|
|
| None ->
|
|
|
|
{type_name= package_classname; package= None}
|
|
|
|
|
|
|
|
|
|
|
|
let package {package} = package
|
|
|
|
|
|
|
|
let type_name {type_name} = type_name
|
|
|
|
|
|
|
|
let java_lang_object = make ~package:"java.lang" "Object"
|
|
|
|
|
|
|
|
let java_lang_string = make ~package:"java.lang" "String"
|
|
|
|
|
|
|
|
let void = make "void"
|
|
|
|
end
|
|
|
|
|
|
|
|
let from_string name_str = JavaClass (Mangled.from_string name_str)
|
|
|
|
|
|
|
|
let from_package_class package_name class_name =
|
|
|
|
if String.equal package_name "" then from_string class_name
|
|
|
|
else from_string (package_name ^ "." ^ class_name)
|
|
|
|
|
|
|
|
|
|
|
|
let is_class = function JavaClass _ -> true | _ -> false
|
|
|
|
|
|
|
|
let java_io_serializable = from_string "java.io.Serializable"
|
|
|
|
|
|
|
|
let java_lang_class = from_string "java.lang.Class"
|
|
|
|
|
|
|
|
let java_lang_cloneable = from_string "java.lang.Cloneable"
|
|
|
|
|
|
|
|
let java_lang_object = from_string "java.lang.Object"
|
|
|
|
|
|
|
|
let split_typename typename = Split.of_string (name typename)
|
|
|
|
|
|
|
|
let get_outer_class class_name =
|
|
|
|
let {Split.package; type_name} = split_typename class_name in
|
|
|
|
match String.rsplit2 ~on:'$' type_name with
|
|
|
|
| Some (parent_class, _) ->
|
|
|
|
Some (from_package_class (Option.value ~default:"" package) parent_class)
|
|
|
|
| None ->
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
|
|
let is_anonymous_inner_class_name class_name =
|
|
|
|
let class_name_no_package = Split.type_name (split_typename class_name) in
|
|
|
|
match String.rsplit2 class_name_no_package ~on:'$' with
|
|
|
|
| Some (_, s) ->
|
|
|
|
let is_int =
|
|
|
|
try
|
|
|
|
ignore (int_of_string (String.strip s)) ;
|
|
|
|
true
|
|
|
|
with Failure _ -> false
|
|
|
|
in
|
|
|
|
is_int
|
|
|
|
| None ->
|
|
|
|
false
|
|
|
|
|
|
|
|
|
|
|
|
let is_external_classname name_string =
|
|
|
|
let {Split.package} = Split.of_string name_string in
|
|
|
|
Option.exists ~f:Config.java_package_is_external package
|
|
|
|
|
|
|
|
|
|
|
|
let is_external t = is_external_classname (name t)
|
|
|
|
end
|
|
|
|
|
|
|
|
module Cpp = struct
|
|
|
|
let from_qual_name template_spec_info qual_name = CppClass (qual_name, template_spec_info)
|
|
|
|
|
|
|
|
let is_class = function CppClass _ -> true | _ -> false
|
|
|
|
end
|
|
|
|
|
|
|
|
module Objc = struct
|
|
|
|
let from_qual_name qual_name = ObjcClass qual_name
|
|
|
|
|
|
|
|
let from_string name_str = QualifiedCppName.of_qual_string name_str |> from_qual_name
|
|
|
|
|
|
|
|
let protocol_from_qual_name qual_name = ObjcProtocol qual_name
|
|
|
|
|
|
|
|
let is_class = function ObjcClass _ -> true | _ -> false
|
|
|
|
end
|
|
|
|
|
|
|
|
module Set = Caml.Set.Make (struct
|
|
|
|
type nonrec t = t
|
|
|
|
|
|
|
|
let compare = compare
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
(** dump a type with all the details. *)
|
|
|
|
let d_full (t : t) = L.d_pp_with_pe pp_full t
|
|
|
|
|
|
|
|
(** dump a list of types. *)
|
|
|
|
let d_list (tl : t list) =
|
|
|
|
let pp pe = Pp.seq (pp pe) in
|
|
|
|
L.d_pp_with_pe pp tl
|
|
|
|
|
|
|
|
|
|
|
|
let name typ = match typ.desc with Tstruct name -> Some name | _ -> None
|
|
|
|
|
|
|
|
let unsome s = function
|
|
|
|
| Some default_typ ->
|
|
|
|
default_typ
|
|
|
|
| None ->
|
|
|
|
L.internal_error "No default typ in %s@." s ;
|
|
|
|
assert false
|
|
|
|
|
|
|
|
|
|
|
|
(** turn a *T into a T. fails if [typ] is not a pointer type *)
|
|
|
|
let strip_ptr typ = match typ.desc with Tptr (t, _) -> t | _ -> assert false
|
|
|
|
|
|
|
|
(** If an array type, return the type of the element.
|
|
|
|
If not, return the default type if given, otherwise raise an exception *)
|
|
|
|
let array_elem default_opt typ =
|
|
|
|
match typ.desc with Tarray {elt} -> elt | _ -> unsome "array_elem" default_opt
|
|
|
|
|
|
|
|
|
|
|
|
let is_class_of_kind check_fun typ =
|
|
|
|
match typ.desc with Tstruct tname -> check_fun tname | _ -> false
|
|
|
|
|
|
|
|
|
|
|
|
let is_objc_class = is_class_of_kind Name.Objc.is_class
|
|
|
|
|
|
|
|
let is_cpp_class = is_class_of_kind Name.Cpp.is_class
|
|
|
|
|
|
|
|
let is_pointer typ = match typ.desc with Tptr _ -> true | _ -> false
|
|
|
|
|
|
|
|
let is_reference typ = match typ.desc with Tptr (_, Pk_reference) -> true | _ -> false
|
|
|
|
|
|
|
|
let is_struct typ = match typ.desc with Tstruct _ -> true | _ -> false
|
|
|
|
|
|
|
|
let is_pointer_to_cpp_class typ = match typ.desc with Tptr (t, _) -> is_cpp_class t | _ -> false
|
|
|
|
|
|
|
|
let is_pointer_to_void typ = match typ.desc with Tptr ({desc= Tvoid}, _) -> true | _ -> false
|
|
|
|
|
|
|
|
let is_pointer_to_int typ = match typ.desc with Tptr ({desc= Tint _}, _) -> true | _ -> false
|
|
|
|
|
|
|
|
let is_int typ = match typ.desc with Tint _ -> true | _ -> false
|
|
|
|
|
|
|
|
let is_unsigned_int typ = match typ.desc with Tint ikind -> ikind_is_unsigned ikind | _ -> false
|
|
|
|
|
|
|
|
let is_char typ = match typ.desc with Tint ikind -> ikind_is_char ikind | _ -> false
|
|
|
|
|
|
|
|
let has_block_prefix s =
|
|
|
|
match Str.split_delim (Str.regexp_string Config.anonymous_block_prefix) s with
|
|
|
|
| _ :: _ :: _ ->
|
|
|
|
true
|
|
|
|
| _ ->
|
|
|
|
false
|
|
|
|
|
|
|
|
|
|
|
|
type typ = t
|
|
|
|
|
|
|
|
module Procname = struct
|
|
|
|
(** Level of verbosity of some to_string functions. *)
|
|
|
|
type detail_level = Verbose | Non_verbose | Simple [@@deriving compare]
|
|
|
|
|
|
|
|
let equal_detail_level = [%compare.equal: detail_level]
|
|
|
|
|
|
|
|
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 = Name.Java.Split.t = {package: string option; type_name: string}
|
|
|
|
[@@deriving compare]
|
|
|
|
|
|
|
|
let java_void = {package= None; type_name= "void"}
|
|
|
|
|
|
|
|
(** Type of java procedure names. *)
|
|
|
|
type t =
|
|
|
|
{ method_name: string
|
|
|
|
; parameters: java_type list
|
|
|
|
; class_name: 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}
|
|
|
|
|
|
|
|
|
|
|
|
(** A type is a pair (package, type_name) that is translated in a string package.type_name *)
|
|
|
|
let pp_type_verbosity verbosity fmt p =
|
|
|
|
match p with
|
|
|
|
| {package= Some package; type_name} when is_verbose verbosity ->
|
|
|
|
F.fprintf fmt "%s.%s" package type_name
|
|
|
|
| {type_name} ->
|
|
|
|
F.pp_print_string fmt type_name
|
|
|
|
|
|
|
|
|
|
|
|
(** 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 = Name.Java.Split.of_string (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 = Name.name j.class_name
|
|
|
|
|
|
|
|
let get_class_type_name j = j.class_name
|
|
|
|
|
|
|
|
let get_simple_class_name j = Name.Java.Split.(j |> get_class_name |> of_string |> type_name)
|
|
|
|
|
|
|
|
let get_package j = 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 (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)
|
|
|
|
(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" ->
|
|
|
|
mk Tvoid
|
|
|
|
| "int" ->
|
|
|
|
mk (Tint IInt)
|
|
|
|
| "byte" ->
|
|
|
|
mk (Tint IShort)
|
|
|
|
| "short" ->
|
|
|
|
mk (Tint IShort)
|
|
|
|
| "boolean" ->
|
|
|
|
mk (Tint IBool)
|
|
|
|
| "char" ->
|
|
|
|
mk (Tint IChar)
|
|
|
|
| "long" ->
|
|
|
|
mk (Tint ILong)
|
|
|
|
| "float" ->
|
|
|
|
mk (Tfloat FFloat)
|
|
|
|
| "double" ->
|
|
|
|
mk (Tfloat FDouble)
|
|
|
|
| typ_str when String.contains typ_str '[' ->
|
|
|
|
let stripped_typ = String.sub typ_str ~pos:0 ~len:(String.length typ_str - 2) in
|
|
|
|
mk (Tptr (mk_array (java_from_string stripped_typ), Pk_pointer))
|
|
|
|
| typ_str ->
|
|
|
|
mk (Tstruct (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 _ -> 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} =
|
|
|
|
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} =
|
|
|
|
match List.last parameters with Some {type_name= "java.lang.Object[]"} -> true | _ -> false
|
|
|
|
|
|
|
|
|
|
|
|
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 = 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.T.desc with T.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 (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," (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: Name.t
|
|
|
|
; kind: kind
|
|
|
|
; method_name: string
|
|
|
|
; parameters: Parameter.clang_parameter list
|
|
|
|
; template_args: 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 = Name.name objc_cpp.class_name
|
|
|
|
|
|
|
|
let get_class_type_name objc_cpp = objc_cpp.class_name
|
|
|
|
|
|
|
|
let get_class_qualifiers objc_cpp = 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 is_operator_equal {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" (Name.name osig.class_name) osig.method_name
|
|
|
|
| Verbose ->
|
|
|
|
F.fprintf fmt "%s::%s%a%a" (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: 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 : 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 -> 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)
|
|
|
|
end
|
|
|
|
|
|
|
|
module Fieldname = struct
|
[typ][fieldname] make java representation more sharing friendly and typesafe
Summary:
The `Typ.FIeldname` module has many issues. Among those:
- It has 5 different string/printing functions and most of them do radically different things in Java and in Clang.
- There is no type safety: creating a Clang field and calling a Java function on it will lead to a crash (`rindex_exn` etc, there are usually no dots in Clang fields).
- It uses a single string for Java fields, containing the package, the class and the field, e.g., `java.lang.Object.field`. This is wasteful, because
- there is no sharing of strings for packages/classes, and,
- string operations need to be performed every time we need the field or the class or the package alone.
This diff preserves the behaviour of the module's interface, so the API problems remain.
However, by using a saner representation for Java fields we can get small performance and large memory gains (the type environment in Java is much smaller, about 30-40%).
In addition, many functions on clang fields would previously do string manipulations (look for `.` and split on it) before returning the final field unchanged -- now they use the type of the field for that.
Reviewed By: jvillard
Differential Revision: D18908864
fbshipit-source-id: a72d847cc
5 years ago
|
|
|
type t =
|
|
|
|
| Clang of {class_name: Name.t; field_name: string}
|
|
|
|
| Java of {class_name: string; field_name: string}
|
|
|
|
[@@deriving compare, equal]
|
|
|
|
|
|
|
|
let is_java = function Java _ -> true | Clang _ -> false
|
|
|
|
|
|
|
|
module T = struct
|
|
|
|
type nonrec t = t
|
|
|
|
|
|
|
|
let compare = compare
|
|
|
|
end
|
|
|
|
|
|
|
|
module Set = Caml.Set.Make (T)
|
|
|
|
module Map = Caml.Map.Make (T)
|
|
|
|
|
[typ][fieldname] make java representation more sharing friendly and typesafe
Summary:
The `Typ.FIeldname` module has many issues. Among those:
- It has 5 different string/printing functions and most of them do radically different things in Java and in Clang.
- There is no type safety: creating a Clang field and calling a Java function on it will lead to a crash (`rindex_exn` etc, there are usually no dots in Clang fields).
- It uses a single string for Java fields, containing the package, the class and the field, e.g., `java.lang.Object.field`. This is wasteful, because
- there is no sharing of strings for packages/classes, and,
- string operations need to be performed every time we need the field or the class or the package alone.
This diff preserves the behaviour of the module's interface, so the API problems remain.
However, by using a saner representation for Java fields we can get small performance and large memory gains (the type environment in Java is much smaller, about 30-40%).
In addition, many functions on clang fields would previously do string manipulations (look for `.` and split on it) before returning the final field unchanged -- now they use the type of the field for that.
Reviewed By: jvillard
Differential Revision: D18908864
fbshipit-source-id: a72d847cc
5 years ago
|
|
|
let dot_join s1 s2 = String.concat ~sep:"." [s1; s2]
|
|
|
|
|
[typ][fieldname] make java representation more sharing friendly and typesafe
Summary:
The `Typ.FIeldname` module has many issues. Among those:
- It has 5 different string/printing functions and most of them do radically different things in Java and in Clang.
- There is no type safety: creating a Clang field and calling a Java function on it will lead to a crash (`rindex_exn` etc, there are usually no dots in Clang fields).
- It uses a single string for Java fields, containing the package, the class and the field, e.g., `java.lang.Object.field`. This is wasteful, because
- there is no sharing of strings for packages/classes, and,
- string operations need to be performed every time we need the field or the class or the package alone.
This diff preserves the behaviour of the module's interface, so the API problems remain.
However, by using a saner representation for Java fields we can get small performance and large memory gains (the type environment in Java is much smaller, about 30-40%).
In addition, many functions on clang fields would previously do string manipulations (look for `.` and split on it) before returning the final field unchanged -- now they use the type of the field for that.
Reviewed By: jvillard
Differential Revision: D18908864
fbshipit-source-id: a72d847cc
5 years ago
|
|
|
let to_string = function
|
|
|
|
| Java {class_name; field_name} when String.is_empty class_name ->
|
|
|
|
field_name
|
|
|
|
| Java {class_name; field_name} ->
|
|
|
|
dot_join class_name field_name
|
|
|
|
| Clang {field_name} ->
|
|
|
|
field_name
|
|
|
|
|
|
|
|
|
|
|
|
let to_simplified_string = function
|
|
|
|
| Java {class_name; field_name} ->
|
|
|
|
String.rsplit2 class_name ~on:'.'
|
|
|
|
|> Option.value_map ~default:field_name ~f:(fun (_, class_only) ->
|
|
|
|
dot_join class_only field_name )
|
|
|
|
| Clang {field_name} ->
|
|
|
|
field_name
|
|
|
|
|
|
|
|
|
[typ][fieldname] make java representation more sharing friendly and typesafe
Summary:
The `Typ.FIeldname` module has many issues. Among those:
- It has 5 different string/printing functions and most of them do radically different things in Java and in Clang.
- There is no type safety: creating a Clang field and calling a Java function on it will lead to a crash (`rindex_exn` etc, there are usually no dots in Clang fields).
- It uses a single string for Java fields, containing the package, the class and the field, e.g., `java.lang.Object.field`. This is wasteful, because
- there is no sharing of strings for packages/classes, and,
- string operations need to be performed every time we need the field or the class or the package alone.
This diff preserves the behaviour of the module's interface, so the API problems remain.
However, by using a saner representation for Java fields we can get small performance and large memory gains (the type environment in Java is much smaller, about 30-40%).
In addition, many functions on clang fields would previously do string manipulations (look for `.` and split on it) before returning the final field unchanged -- now they use the type of the field for that.
Reviewed By: jvillard
Differential Revision: D18908864
fbshipit-source-id: a72d847cc
5 years ago
|
|
|
let to_full_string = function
|
|
|
|
| Clang {class_name; field_name} ->
|
|
|
|
Name.to_string class_name ^ "::" ^ field_name
|
[typ][fieldname] make java representation more sharing friendly and typesafe
Summary:
The `Typ.FIeldname` module has many issues. Among those:
- It has 5 different string/printing functions and most of them do radically different things in Java and in Clang.
- There is no type safety: creating a Clang field and calling a Java function on it will lead to a crash (`rindex_exn` etc, there are usually no dots in Clang fields).
- It uses a single string for Java fields, containing the package, the class and the field, e.g., `java.lang.Object.field`. This is wasteful, because
- there is no sharing of strings for packages/classes, and,
- string operations need to be performed every time we need the field or the class or the package alone.
This diff preserves the behaviour of the module's interface, so the API problems remain.
However, by using a saner representation for Java fields we can get small performance and large memory gains (the type environment in Java is much smaller, about 30-40%).
In addition, many functions on clang fields would previously do string manipulations (look for `.` and split on it) before returning the final field unchanged -- now they use the type of the field for that.
Reviewed By: jvillard
Differential Revision: D18908864
fbshipit-source-id: a72d847cc
5 years ago
|
|
|
| Java {class_name; field_name} ->
|
|
|
|
dot_join class_name field_name
|
|
|
|
|
|
|
|
|
[typ][fieldname] make java representation more sharing friendly and typesafe
Summary:
The `Typ.FIeldname` module has many issues. Among those:
- It has 5 different string/printing functions and most of them do radically different things in Java and in Clang.
- There is no type safety: creating a Clang field and calling a Java function on it will lead to a crash (`rindex_exn` etc, there are usually no dots in Clang fields).
- It uses a single string for Java fields, containing the package, the class and the field, e.g., `java.lang.Object.field`. This is wasteful, because
- there is no sharing of strings for packages/classes, and,
- string operations need to be performed every time we need the field or the class or the package alone.
This diff preserves the behaviour of the module's interface, so the API problems remain.
However, by using a saner representation for Java fields we can get small performance and large memory gains (the type environment in Java is much smaller, about 30-40%).
In addition, many functions on clang fields would previously do string manipulations (look for `.` and split on it) before returning the final field unchanged -- now they use the type of the field for that.
Reviewed By: jvillard
Differential Revision: D18908864
fbshipit-source-id: a72d847cc
5 years ago
|
|
|
let to_flat_string = function Java {field_name} -> field_name | Clang {field_name} -> field_name
|
|
|
|
|
[typ][fieldname] make java representation more sharing friendly and typesafe
Summary:
The `Typ.FIeldname` module has many issues. Among those:
- It has 5 different string/printing functions and most of them do radically different things in Java and in Clang.
- There is no type safety: creating a Clang field and calling a Java function on it will lead to a crash (`rindex_exn` etc, there are usually no dots in Clang fields).
- It uses a single string for Java fields, containing the package, the class and the field, e.g., `java.lang.Object.field`. This is wasteful, because
- there is no sharing of strings for packages/classes, and,
- string operations need to be performed every time we need the field or the class or the package alone.
This diff preserves the behaviour of the module's interface, so the API problems remain.
However, by using a saner representation for Java fields we can get small performance and large memory gains (the type environment in Java is much smaller, about 30-40%).
In addition, many functions on clang fields would previously do string manipulations (look for `.` and split on it) before returning the final field unchanged -- now they use the type of the field for that.
Reviewed By: jvillard
Differential Revision: D18908864
fbshipit-source-id: a72d847cc
5 years ago
|
|
|
let pp f = function
|
|
|
|
| Java {class_name; field_name} when String.is_empty class_name ->
|
|
|
|
Format.pp_print_string f field_name
|
|
|
|
| Java {class_name; field_name} ->
|
|
|
|
F.pp_print_string f (dot_join class_name field_name)
|
|
|
|
| Clang {field_name} ->
|
|
|
|
Format.pp_print_string f field_name
|
|
|
|
|
|
|
|
|
|
|
|
let clang_get_qual_class = function
|
|
|
|
| Clang {class_name} ->
|
|
|
|
Some (Name.qual_name class_name)
|
|
|
|
| _ ->
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
|
|
module Clang = struct
|
|
|
|
let from_class_name class_name field_name = Clang {class_name; field_name}
|
|
|
|
end
|
|
|
|
|
|
|
|
module Java = struct
|
[typ][fieldname] make java representation more sharing friendly and typesafe
Summary:
The `Typ.FIeldname` module has many issues. Among those:
- It has 5 different string/printing functions and most of them do radically different things in Java and in Clang.
- There is no type safety: creating a Clang field and calling a Java function on it will lead to a crash (`rindex_exn` etc, there are usually no dots in Clang fields).
- It uses a single string for Java fields, containing the package, the class and the field, e.g., `java.lang.Object.field`. This is wasteful, because
- there is no sharing of strings for packages/classes, and,
- string operations need to be performed every time we need the field or the class or the package alone.
This diff preserves the behaviour of the module's interface, so the API problems remain.
However, by using a saner representation for Java fields we can get small performance and large memory gains (the type environment in Java is much smaller, about 30-40%).
In addition, many functions on clang fields would previously do string manipulations (look for `.` and split on it) before returning the final field unchanged -- now they use the type of the field for that.
Reviewed By: jvillard
Differential Revision: D18908864
fbshipit-source-id: a72d847cc
5 years ago
|
|
|
let from_string full_fieldname =
|
|
|
|
let class_name, field_name =
|
|
|
|
match String.rsplit2 full_fieldname ~on:'.' with
|
|
|
|
| None ->
|
|
|
|
("", full_fieldname)
|
|
|
|
| Some split ->
|
|
|
|
split
|
|
|
|
in
|
|
|
|
Java {class_name; field_name}
|
|
|
|
|
[typ][fieldname] make java representation more sharing friendly and typesafe
Summary:
The `Typ.FIeldname` module has many issues. Among those:
- It has 5 different string/printing functions and most of them do radically different things in Java and in Clang.
- There is no type safety: creating a Clang field and calling a Java function on it will lead to a crash (`rindex_exn` etc, there are usually no dots in Clang fields).
- It uses a single string for Java fields, containing the package, the class and the field, e.g., `java.lang.Object.field`. This is wasteful, because
- there is no sharing of strings for packages/classes, and,
- string operations need to be performed every time we need the field or the class or the package alone.
This diff preserves the behaviour of the module's interface, so the API problems remain.
However, by using a saner representation for Java fields we can get small performance and large memory gains (the type environment in Java is much smaller, about 30-40%).
In addition, many functions on clang fields would previously do string manipulations (look for `.` and split on it) before returning the final field unchanged -- now they use the type of the field for that.
Reviewed By: jvillard
Differential Revision: D18908864
fbshipit-source-id: a72d847cc
5 years ago
|
|
|
|
|
|
|
let is_captured_parameter = function
|
|
|
|
| Java {field_name} ->
|
|
|
|
String.is_prefix ~prefix:"val$" field_name
|
|
|
|
| Clang _ ->
|
|
|
|
false
|
|
|
|
|
|
|
|
|
[typ][fieldname] make java representation more sharing friendly and typesafe
Summary:
The `Typ.FIeldname` module has many issues. Among those:
- It has 5 different string/printing functions and most of them do radically different things in Java and in Clang.
- There is no type safety: creating a Clang field and calling a Java function on it will lead to a crash (`rindex_exn` etc, there are usually no dots in Clang fields).
- It uses a single string for Java fields, containing the package, the class and the field, e.g., `java.lang.Object.field`. This is wasteful, because
- there is no sharing of strings for packages/classes, and,
- string operations need to be performed every time we need the field or the class or the package alone.
This diff preserves the behaviour of the module's interface, so the API problems remain.
However, by using a saner representation for Java fields we can get small performance and large memory gains (the type environment in Java is much smaller, about 30-40%).
In addition, many functions on clang fields would previously do string manipulations (look for `.` and split on it) before returning the final field unchanged -- now they use the type of the field for that.
Reviewed By: jvillard
Differential Revision: D18908864
fbshipit-source-id: a72d847cc
5 years ago
|
|
|
let get_class = function
|
|
|
|
| Java {class_name} ->
|
|
|
|
class_name
|
|
|
|
| Clang _ as field ->
|
|
|
|
L.die InternalError "get_class: fieldname %a is not Java@\n" pp field
|
|
|
|
|
|
|
|
|
[typ][fieldname] make java representation more sharing friendly and typesafe
Summary:
The `Typ.FIeldname` module has many issues. Among those:
- It has 5 different string/printing functions and most of them do radically different things in Java and in Clang.
- There is no type safety: creating a Clang field and calling a Java function on it will lead to a crash (`rindex_exn` etc, there are usually no dots in Clang fields).
- It uses a single string for Java fields, containing the package, the class and the field, e.g., `java.lang.Object.field`. This is wasteful, because
- there is no sharing of strings for packages/classes, and,
- string operations need to be performed every time we need the field or the class or the package alone.
This diff preserves the behaviour of the module's interface, so the API problems remain.
However, by using a saner representation for Java fields we can get small performance and large memory gains (the type environment in Java is much smaller, about 30-40%).
In addition, many functions on clang fields would previously do string manipulations (look for `.` and split on it) before returning the final field unchanged -- now they use the type of the field for that.
Reviewed By: jvillard
Differential Revision: D18908864
fbshipit-source-id: a72d847cc
5 years ago
|
|
|
let get_field = function
|
|
|
|
| Java {field_name} ->
|
|
|
|
field_name
|
|
|
|
| Clang _ as field ->
|
|
|
|
L.die InternalError "get_field: fieldname %a is not Java@\n" pp field
|
|
|
|
|
|
|
|
|
[typ][fieldname] make java representation more sharing friendly and typesafe
Summary:
The `Typ.FIeldname` module has many issues. Among those:
- It has 5 different string/printing functions and most of them do radically different things in Java and in Clang.
- There is no type safety: creating a Clang field and calling a Java function on it will lead to a crash (`rindex_exn` etc, there are usually no dots in Clang fields).
- It uses a single string for Java fields, containing the package, the class and the field, e.g., `java.lang.Object.field`. This is wasteful, because
- there is no sharing of strings for packages/classes, and,
- string operations need to be performed every time we need the field or the class or the package alone.
This diff preserves the behaviour of the module's interface, so the API problems remain.
However, by using a saner representation for Java fields we can get small performance and large memory gains (the type environment in Java is much smaller, about 30-40%).
In addition, many functions on clang fields would previously do string manipulations (look for `.` and split on it) before returning the final field unchanged -- now they use the type of the field for that.
Reviewed By: jvillard
Differential Revision: D18908864
fbshipit-source-id: a72d847cc
5 years ago
|
|
|
let is_outer_instance = function
|
|
|
|
| Java {field_name} ->
|
|
|
|
let this = "this$" in
|
|
|
|
let last_char = field_name.[String.length field_name - 1] in
|
|
|
|
(last_char >= '0' && last_char <= '9')
|
|
|
|
&& String.is_suffix field_name ~suffix:(this ^ String.of_char last_char)
|
|
|
|
| Clang _ ->
|
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module Struct = struct
|
|
|
|
type field = Fieldname.t * T.t * Annot.Item.t [@@deriving compare]
|
|
|
|
|
|
|
|
type fields = field list
|
|
|
|
|
|
|
|
(** Type for a structured value. *)
|
|
|
|
type t =
|
|
|
|
{ fields: fields (** non-static fields *)
|
|
|
|
; statics: fields (** static fields *)
|
|
|
|
; supers: Name.t list (** superclasses *)
|
|
|
|
; methods: Procname.t list (** methods defined *)
|
|
|
|
; exported_objc_methods: Procname.t list (** methods in ObjC interface, subset of [methods] *)
|
|
|
|
; annots: Annot.Item.t (** annotations *)
|
|
|
|
; dummy: bool (** dummy struct for class including static method *) }
|
|
|
|
|
|
|
|
type lookup = Name.t -> t option
|
|
|
|
|
|
|
|
let pp_field pe f (field_name, typ, ann) =
|
|
|
|
F.fprintf f "@\n\t\t%a %a %a" (pp_full pe) typ Fieldname.pp field_name Annot.Item.pp ann
|
|
|
|
|
|
|
|
|
|
|
|
let pp pe name f {fields; supers; methods; exported_objc_methods; annots} =
|
|
|
|
if Config.debug_mode then
|
|
|
|
(* change false to true to print the details of struct *)
|
|
|
|
F.fprintf f
|
|
|
|
"%a @\n\
|
|
|
|
\tfields: {%a@\n\
|
|
|
|
\t}@\n\
|
|
|
|
\tsupers: {%a@\n\
|
|
|
|
\t}@\n\
|
|
|
|
\tmethods: {%a@\n\
|
|
|
|
\t}@\n\
|
|
|
|
\texported_obj_methods: {%a@\n\
|
|
|
|
\t}@\n\
|
|
|
|
\tannots: {%a@\n\
|
|
|
|
\t}"
|
|
|
|
Name.pp name
|
|
|
|
(Pp.seq (pp_field pe))
|
|
|
|
fields
|
|
|
|
(Pp.seq (fun f n -> F.fprintf f "@\n\t\t%a" Name.pp n))
|
|
|
|
supers
|
|
|
|
(Pp.seq (fun f m -> F.fprintf f "@\n\t\t%a" Procname.pp m))
|
|
|
|
methods
|
|
|
|
(Pp.seq (fun f m -> F.fprintf f "@\n\t\t%a" Procname.pp m))
|
|
|
|
exported_objc_methods Annot.Item.pp annots
|
|
|
|
else Name.pp f name
|
|
|
|
|
|
|
|
|
|
|
|
let internal_mk_struct ?default ?fields ?statics ?methods ?exported_objc_methods ?supers ?annots
|
|
|
|
?dummy () =
|
|
|
|
let default_ =
|
|
|
|
{ fields= []
|
|
|
|
; statics= []
|
|
|
|
; methods= []
|
|
|
|
; exported_objc_methods= []
|
|
|
|
; supers= []
|
|
|
|
; annots= Annot.Item.empty
|
|
|
|
; dummy= false }
|
|
|
|
in
|
|
|
|
let mk_struct_ ?(default = default_) ?(fields = default.fields) ?(statics = default.statics)
|
|
|
|
?(methods = default.methods) ?(exported_objc_methods = default.exported_objc_methods)
|
|
|
|
?(supers = default.supers) ?(annots = default.annots) ?(dummy = default.dummy) () =
|
|
|
|
{fields; statics; methods; exported_objc_methods; supers; annots; dummy}
|
|
|
|
in
|
|
|
|
mk_struct_ ?default ?fields ?statics ?methods ?exported_objc_methods ?supers ?annots ?dummy ()
|
|
|
|
|
|
|
|
|
|
|
|
(** the element typ of the final extensible array in the given typ, if any *)
|
|
|
|
let rec get_extensible_array_element_typ ~lookup (typ : T.t) =
|
|
|
|
match typ.desc with
|
|
|
|
| Tarray {elt} ->
|
|
|
|
Some elt
|
|
|
|
| Tstruct name -> (
|
|
|
|
match lookup name with
|
|
|
|
| Some {fields} -> (
|
|
|
|
match List.last fields with
|
|
|
|
| Some (_, fld_typ, _) ->
|
|
|
|
get_extensible_array_element_typ ~lookup fld_typ
|
|
|
|
| None ->
|
|
|
|
None )
|
|
|
|
| None ->
|
|
|
|
None )
|
|
|
|
| _ ->
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
|
|
(** If a struct type with field f, return the type of f. If not, return the default *)
|
|
|
|
let fld_typ ~lookup ~default fn (typ : T.t) =
|
|
|
|
match typ.desc with
|
|
|
|
| Tstruct name -> (
|
|
|
|
match lookup name with
|
|
|
|
| Some {fields} ->
|
|
|
|
List.find ~f:(fun (f, _, _) -> Fieldname.equal f fn) fields
|
|
|
|
|> Option.value_map ~f:snd3 ~default
|
|
|
|
| None ->
|
|
|
|
default )
|
|
|
|
| _ ->
|
|
|
|
default
|
|
|
|
|
|
|
|
|
|
|
|
let get_field_type_and_annotation ~lookup fn (typ : T.t) =
|
|
|
|
match typ.desc with
|
|
|
|
| Tstruct name | Tptr ({desc= Tstruct name}, _) -> (
|
|
|
|
match lookup name with
|
|
|
|
| Some {fields; statics} ->
|
|
|
|
List.find_map
|
|
|
|
~f:(fun (f, t, a) ->
|
|
|
|
match Fieldname.equal f fn with true -> Some (t, a) | false -> None )
|
|
|
|
(fields @ statics)
|
|
|
|
| None ->
|
|
|
|
None )
|
|
|
|
| _ ->
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
|
|
let is_dummy {dummy} = dummy
|
|
|
|
end
|