|
|
|
(*
|
|
|
|
* 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.
|
|
|
|
*)
|
|
|
|
|
|
|
|
(** Contains current class and current method to be translated as well as local variables, *)
|
|
|
|
(** and the cg, cfg, and tenv corresponding to the current file. *)
|
|
|
|
|
|
|
|
open Utils
|
|
|
|
open CFrontend_utils
|
|
|
|
|
|
|
|
type varStack = (Mangled.t * Sil.typ * int) Stack.t
|
|
|
|
|
|
|
|
type varMap = varStack StringMap.t
|
|
|
|
|
|
|
|
type pointerVarMap = Sil.pvar StringMap.t
|
|
|
|
|
|
|
|
module L = Logging
|
|
|
|
|
|
|
|
type curr_class =
|
|
|
|
| ContextCls of string * string option * string list
|
|
|
|
(*class name and name of (optional) super class , and a list of protocols *)
|
|
|
|
| ContextCategory of string * string (* category name and corresponding class *)
|
|
|
|
| ContextProtocol of string (* category name and corresponding class *)
|
|
|
|
| ContextNoCls
|
|
|
|
|
|
|
|
type t =
|
|
|
|
{
|
|
|
|
tenv : Sil.tenv;
|
|
|
|
cg : Cg.t;
|
|
|
|
cfg : Cfg.cfg;
|
|
|
|
procdesc : Cfg.Procdesc.t;
|
|
|
|
is_objc_method : bool;
|
|
|
|
is_instance : bool;
|
|
|
|
curr_class: curr_class;
|
|
|
|
is_callee_expression : bool;
|
|
|
|
namespace: string option; (* contains the name of the namespace if we are in the scope of one*)
|
|
|
|
mutable local_vars : (Mangled.t * Sil.typ * bool) list; (* (name, type, is_static flag) *)
|
|
|
|
mutable captured_vars : (Mangled.t * Sil.typ * bool) list; (* (name, type, is_static flag) *)
|
|
|
|
mutable local_vars_stack : varMap;
|
|
|
|
mutable local_vars_pointer : pointerVarMap
|
|
|
|
}
|
|
|
|
|
|
|
|
module LocalVars =
|
|
|
|
struct
|
|
|
|
|
|
|
|
module Block =
|
|
|
|
struct
|
|
|
|
let depth_counter = ref 0
|
|
|
|
|
|
|
|
let enter () =
|
|
|
|
depth_counter := !depth_counter + 1
|
|
|
|
|
|
|
|
let leave () =
|
|
|
|
depth_counter := !depth_counter - 1
|
|
|
|
|
|
|
|
let reset () =
|
|
|
|
depth_counter := 0
|
|
|
|
|
|
|
|
let depth () = !depth_counter
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
let lookup_var_map context var_name =
|
|
|
|
try
|
|
|
|
StringMap.find var_name context.local_vars_stack
|
|
|
|
with Not_found -> Stack.create ()
|
|
|
|
|
|
|
|
let lookup_var_formals context procname var_name =
|
|
|
|
let formals = Cfg.Procdesc.get_formals context.procdesc in
|
|
|
|
let arg, typ = list_find (fun (arg, typ) -> arg = var_name) formals in
|
|
|
|
let var = Sil.mk_pvar (Mangled.from_string var_name) procname in
|
|
|
|
(var, typ)
|
|
|
|
|
|
|
|
let lookup_var_captured context procname var_name =
|
|
|
|
let cv, typ, _ = list_find (fun (arg, typ, _) -> arg = Mangled.from_string var_name) context.captured_vars in
|
|
|
|
let var = Sil.mk_pvar cv procname in
|
|
|
|
(var, typ)
|
|
|
|
|
|
|
|
let lookup_var_globals context procname name =
|
|
|
|
let var_name = Mangled.from_string name in
|
|
|
|
let global_var = CGlobal_vars.find var_name in
|
|
|
|
let var = CGlobal_vars.var_get_name global_var in
|
|
|
|
Printing.log_out " ...Variable '%s' found in globals!!@\n" (Sil.pvar_to_string var);
|
|
|
|
let typ = CGlobal_vars.var_get_typ global_var in
|
|
|
|
var, typ
|
|
|
|
|
|
|
|
let is_captured_var context name =
|
|
|
|
list_exists (fun (v, _, _) -> (Mangled.to_string v) = name) context.captured_vars
|
|
|
|
|
|
|
|
let is_global_var context name =
|
|
|
|
try
|
|
|
|
let procname = Cfg.Procdesc.get_proc_name context.procdesc in
|
|
|
|
ignore (lookup_var_globals context procname name);
|
|
|
|
true
|
|
|
|
with Not_found -> false
|
|
|
|
|
|
|
|
let print_locals context =
|
|
|
|
let print_stack var_name stack =
|
|
|
|
Stack.iter
|
|
|
|
(fun (var_name, typ, level) ->
|
|
|
|
Printing.log_out "var item %s:" (Mangled.to_string var_name);
|
|
|
|
Printing.log_out "%s" (Sil.typ_to_string typ);
|
|
|
|
Printing.log_out "- %s @." (string_of_int level)) stack in
|
|
|
|
Printing.log_out "LOCAL VARS:@\n";
|
|
|
|
StringMap.iter print_stack context.local_vars_stack
|
|
|
|
|
|
|
|
let print_pointer_vars context =
|
|
|
|
let print_pointer_var pointer var =
|
|
|
|
Printing.log_out "%s ->" pointer;
|
|
|
|
Printing.log_out " %s@\n" (Sil.pvar_to_string var) in
|
|
|
|
Printing.log_out "POINTER VARS:@\n";
|
|
|
|
StringMap.iter print_pointer_var context.local_vars_pointer
|
|
|
|
|
|
|
|
let add_pointer_var pointer var context =
|
|
|
|
Printing.log_out " ...Adding pointer '%s' " pointer;
|
|
|
|
Printing.log_out "to the map with variable '%s'@." (Sil.pvar_to_string var);
|
|
|
|
context.local_vars_pointer <- StringMap.add pointer var context.local_vars_pointer
|
|
|
|
|
|
|
|
let find_var_with_pointer context pointer =
|
|
|
|
try
|
|
|
|
StringMap.find pointer context.local_vars_pointer
|
|
|
|
with Not_found ->
|
|
|
|
(Printing.log_err " ...Variable for pointer %s not found!!\n%!" pointer);
|
|
|
|
print_pointer_vars context;
|
|
|
|
assert false
|
|
|
|
|
|
|
|
let lookup_var_locals context procname var_name =
|
|
|
|
let stack = lookup_var_map context var_name in
|
|
|
|
let (var_name, typ, level) = Stack.top stack in
|
|
|
|
Printing.log_out " ...Variable %s found in locals!!@." (Mangled.to_string var_name);
|
|
|
|
(Sil.mk_pvar var_name procname), typ
|
|
|
|
|
|
|
|
let lookup_var context pointer var_name kind =
|
|
|
|
let procname = Cfg.Procdesc.get_proc_name context.procdesc in
|
|
|
|
if (kind = `Var) then
|
|
|
|
try
|
|
|
|
Some (fst (lookup_var_locals context procname var_name))
|
|
|
|
with Stack.Empty ->
|
|
|
|
try
|
|
|
|
Some (fst (lookup_var_globals context procname var_name))
|
|
|
|
with Not_found ->
|
|
|
|
if is_captured_var context var_name then
|
|
|
|
try (* if it's a captured variable we need to look at the parameters list*)
|
|
|
|
Some (fst (lookup_var_formals context procname var_name))
|
|
|
|
with Not_found ->
|
|
|
|
Printing.log_err "Variable %s not found!!\n%!" var_name;
|
|
|
|
print_locals context;
|
|
|
|
None
|
|
|
|
else None
|
|
|
|
else if (kind = `ParmVar) then
|
|
|
|
try
|
|
|
|
Some (fst (lookup_var_formals context procname var_name))
|
|
|
|
with Not_found ->
|
|
|
|
let list_to_string = list_to_string (fun (a, typ) -> a^":"^(Sil.typ_to_string typ)) in
|
|
|
|
Printing.log_err "Warning: Parameter %s not found!!\n%!" var_name;
|
|
|
|
Printing.log_err "Formals of procdesc %s" (Procname.to_string procname);
|
|
|
|
Printing.log_err " are %s\n%!" (list_to_string (Cfg.Procdesc.get_formals context.procdesc));
|
|
|
|
Printing.print_failure_info pointer;
|
|
|
|
assert false
|
|
|
|
else if (kind = `Function || kind = `ImplicitParam) then (
|
|
|
|
(* ImplicitParam are 'self' and '_cmd'. These are never defined but they can be referred to in the code. *)
|
|
|
|
Printing.log_err "Creating a variable for '%s' \n%!" var_name;
|
|
|
|
Some (Sil.mk_pvar (Mangled.from_string var_name) procname))
|
|
|
|
else if (kind = `EnumConstant) then
|
|
|
|
(Printing.print_failure_info pointer;
|
|
|
|
assert false)
|
|
|
|
else (Printing.log_err "WARNING: In lookup_var kind %s not handled. Giving up!\n%!" (Clang_ast_j.string_of_decl_kind kind);
|
|
|
|
Printing.print_failure_info pointer;
|
|
|
|
assert false)
|
|
|
|
|
|
|
|
let get_variable_name name =
|
|
|
|
Mangled.mangled name ((string_of_int(Block.depth ())))
|
|
|
|
|
|
|
|
let add_local_var context var_name typ pointer is_static =
|
|
|
|
Printing.log_out " ...Creating var %s" var_name;
|
|
|
|
Printing.log_out " with pointer %s@\n" pointer;
|
|
|
|
if not (is_global_var context var_name) || is_static then
|
|
|
|
let var = get_variable_name var_name in
|
|
|
|
context.local_vars <- context.local_vars@[(var, typ, is_static)] ;
|
|
|
|
let stack = lookup_var_map context var_name in
|
|
|
|
let procname = Cfg.Procdesc.get_proc_name context.procdesc in
|
|
|
|
let pvar = Sil.mk_pvar var procname in
|
|
|
|
Stack.push (var, typ, Block.depth ()) stack;
|
|
|
|
context.local_vars_stack <- StringMap.add var_name stack context.local_vars_stack;
|
|
|
|
add_pointer_var pointer pvar context
|
|
|
|
|
|
|
|
let reset_local_vars context =
|
|
|
|
context.local_vars <- []
|
|
|
|
|
|
|
|
let get_local_vars context =
|
|
|
|
context.local_vars
|
|
|
|
|
|
|
|
let remove_top_level_local_vars context =
|
|
|
|
let remove_top var_name stack =
|
|
|
|
try
|
|
|
|
let (top_var, top_typ, top_level) = Stack.top stack in
|
|
|
|
if top_level == (Block.depth ()) then
|
|
|
|
(ignore (Stack.pop stack);
|
|
|
|
context.local_vars_stack <-
|
|
|
|
StringMap.add var_name stack context.local_vars_stack)
|
|
|
|
else ()
|
|
|
|
with Stack.Empty -> () in
|
|
|
|
StringMap.iter remove_top context.local_vars_stack
|
|
|
|
|
|
|
|
let enter_and_leave_scope context f lstmt =
|
|
|
|
Block.enter ();
|
|
|
|
f context lstmt;
|
|
|
|
remove_top_level_local_vars context;
|
|
|
|
Block.leave ()
|
|
|
|
|
|
|
|
let reset_block = Block.reset
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
let create_context tenv cg cfg procdesc ns curr_class is_objc_method cv is_instance =
|
|
|
|
{ tenv = tenv;
|
|
|
|
cg = cg;
|
|
|
|
cfg = cfg;
|
|
|
|
procdesc = procdesc;
|
|
|
|
curr_class = curr_class;
|
|
|
|
is_callee_expression = false;
|
|
|
|
is_objc_method = is_objc_method;
|
|
|
|
is_instance = is_instance;
|
|
|
|
namespace = ns;
|
|
|
|
local_vars = [];
|
|
|
|
captured_vars = cv;
|
|
|
|
local_vars_stack = StringMap.empty;
|
|
|
|
local_vars_pointer = StringMap.empty
|
|
|
|
}
|
|
|
|
|
|
|
|
let get_cfg context = context.cfg
|
|
|
|
|
|
|
|
let get_cg context = context.cg
|
|
|
|
|
|
|
|
let get_tenv context = context.tenv
|
|
|
|
|
|
|
|
let get_procdesc context = context.procdesc
|
|
|
|
|
|
|
|
let is_objc_method context = context.is_objc_method
|
|
|
|
|
|
|
|
let get_curr_class context = context.curr_class
|
|
|
|
|
|
|
|
let get_curr_class_name curr_class =
|
|
|
|
match curr_class with
|
|
|
|
| ContextCls (name, _, _) -> name
|
|
|
|
| ContextCategory (name, cls) -> cls
|
|
|
|
| ContextProtocol name -> name
|
|
|
|
| ContextNoCls -> assert false
|
|
|
|
|
|
|
|
let curr_class_to_string curr_class =
|
|
|
|
match curr_class with
|
|
|
|
| ContextCls (name, superclass, protocols) ->
|
|
|
|
("class " ^ name ^ ", superclass: " ^ (Option.default "" superclass) ^
|
|
|
|
", protocols: " ^ (Utils.list_to_string (fun x -> x) protocols))
|
|
|
|
| ContextCategory (name, cls) -> ("category " ^ name ^ " of class " ^ cls)
|
|
|
|
| ContextProtocol name -> ("protocol " ^ name)
|
|
|
|
| ContextNoCls -> "no class"
|
|
|
|
|
|
|
|
let curr_class_compare curr_class1 curr_class2 =
|
|
|
|
match curr_class1, curr_class2 with
|
|
|
|
| ContextCls (name1, _, _), ContextCls (name2, _, _) ->
|
|
|
|
String.compare name1 name2
|
|
|
|
| ContextCls (_, _, _), _ -> -1
|
|
|
|
| _, ContextCls (_, _, _) -> 1
|
|
|
|
| ContextCategory (name1, cls1), ContextCategory (name2, cls2) ->
|
|
|
|
Utils.pair_compare String.compare String.compare (name1, cls1) (name2, cls2)
|
|
|
|
| ContextCategory (_, _), _ -> -1
|
|
|
|
| _, ContextCategory (_, _) -> 1
|
|
|
|
| ContextProtocol name1, ContextProtocol name2 ->
|
|
|
|
String.compare name1 name2
|
|
|
|
| ContextProtocol _, _ -> -1
|
|
|
|
| _, ContextProtocol _ -> 1
|
|
|
|
| ContextNoCls, ContextNoCls -> 0
|
|
|
|
|
|
|
|
let curr_class_equal curr_class1 curr_class2 =
|
|
|
|
curr_class_compare curr_class1 curr_class2 == 0
|
|
|
|
|
|
|
|
let curr_class_hash curr_class =
|
|
|
|
match curr_class with
|
|
|
|
| ContextCls (name, _, _) -> Hashtbl.hash name
|
|
|
|
| ContextCategory (name, cls) -> Hashtbl.hash (name, cls)
|
|
|
|
| ContextProtocol name -> Hashtbl.hash name
|
|
|
|
| ContextNoCls -> Hashtbl.hash "no class"
|
|
|
|
|
|
|
|
let get_qt_curr_class curr_class =
|
|
|
|
(get_curr_class_name curr_class)^" *"
|
|
|
|
|
|
|
|
let get_captured_vars context = context.captured_vars
|
|
|
|
|
|
|
|
let create_curr_class tenv class_name =
|
|
|
|
let class_tn_name = Sil.TN_csu (Sil.Class, (Mangled.from_string class_name)) in
|
|
|
|
match Sil.tenv_lookup tenv class_tn_name with
|
|
|
|
| Some Sil.Tstruct(intf_fields, _, _, _, superclasses, methods, annotation) ->
|
|
|
|
(let superclasses_names = list_map (fun (_, name) -> Mangled.to_string name) superclasses in
|
|
|
|
match superclasses_names with
|
|
|
|
| superclass:: protocols ->
|
|
|
|
ContextCls (class_name, Some superclass, protocols)
|
|
|
|
| [] -> ContextCls (class_name, None, []))
|
|
|
|
| _ -> assert false
|