|
|
|
(*
|
|
|
|
* 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.
|
|
|
|
*)
|
|
|
|
|
|
|
|
(** Module for utility functions for the whole frontend. Includes functions for printing, *)
|
|
|
|
(** for transformations of ast nodes and general utility functions such as functions on lists *)
|
|
|
|
|
|
|
|
open Utils
|
|
|
|
open Clang_ast_t
|
|
|
|
|
|
|
|
module L = Logging
|
|
|
|
module F = Format
|
|
|
|
|
|
|
|
module Printing =
|
|
|
|
struct
|
|
|
|
let log_out fmt =
|
|
|
|
let pp = if !CFrontend_config.debug_mode then Format.fprintf else Format.ifprintf in
|
|
|
|
pp Format.std_formatter fmt
|
|
|
|
|
|
|
|
let log_err fmt =
|
|
|
|
let pp = if !CFrontend_config.debug_mode then Format.fprintf else Format.ifprintf in
|
|
|
|
pp Format.err_formatter fmt
|
|
|
|
|
|
|
|
let log_stats fmt =
|
|
|
|
let pp =
|
|
|
|
if !CFrontend_config.stats_mode || !CFrontend_config.debug_mode
|
|
|
|
then Format.fprintf else Format.ifprintf in
|
|
|
|
pp Format.std_formatter fmt
|
|
|
|
|
|
|
|
let print_tenv tenv =
|
|
|
|
Sil.tenv_iter (fun typname typ ->
|
|
|
|
match typname with
|
|
|
|
| Sil.TN_csu (Sil.Class, _) | Sil.TN_csu (Sil.Protocol, _) ->
|
|
|
|
(match typ with (Sil.Tstruct (fields, static_fields, _, cls, super_classes, methods, iann)) ->
|
|
|
|
(print_endline (
|
|
|
|
(Sil.typename_to_string typname)^"\n"^
|
|
|
|
"---> superclass and protocols "^(list_to_string (fun (csu, x) ->
|
|
|
|
let nsu = Sil.TN_csu (csu, x) in
|
|
|
|
"\t"^(Sil.typename_to_string nsu)^"\n") super_classes)^
|
|
|
|
"---> methods "^(list_to_string (fun x ->"\t"^(Procname.to_string x)^"\n") methods)^" "^
|
|
|
|
"\t---> static fields "^(list_to_string (fun (fieldname, typ, _) ->
|
|
|
|
"\t "^(Ident.fieldname_to_string fieldname)^" "^
|
|
|
|
(Sil.typ_to_string typ)^"\n") static_fields)^
|
|
|
|
"\t---> fields "^(list_to_string (fun (fieldname, typ, _) ->
|
|
|
|
"\t "^(Ident.fieldname_to_string fieldname)^" "^
|
|
|
|
(Sil.typ_to_string typ)^"\n") fields
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
| _ -> ())
|
|
|
|
| _ -> ()
|
|
|
|
) tenv
|
|
|
|
|
|
|
|
let print_tenv_struct_unions tenv =
|
|
|
|
Sil.tenv_iter (fun typname typ ->
|
|
|
|
match typname with
|
|
|
|
| Sil.TN_csu (Sil.Struct, _) | Sil.TN_csu (Sil.Union, _) ->
|
|
|
|
(match typ with
|
|
|
|
| (Sil.Tstruct (fields, static_fields, _, cls, super_classes, methods, iann)) ->
|
|
|
|
(print_endline (
|
|
|
|
(Sil.typename_to_string typname)^"\n"^
|
|
|
|
"\t---> fields "^(list_to_string (fun (fieldname, typ, _) ->
|
|
|
|
match typ with
|
|
|
|
| Sil.Tvar tname -> "tvar"^(Sil.typename_to_string tname)
|
|
|
|
| Sil.Tstruct (_, _, _, _, _, _, _) | _ ->
|
|
|
|
"\t struct "^(Ident.fieldname_to_string fieldname)^" "^
|
|
|
|
(Sil.typ_to_string typ)^"\n") fields
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
| _ -> ())
|
|
|
|
| Sil.TN_typedef typname ->
|
|
|
|
print_endline ((Mangled.to_string typname)^"-->"^(Sil.typ_to_string typ))
|
|
|
|
| _ -> ()
|
|
|
|
) tenv
|
|
|
|
|
|
|
|
let print_procedures cfg =
|
|
|
|
let procs = Cfg.get_all_procs cfg in
|
|
|
|
print_endline
|
|
|
|
(list_to_string (fun pdesc ->
|
|
|
|
let pname = Cfg.Procdesc.get_proc_name pdesc in
|
|
|
|
"name> "^
|
|
|
|
(Procname.to_string pname) ^
|
|
|
|
" defined? " ^ (string_of_bool (Cfg.Procdesc.is_defined pdesc)) ^ "\n")
|
|
|
|
procs)
|
|
|
|
|
|
|
|
let print_failure_info pointer =
|
|
|
|
L.err "AST Element> %s IN FILE> %s @.@." pointer !CFrontend_config.json
|
|
|
|
|
|
|
|
let print_nodes nodes =
|
|
|
|
list_iter (fun node -> print_endline (Cfg.Node.get_description Utils.pe_text node)) nodes
|
|
|
|
|
|
|
|
let instrs_to_string instrs =
|
|
|
|
let pp fmt () = Format.fprintf fmt "%a" (Sil.pp_instr_list Utils.pe_text) instrs in
|
|
|
|
pp_to_string pp ()
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
module Ast_utils =
|
|
|
|
struct
|
|
|
|
|
|
|
|
let string_of_decl decl =
|
|
|
|
let name = Clang_ast_proj.get_decl_kind_string decl in
|
|
|
|
let info = Clang_ast_proj.get_decl_tuple decl in
|
|
|
|
"<\"" ^ name ^ "\"> '" ^ info.Clang_ast_t.di_pointer ^ "'"
|
|
|
|
|
|
|
|
let string_of_unary_operator_kind = function
|
|
|
|
| `PostInc -> "PostInc"
|
|
|
|
| `PostDec -> "PostDec"
|
|
|
|
| `PreInc -> "PreInc"
|
|
|
|
| `PreDec -> "PreDec"
|
|
|
|
| `AddrOf -> "AddrOf"
|
|
|
|
| `Deref -> "Deref"
|
|
|
|
| `Plus -> "Plus"
|
|
|
|
| `Minus -> "Minus"
|
|
|
|
| `Not -> "Not"
|
|
|
|
| `LNot -> "LNot"
|
|
|
|
| `Real -> "Real"
|
|
|
|
| `Imag -> "Imag"
|
|
|
|
| `Extension -> "Extension"
|
|
|
|
|
|
|
|
let string_of_stmt stmt =
|
|
|
|
let name = Clang_ast_proj.get_stmt_kind_string stmt in
|
|
|
|
let info, _ = Clang_ast_proj.get_stmt_tuple stmt in
|
|
|
|
"<\"" ^ name ^ "\"> '" ^ info.Clang_ast_t.si_pointer ^ "'"
|
|
|
|
|
|
|
|
let get_stmts_from_stmt stmt =
|
|
|
|
match stmt with
|
|
|
|
| OpaqueValueExpr(_, lstmt, _, opaque_value_expr_info) ->
|
|
|
|
(match opaque_value_expr_info.Clang_ast_t.ovei_source_expr with
|
|
|
|
| Some stmt -> lstmt@[stmt]
|
|
|
|
| _ -> lstmt)
|
|
|
|
(* given that this has not been translated, looking up for variables *)
|
|
|
|
(* inside leads to inconsistencies *)
|
|
|
|
| ObjCAtCatchStmt (stmt_info, stmt_list, obj_c_message_expr_kind) ->
|
|
|
|
[]
|
|
|
|
| _ -> snd (Clang_ast_proj.get_stmt_tuple stmt)
|
|
|
|
|
|
|
|
let namespace_to_string namespace =
|
|
|
|
match namespace with
|
|
|
|
| None -> ""
|
|
|
|
| Some ns when ns ="" -> ""
|
|
|
|
| Some ns -> ns^"::"
|
|
|
|
|
|
|
|
let property_name property_impl_decl_info =
|
|
|
|
let no_property_name = "WARNING_NO_PROPERTY_NAME" in
|
|
|
|
match property_impl_decl_info.Clang_ast_t.opidi_property_decl with
|
|
|
|
| Some decl_ref ->
|
|
|
|
(match decl_ref.Clang_ast_t.dr_name with
|
|
|
|
| Some n -> n.Clang_ast_t.ni_name
|
|
|
|
| _ -> no_property_name)
|
|
|
|
| None -> no_property_name
|
|
|
|
|
|
|
|
let generated_ivar_name property_name =
|
|
|
|
"_"^property_name
|
|
|
|
|
|
|
|
let property_attribute_compare att1 att2 =
|
|
|
|
match att1, att2 with
|
|
|
|
`Readonly, `Readonly -> 0
|
|
|
|
| `Readonly, _ -> -1
|
|
|
|
| _, `Readonly -> 1
|
|
|
|
| `Assign, `Assign -> 0
|
|
|
|
| `Assign, _ -> -1
|
|
|
|
| _, `Assign -> 1
|
|
|
|
| `Readwrite, `Readwrite -> 0
|
|
|
|
| `Readwrite, _ -> -1
|
|
|
|
| _, `Readwrite -> 1
|
|
|
|
| `Retain, `Retain -> 0
|
|
|
|
| `Retain, _ -> -1
|
|
|
|
| _, `Retain -> 1
|
|
|
|
| `Copy, `Copy -> 0
|
|
|
|
| `Copy, _ -> -1
|
|
|
|
| _, `Copy -> 1
|
|
|
|
| `Nonatomic, `Nonatomic -> 0
|
|
|
|
| `Nonatomic, _ -> -1
|
|
|
|
| _, `Nonatomic -> 1
|
|
|
|
| `Atomic, `Atomic -> 0
|
|
|
|
| `Atomic, _ -> 1
|
|
|
|
| _, `Atomic -> 1
|
|
|
|
| `Weak, `Weak -> 0
|
|
|
|
| `Weak, _ -> -1
|
|
|
|
| _, `Weak -> 1
|
|
|
|
| `Strong, `Strong -> 0
|
|
|
|
| `Strong, _ -> -1
|
|
|
|
| _, `Strong -> 1
|
|
|
|
| `Unsafe_unretained, `Unsafe_unretained -> 0
|
|
|
|
| `Unsafe_unretained, _ -> -1
|
|
|
|
| _, `Unsafe_unretained -> 1
|
|
|
|
| `Getter _, `Getter _ -> 0
|
|
|
|
| `Getter _, _ -> -1
|
|
|
|
| _, `Getter _ -> 1
|
|
|
|
| `Setter _, `Setter _ -> 0
|
|
|
|
|
|
|
|
let property_attribute_eq att1 att2 =
|
|
|
|
property_attribute_compare att1 att2 = 0
|
|
|
|
|
|
|
|
let get_memory_management_attributes () =
|
|
|
|
[`Assign; `Retain; `Copy; `Weak; `Strong; `Unsafe_unretained]
|
|
|
|
|
|
|
|
let is_retain attribute_opt =
|
|
|
|
match attribute_opt with
|
|
|
|
| Some attribute ->
|
|
|
|
attribute = `Retain || attribute = `Strong
|
|
|
|
| _ -> false
|
|
|
|
|
|
|
|
let is_copy attribute_opt =
|
|
|
|
match attribute_opt with
|
|
|
|
| Some attribute ->
|
|
|
|
attribute = `Copy
|
|
|
|
| _ -> false
|
|
|
|
|
|
|
|
let name_opt_of_name_info_opt name_info_opt =
|
|
|
|
match name_info_opt with
|
|
|
|
| Some name_info -> Some name_info.Clang_ast_t.ni_name
|
|
|
|
| None -> None
|
|
|
|
|
|
|
|
let rec getter_attribute_opt attributes =
|
|
|
|
match attributes with
|
|
|
|
| [] -> None
|
|
|
|
| attr:: rest ->
|
|
|
|
match attr with
|
|
|
|
| `Getter getter -> name_opt_of_name_info_opt getter.Clang_ast_t.dr_name
|
|
|
|
| _ -> (getter_attribute_opt rest)
|
|
|
|
|
|
|
|
let rec setter_attribute_opt attributes =
|
|
|
|
match attributes with
|
|
|
|
| [] -> None
|
|
|
|
| attr:: rest ->
|
|
|
|
match attr with
|
|
|
|
| `Setter setter -> name_opt_of_name_info_opt setter.Clang_ast_t.dr_name
|
|
|
|
| _ -> (setter_attribute_opt rest)
|
|
|
|
|
|
|
|
(*TODO: take the attributes into account too. To be done after we get the attribute's arguments. *)
|
|
|
|
let is_type_nonnull qt attributes =
|
|
|
|
Utils.string_is_prefix CFrontend_config.nonnull_attribute qt.Clang_ast_t.qt_raw
|
|
|
|
|
|
|
|
let pointer_counter = ref 0
|
|
|
|
|
|
|
|
let get_fresh_pointer () =
|
|
|
|
pointer_counter := !pointer_counter + 1;
|
|
|
|
CFrontend_config.pointer_prefix^(string_of_int (!pointer_counter))
|
|
|
|
|
|
|
|
let get_invalid_pointer () =
|
|
|
|
CFrontend_config.pointer_prefix^("INVALID")
|
|
|
|
|
|
|
|
let type_from_unary_expr_or_type_trait_expr_info info =
|
|
|
|
match info.uttei_qual_type with
|
|
|
|
| Some qt -> Some qt
|
|
|
|
| None -> None
|
|
|
|
|
|
|
|
let is_generated name_info =
|
|
|
|
match name_info.Clang_ast_t.ni_qual_name with
|
|
|
|
| generated:: rest -> generated = CFrontend_config.generated_suffix
|
|
|
|
| _ -> false
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
(* Global counter for anonymous block*)
|
|
|
|
let block_counter = ref 0
|
|
|
|
|
|
|
|
(* Returns a fresh index for a new anonymous block *)
|
|
|
|
let get_fresh_block_index () =
|
|
|
|
block_counter := !block_counter +1;
|
|
|
|
!block_counter
|
|
|
|
|
|
|
|
module General_utils =
|
|
|
|
struct
|
|
|
|
|
|
|
|
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 get_fun_body fdecl_info = fdecl_info.Clang_ast_t.fdi_body
|
|
|
|
|
|
|
|
let rec append_no_duplicates eq list1 list2 =
|
|
|
|
match list2 with
|
|
|
|
| el:: rest2 ->
|
|
|
|
if (list_mem eq el list1) 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 Sil.csu_name_equal list1 list2
|
|
|
|
|
|
|
|
let append_no_duplicates_methods list1 list2 =
|
|
|
|
append_no_duplicates Procname.equal list1 list2
|
|
|
|
|
|
|
|
let append_no_duplicated_vars list1 list2 =
|
|
|
|
let eq (m1, t1) (m2, t2) = (Mangled.equal m1 m2) && (Sil.typ_equal t1 t2) in
|
|
|
|
append_no_duplicates eq list1 list2
|
|
|
|
|
|
|
|
let append_no_duplicated_pvars list1 list2 =
|
|
|
|
let eq (e1, t1) (e2, t2) = (Sil.exp_equal e1 e2) && (Sil.typ_equal t1 t2) in
|
|
|
|
append_no_duplicates eq list1 list2
|
|
|
|
|
|
|
|
let append_no_duplicates_fields list1 list2 =
|
|
|
|
let field_eq (n1, t1, a1) (n2, t2, a2) =
|
|
|
|
match Ident.fieldname_equal n1 n2, Sil.typ_equal t1 t2, Sil.item_annotation_compare a1 a2 with
|
|
|
|
| true, true, _ -> true
|
|
|
|
| _, _, _ -> false in
|
|
|
|
append_no_duplicates field_eq list1 list2
|
|
|
|
|
|
|
|
let sort_fields fields =
|
|
|
|
let compare (name1, _, _) (name2, _, _) =
|
|
|
|
Ident.fieldname_compare name1 name2 in
|
|
|
|
list_sort compare fields
|
|
|
|
|
|
|
|
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 -> sc = CFrontend_config.static
|
|
|
|
| _ -> false
|
|
|
|
|
|
|
|
let block_procname_with_index defining_proc i =
|
|
|
|
Config.anonymous_block_prefix^(Procname.to_string defining_proc)^Config.anonymous_block_num_sep^(string_of_int i)
|
|
|
|
|
|
|
|
(* Makes a fresh name for a block defined inside the defining procedure.*)
|
|
|
|
(* It updates the global block_counter *)
|
|
|
|
let mk_fresh_block_procname defining_proc =
|
|
|
|
let name = block_procname_with_index defining_proc (get_fresh_block_index ()) in
|
|
|
|
Procname.mangled_objc_block name
|
|
|
|
|
|
|
|
(* Returns the next fresh name for a block defined inside the defining procedure *)
|
|
|
|
(* It does not update the global block_counter *)
|
|
|
|
let get_next_block_pvar defining_proc =
|
|
|
|
let name = block_procname_with_index defining_proc (!block_counter +1) in
|
|
|
|
Sil.mk_pvar (Mangled.from_string (CFrontend_config.temp_var^"_"^name)) defining_proc
|
|
|
|
|
|
|
|
(* Reset block counter *)
|
|
|
|
let reset_block_counter () =
|
|
|
|
block_counter := 0
|
|
|
|
|
|
|
|
let mk_function_decl_info_from_block block_decl_info =
|
|
|
|
{
|
|
|
|
Clang_ast_t.fdi_storage_class = None;
|
|
|
|
Clang_ast_t.fdi_is_inline = true; (* This value should not matter as we don't use it*)
|
|
|
|
Clang_ast_t.fdi_is_virtual = false; (* This value should not matter as we don't use it*)
|
|
|
|
Clang_ast_t.fdi_is_module_private = true; (* This value should not matter as we don't use it*)
|
|
|
|
Clang_ast_t.fdi_is_pure = false; (* This value should not matter as we don't use it*)
|
|
|
|
Clang_ast_t.fdi_is_delete_as_written = false; (* This value should not matter as we don't use it*)
|
|
|
|
Clang_ast_t.fdi_decls_in_prototype_scope =[];
|
|
|
|
Clang_ast_t.fdi_parameters = block_decl_info.Clang_ast_t.bdi_parameters;
|
|
|
|
Clang_ast_t.fdi_cxx_ctor_initializers = [];
|
|
|
|
Clang_ast_t.fdi_body = block_decl_info.Clang_ast_t.bdi_body;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 (fun i -> el) (list_range 0 (n -1))
|
|
|
|
|
|
|
|
let mk_class_field_name class_name field_name =
|
|
|
|
Ident.create_fieldname (Mangled.mangled field_name (class_name^"_"^field_name)) 0
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|