You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

387 lines
13 KiB

(*
* 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