|
|
|
(*
|
|
|
|
* Copyright (c) 2013 - present Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*)
|
|
|
|
|
|
|
|
open! IStd
|
|
|
|
|
|
|
|
(** General utility functions such as functions on lists *)
|
|
|
|
|
|
|
|
module L = Logging
|
|
|
|
module F = Format
|
|
|
|
|
|
|
|
type var_info = Clang_ast_t.decl_info * Clang_ast_t.qual_type * Clang_ast_t.var_decl_info * bool
|
|
|
|
|
|
|
|
let rec swap_elements_list l =
|
|
|
|
match l with
|
|
|
|
| el1 :: el2 :: rest ->
|
|
|
|
el2 :: el1 :: swap_elements_list rest
|
|
|
|
| [] ->
|
|
|
|
[]
|
|
|
|
| _ ->
|
|
|
|
assert false
|
|
|
|
|
|
|
|
|
|
|
|
let rec string_from_list l =
|
|
|
|
match l with [] -> "" | [item] -> item | item :: l' -> item ^ " " ^ string_from_list l'
|
|
|
|
|
|
|
|
|
|
|
|
let rec append_no_duplicates eq list1 list2 =
|
|
|
|
match list2 with
|
|
|
|
| el :: rest2 ->
|
|
|
|
if List.mem ~equal:eq list1 el then append_no_duplicates eq list1 rest2
|
|
|
|
else append_no_duplicates eq list1 rest2 @ [el]
|
|
|
|
| [] ->
|
|
|
|
list1
|
|
|
|
|
|
|
|
|
|
|
|
let append_no_duplicates_csu list1 list2 = append_no_duplicates Typ.Name.equal list1 list2
|
|
|
|
|
|
|
|
let append_no_duplicates_annotations list1 list2 =
|
|
|
|
let eq (annot1, _) (annot2, _) = String.equal annot1.Annot.class_name annot2.Annot.class_name in
|
|
|
|
append_no_duplicates eq list1 list2
|
|
|
|
|
|
|
|
|
|
|
|
let add_no_duplicates_fields field_tuple l =
|
|
|
|
let rec replace_field field_tuple l found =
|
|
|
|
match (field_tuple, l) with
|
|
|
|
| (field, typ, annot), ((old_field, old_typ, old_annot) as old_field_tuple) :: rest ->
|
|
|
|
let ret_list, ret_found = replace_field field_tuple rest found in
|
|
|
|
if Typ.Fieldname.equal field old_field && Typ.equal typ old_typ then
|
|
|
|
let annotations = append_no_duplicates_annotations annot old_annot in
|
|
|
|
((field, typ, annotations) :: ret_list, true)
|
|
|
|
else (old_field_tuple :: ret_list, ret_found)
|
|
|
|
| _, [] ->
|
|
|
|
([], found)
|
|
|
|
in
|
|
|
|
let new_list, found = replace_field field_tuple l false in
|
|
|
|
if found then new_list else field_tuple :: l
|
|
|
|
|
|
|
|
|
|
|
|
let rec append_no_duplicates_fields list1 list2 =
|
|
|
|
match list1 with
|
|
|
|
| field_tuple :: rest ->
|
|
|
|
let updated_list2 = append_no_duplicates_fields rest list2 in
|
|
|
|
add_no_duplicates_fields field_tuple updated_list2
|
|
|
|
| [] ->
|
|
|
|
list2
|
|
|
|
|
|
|
|
|
|
|
|
let rec collect_list_tuples l (a, a1, b, c, d) =
|
|
|
|
match l with
|
|
|
|
| [] ->
|
|
|
|
(a, a1, b, c, d)
|
|
|
|
| (a', a1', b', c', d') :: l' ->
|
|
|
|
collect_list_tuples l' (a @ a', a1 @ a1', b @ b', c @ c', d @ d')
|
|
|
|
|
|
|
|
|
|
|
|
let is_static_var var_decl_info =
|
|
|
|
match var_decl_info.Clang_ast_t.vdi_storage_class with
|
|
|
|
| Some sc ->
|
|
|
|
String.equal sc CFrontend_config.static
|
|
|
|
| _ ->
|
|
|
|
false
|
|
|
|
|
|
|
|
|
|
|
|
let rec zip xs ys =
|
|
|
|
match (xs, ys) with [], _ | _, [] -> [] | x :: xs, y :: ys -> (x, y) :: zip xs ys
|
|
|
|
|
|
|
|
|
|
|
|
let list_range i j =
|
|
|
|
let rec aux n acc = if n < i then acc else aux (n - 1) (n :: acc) in
|
|
|
|
aux j []
|
|
|
|
|
|
|
|
|
|
|
|
let replicate n el = List.map ~f:(fun _ -> el) (list_range 0 (n - 1))
|
|
|
|
|
|
|
|
let mk_class_field_name class_tname field_name =
|
|
|
|
Typ.Fieldname.Clang.from_class_name class_tname field_name
|
|
|
|
|
|
|
|
|
|
|
|
let is_cpp_translation translation_unit_context =
|
|
|
|
let lang = translation_unit_context.CFrontend_config.lang in
|
|
|
|
CFrontend_config.equal_clang_lang lang CFrontend_config.CPP
|
|
|
|
|| CFrontend_config.equal_clang_lang lang CFrontend_config.ObjCPP
|
|
|
|
|
|
|
|
|
|
|
|
let is_objc_extension translation_unit_context =
|
|
|
|
let lang = translation_unit_context.CFrontend_config.lang in
|
|
|
|
CFrontend_config.equal_clang_lang lang CFrontend_config.ObjC
|
|
|
|
|| CFrontend_config.equal_clang_lang lang CFrontend_config.ObjCPP
|
|
|
|
|
|
|
|
|
|
|
|
let get_var_name_mangled name_info var_decl_info =
|
|
|
|
let clang_name = CAst_utils.get_qualified_name name_info |> QualifiedCppName.to_qual_string in
|
|
|
|
let param_idx_opt = var_decl_info.Clang_ast_t.vdi_parm_index_in_function in
|
|
|
|
let name_string =
|
|
|
|
match (clang_name, param_idx_opt) with
|
|
|
|
| "", Some index ->
|
|
|
|
"__param_" ^ string_of_int index
|
|
|
|
| "", None ->
|
|
|
|
CFrontend_config.incorrect_assumption
|
|
|
|
"Got both empty clang_name and None for param_idx in get_var_name_mangled (%a) (%a)"
|
|
|
|
(Pp.to_string ~f:Clang_ast_j.string_of_named_decl_info)
|
|
|
|
name_info
|
|
|
|
(Pp.to_string ~f:Clang_ast_j.string_of_var_decl_info)
|
|
|
|
var_decl_info
|
|
|
|
| _ ->
|
|
|
|
clang_name
|
|
|
|
in
|
|
|
|
let mangled =
|
|
|
|
match param_idx_opt with
|
|
|
|
| Some index ->
|
|
|
|
Mangled.mangled name_string (string_of_int index)
|
|
|
|
| None ->
|
|
|
|
Mangled.from_string name_string
|
|
|
|
in
|
|
|
|
(name_string, mangled)
|
|
|
|
|
|
|
|
|
|
|
|
let mk_sil_global_var {CFrontend_config.source_file} ?(mk_name= fun _ x -> x) named_decl_info
|
|
|
|
var_decl_info qt =
|
|
|
|
let name_string, simple_name = get_var_name_mangled named_decl_info var_decl_info in
|
|
|
|
let translation_unit =
|
|
|
|
match
|
|
|
|
(var_decl_info.Clang_ast_t.vdi_storage_class, var_decl_info.Clang_ast_t.vdi_init_expr)
|
|
|
|
with
|
|
|
|
| Some "extern", None ->
|
|
|
|
(* some compilers simply disregard "extern" when the global is given some initialisation
|
|
|
|
code, which is why we make sure that [vdi_init_expr] is None here... *)
|
|
|
|
Pvar.TUExtern
|
|
|
|
| _ ->
|
|
|
|
Pvar.TUFile source_file
|
|
|
|
in
|
|
|
|
let is_constexpr = var_decl_info.Clang_ast_t.vdi_is_const_expr in
|
|
|
|
let is_pod =
|
|
|
|
CAst_utils.get_desugared_type qt.Clang_ast_t.qt_type_ptr
|
|
|
|
|> Option.bind ~f:(function
|
|
|
|
| Clang_ast_t.RecordType (_, decl_ptr) ->
|
|
|
|
CAst_utils.get_decl decl_ptr
|
|
|
|
| _ ->
|
|
|
|
None )
|
|
|
|
|> Option.value_map ~default:true ~f:(function
|
|
|
|
| Clang_ast_t.CXXRecordDecl (_, _, _, _, _, _, _, {xrdi_is_pod})
|
|
|
|
| Clang_ast_t.ClassTemplateSpecializationDecl (_, _, _, _, _, _, _, {xrdi_is_pod}, _, _) ->
|
|
|
|
xrdi_is_pod
|
|
|
|
| _ ->
|
|
|
|
true )
|
|
|
|
in
|
|
|
|
Pvar.mk_global ~is_constexpr ~is_pod
|
|
|
|
~is_static_local:var_decl_info.Clang_ast_t.vdi_is_static_local
|
|
|
|
(mk_name name_string simple_name) translation_unit
|
|
|
|
|
|
|
|
|
|
|
|
let mk_sil_var trans_unit_ctx named_decl_info decl_info_qual_type_opt procname outer_procname =
|
|
|
|
match decl_info_qual_type_opt with
|
|
|
|
| Some (decl_info, qt, var_decl_info, should_be_mangled) ->
|
|
|
|
let name_string, simple_name = get_var_name_mangled named_decl_info var_decl_info in
|
|
|
|
if var_decl_info.Clang_ast_t.vdi_is_global then
|
|
|
|
let mk_name =
|
|
|
|
if var_decl_info.Clang_ast_t.vdi_is_static_local then
|
|
|
|
Some
|
|
|
|
(fun name_string _ ->
|
|
|
|
Mangled.from_string (Typ.Procname.to_string outer_procname ^ "_" ^ name_string))
|
|
|
|
else None
|
|
|
|
in
|
|
|
|
mk_sil_global_var trans_unit_ctx ?mk_name named_decl_info var_decl_info qt
|
|
|
|
else if not should_be_mangled then Pvar.mk simple_name procname
|
|
|
|
else
|
|
|
|
let start_location = fst decl_info.Clang_ast_t.di_source_range in
|
|
|
|
let line_opt = start_location.Clang_ast_t.sl_line in
|
|
|
|
let line_str = match line_opt with Some line -> string_of_int line | None -> "" in
|
|
|
|
let mangled = Utils.string_crc_hex32 line_str in
|
|
|
|
let mangled_name = Mangled.mangled name_string mangled in
|
|
|
|
Pvar.mk mangled_name procname
|
|
|
|
| None ->
|
|
|
|
let name_string =
|
|
|
|
CAst_utils.get_qualified_name named_decl_info |> QualifiedCppName.to_qual_string
|
|
|
|
in
|
|
|
|
Pvar.mk (Mangled.from_string name_string) procname
|
|
|
|
|