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.

161 lines
5.7 KiB

(*
* Copyright (c) 2009 - 2013 Monoidics ltd.
* 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.
*)
(** The Smallfoot Intermediate Language: Decompiled Expressions *)
open! IStd
module F = Format
(** expression representing the result of decompilation *)
type t =
| Darray of t * t
| Dbinop of Binop.t * t * t
| Dconst of Const.t
| Dsizeof of Typ.t * t option * Subtype.t
| Dderef of t
| Dfcall of t * t list * Location.t * CallFlags.t
| Darrow of t * Typ.Fieldname.t
| Ddot of t * Typ.Fieldname.t
| Dpvar of Pvar.t
| Dpvaraddr of Pvar.t
| Dunop of Unop.t * t
| Dunknown
| Dretcall of t * t list * Location.t * CallFlags.t
(** Value paths: identify an occurrence of a value in a symbolic heap
each expression represents a path, with Dpvar being the simplest one *)
type vpath = t option
let eradicate_java () = Config.eradicate && Language.curr_language_is Java
let split_var_clang var_name =
match String.rsplit2 ~on:'.' var_name with Some (_, name) -> name | _ -> var_name
let builtin_functions_to_string pn =
if Typ.Procname.equal pn BuiltinDecl.__objc_alloc_no_fail then Some "alloc" else None
let rec pp fmt = function
| Darray (de1, de2) ->
F.fprintf fmt "%a[%a]" pp de1 pp de2
| Dbinop (op, de1, de2) ->
F.fprintf fmt "(%a%a%a)" pp de1 (Pp.to_string ~f:(Binop.str Pp.text)) op pp de2
| Dconst (Cfun pn) -> (
match builtin_functions_to_string pn with
| Some str ->
F.pp_print_string fmt str
| None ->
let procname_str = Typ.Procname.to_simplified_string pn in
match pn with
| Typ.Procname.ObjC_Cpp {kind= ObjCInstanceMethod}
| Typ.Procname.ObjC_Cpp {kind= ObjCClassMethod} -> (
match String.lsplit2 ~on:':' procname_str with
| Some (base_name, _) ->
F.pp_print_string fmt base_name
| None ->
F.pp_print_string fmt procname_str )
| _ ->
F.pp_print_string fmt procname_str )
| Dconst c ->
(Const.pp Pp.text) fmt c
| Dderef de ->
F.fprintf fmt "*%a" pp de
| Dfcall (fun_dexp, args, _, {cf_virtual= isvirtual}) ->
let pp_args fmt des =
if eradicate_java () then ( if des <> [] then F.pp_print_string fmt "..." )
else Pp.comma_seq pp fmt des
in
let pp_fun fmt = function
| Dconst (Cfun pname) ->
let s =
match pname with
| Typ.Procname.Java pname_java ->
Typ.Procname.Java.get_method pname_java
| _ ->
Typ.Procname.to_string pname
in
F.pp_print_string fmt s
| de ->
pp fmt de
in
let receiver, args' =
match args with
| Dpvar pv :: args' when isvirtual && Pvar.is_this pv ->
(None, args')
| a :: args' when isvirtual ->
(Some a, args')
| _ ->
(None, args)
in
let pp_receiver fmt = function None -> () | Some arg -> F.fprintf fmt "%a." pp arg in
F.fprintf fmt "%a%a(%a)" pp_receiver receiver pp_fun fun_dexp pp_args args'
| Darrow (Dpvar pv, f) when Pvar.is_this pv ->
(* this->fieldname *)
F.pp_print_string fmt (Typ.Fieldname.to_simplified_string f)
| Darrow (de, f) ->
if Language.curr_language_is Java then
F.fprintf fmt "%a.%s" pp de (Typ.Fieldname.to_flat_string f)
else F.fprintf fmt "%a->%s" pp de (Typ.Fieldname.to_string f)
| Ddot (Dpvar _, fe) when eradicate_java () ->
(* static field access *)
F.pp_print_string fmt (Typ.Fieldname.to_simplified_string fe)
| Ddot (de, f) ->
let field_text =
if Language.curr_language_is Java then Typ.Fieldname.to_flat_string f
else Typ.Fieldname.to_string f
in
F.fprintf fmt "%a.%s" pp de field_text
| Dpvar pv ->
let var_name = Mangled.to_string (Pvar.get_name pv) in
let s = if Language.curr_language_is Clang then split_var_clang var_name else var_name in
F.pp_print_string fmt s
| Dpvaraddr pv ->
let var_name = Mangled.to_string (Pvar.get_name pv) in
let s =
if eradicate_java () then Pvar.get_simplified_name pv
else if Language.curr_language_is Clang then split_var_clang var_name
else Mangled.to_string (Pvar.get_name pv)
in
let pp_ampersand fmt = if not (eradicate_java ()) then F.pp_print_string fmt "&" in
F.fprintf fmt "%t%s" pp_ampersand s
| Dunop (op, de) ->
F.fprintf fmt "%s%a" (Unop.to_string op) pp de
| Dsizeof (typ, _, _) ->
(Typ.pp_full Pp.text) fmt typ
| Dunknown ->
F.pp_print_string fmt "unknown"
| Dretcall (de, _, _, _) ->
F.fprintf fmt "returned by %a" pp de
let to_string de = F.asprintf "%a" pp de
(** Pretty print a value path *)
let pp_vpath pe fmt vpath =
let pp fmt = function Some de -> pp fmt de | None -> () in
if Pp.equal_print_kind pe.Pp.kind Pp.HTML then
F.fprintf fmt " %a{vpath: %a}%a" Io_infer.Html.pp_start_color Pp.Orange pp vpath
Io_infer.Html.pp_end_color ()
else pp fmt vpath
let rec has_tmp_var = function
| Dpvar pvar | Dpvaraddr pvar ->
Pvar.is_frontend_tmp pvar
| Dderef dexp | Ddot (dexp, _) | Darrow (dexp, _) | Dunop (_, dexp) | Dsizeof (_, Some dexp, _) ->
has_tmp_var dexp
| Darray (dexp1, dexp2) | Dbinop (_, dexp1, dexp2) ->
has_tmp_var dexp1 || has_tmp_var dexp2
| Dretcall (dexp, dexp_list, _, _) | Dfcall (dexp, dexp_list, _, _) ->
has_tmp_var dexp || List.exists ~f:has_tmp_var dexp_list
| Dconst _ | Dunknown | Dsizeof (_, None, _) ->
false