Abstract type for list of instructions in node

Summary:
For now: just moving this list behind an abstract type.
Next: changing the internal representation.

Reviewed By: ngorogiannis

Differential Revision: D8140926

fbshipit-source-id: 5b959b0
master
Mehdi Bouaziz 7 years ago committed by Facebook Github Bot
parent 256b74b289
commit 4927e31c2f

@ -0,0 +1,55 @@
(*
* Copyright (c) 2018 - 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
module F = Format
type t = Sil.instr list
let empty = []
let single instr = [instr]
let append_list = List.append
let prepend_one instr instrs = instr :: instrs
let reverse_order = List.rev
let is_empty = List.is_empty
let exists = List.exists
let for_all = List.for_all
let count = List.length
let nth_exists instrs index = List.drop instrs index |> List.is_empty |> not
let nth_exn = List.nth_exn
let last = List.last
let find_map = List.find_map
let pp pe fmt instrs =
List.iter instrs ~f:(fun instr -> F.fprintf fmt "%a;@\n" (Sil.pp_instr pe) instr)
let filter_map = List.filter_map
let fold = List.fold
let iter = List.iter
let map_changed ~equal instrs ~f = IList.map_changed ~equal instrs ~f
let of_list instrs = instrs
let of_rev_list instrs = List.rev instrs

@ -0,0 +1,52 @@
(*
* Copyright (c) 2018 - 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
type t
val empty : t
val single : Sil.instr -> t
val append_list : t -> Sil.instr list -> t
val prepend_one : Sil.instr -> t -> t
val reverse_order : t -> t
val is_empty : t -> bool
val count : t -> int
val exists : t -> f:(Sil.instr -> bool) -> bool
val for_all : t -> f:(Sil.instr -> bool) -> bool
val nth_exists : t -> int -> bool
val nth_exn : t -> int -> Sil.instr
val last : t -> Sil.instr option
val find_map : t -> f:(Sil.instr -> 'a option) -> 'a option
val pp : Pp.env -> Format.formatter -> t -> unit
val filter_map : t -> f:(Sil.instr -> Sil.instr option) -> t
val map_changed : equal:(Sil.instr -> Sil.instr -> bool) -> t -> f:(Sil.instr -> Sil.instr) -> t
val fold : (t, Sil.instr, 'a) Container.fold
val iter : (t, Sil.instr) Container.iter
val of_list : Sil.instr list -> t
val of_rev_list : Sil.instr list -> t

@ -35,7 +35,7 @@ module Node = struct
{ id: id (** unique id of the node *)
; mutable dist_exit: int option (** distance to the exit node *)
; mutable exn: t list (** exception nodes in the cfg *)
; mutable instrs: Sil.instr list (** instructions for symbolic execution *)
; mutable instrs: Instrs.t (** instructions for symbolic execution *)
; kind: nodekind (** kind of node *)
; loc: Location.t (** location in the source code *)
; mutable preds: t list (** predecessor nodes in the cfg *)
@ -51,7 +51,7 @@ module Node = struct
let dummy pname_opt =
{ id= 0
; dist_exit= None
; instrs= []
; instrs= Instrs.empty
; kind= Skip_node "dummy"
; loc= Location.dummy
; pname_opt
@ -111,7 +111,7 @@ module Node = struct
(** Get the source location of the last instruction in the node *)
let get_last_loc n =
n |> get_instrs |> List.last |> Option.value_map ~f:Sil.instr_get_loc ~default:n.loc
n |> get_instrs |> Instrs.last |> Option.value_map ~f:Sil.instr_get_loc ~default:n.loc
let pp_id f id = F.pp_print_int f id
@ -121,14 +121,13 @@ module Node = struct
let get_distance_to_exit node = node.dist_exit
(** Append the instructions to the list of instructions to execute *)
let append_instrs node instrs = if instrs <> [] then node.instrs <- node.instrs @ instrs
let append_instrs node instrs =
if instrs <> [] then node.instrs <- Instrs.append_list node.instrs instrs
(** Add the instructions at the beginning of the list of instructions to execute *)
let prepend_instrs node instrs = node.instrs <- instrs @ node.instrs
(** Map and replace the instructions to be executed *)
let replace_instrs node ~f =
let instrs' = IList.map_changed node.instrs ~equal:phys_equal ~f in
let instrs' = Instrs.map_changed ~equal:phys_equal node.instrs ~f in
if not (phys_equal instrs' node.instrs) then node.instrs <- instrs'
@ -145,7 +144,7 @@ module Node = struct
in
let ptl = ret_var :: List.map ~f:construct_decl locals in
let instr = Sil.Declare_locals (ptl, loc) in
prepend_instrs node [instr]
node.instrs <- Instrs.prepend_one instr node.instrs
(** Print extended instructions for the node,
@ -156,7 +155,7 @@ module Node = struct
match instro with None -> pe0 | Some instr -> Pp.extend_colormap pe0 (Obj.repr instr) Red
in
let instrs = get_instrs node in
Sil.pp_instr_list pe fmt instrs
Instrs.pp pe fmt instrs
else
let () =
match get_kind node with
@ -296,7 +295,7 @@ let is_java_synchronized pdesc = pdesc.attributes.is_java_synchronized_method
let iter_nodes f pdesc = List.iter ~f (get_nodes pdesc)
let iter_instrs f pdesc =
let do_node node = List.iter ~f:(fun i -> f node i) (Node.get_instrs node) in
let do_node node = Instrs.iter ~f:(fun i -> f node i) (Node.get_instrs node) in
iter_nodes do_node pdesc
@ -304,7 +303,7 @@ let fold_nodes pdesc ~init ~f = List.fold ~f ~init (get_nodes pdesc)
let fold_instrs pdesc ~init ~f =
let fold_node acc node =
List.fold ~f:(fun acc instr -> f acc node instr) ~init:acc (Node.get_instrs node)
Instrs.fold ~f:(fun acc instr -> f acc node instr) ~init:acc (Node.get_instrs node)
in
fold_nodes ~f:fold_node ~init pdesc
@ -312,7 +311,7 @@ let fold_instrs pdesc ~init ~f =
let find_map_nodes pdesc ~f = List.find_map ~f (get_nodes pdesc)
let find_map_instrs pdesc ~f =
let find_map_node node = List.find_map ~f (Node.get_instrs node) in
let find_map_node node = Instrs.find_map ~f (Node.get_instrs node) in
find_map_nodes ~f:find_map_node pdesc
@ -356,7 +355,7 @@ let set_succs_exn_base (node: Node.t) succs exn =
(** Create a new cfg node *)
let create_node pdesc loc kind instrs =
let create_node_internal pdesc loc kind instrs =
pdesc.nodes_num <- pdesc.nodes_num + 1 ;
let node_id = pdesc.nodes_num in
let node =
@ -374,6 +373,8 @@ let create_node pdesc loc kind instrs =
node
let create_node pdesc loc kind instrs = create_node_internal pdesc loc kind (Instrs.of_list instrs)
(** Set the successor and exception nodes.
If this is a join node right before the exit node, add an extra node in the middle,
otherwise nullify and abstract instructions cannot be added after a conditional. *)
@ -381,7 +382,7 @@ let node_set_succs_exn pdesc (node: Node.t) succs exn =
match (node.kind, succs) with
| Join_node, [({Node.kind= Exit_node _} as exit_node)] ->
let kind = Node.Stmt_node "between_join_and_exit" in
let node' = create_node pdesc node.loc kind node.instrs in
let node' = create_node_internal pdesc node.loc kind node.instrs in
set_succs_exn_base node [node'] exn ;
set_succs_exn_base node' [exit_node] exn
| _ ->
@ -529,7 +530,7 @@ let convert_cfg ~callee_pdesc ~resolved_pdesc ~f_instr_list =
let loc = Node.get_loc node
and kind = convert_node_kind (Node.get_kind node)
and instrs = f_instr_list (Node.get_instrs node) in
create_node resolved_pdesc loc kind instrs
create_node_internal resolved_pdesc loc kind instrs
and loop callee_nodes =
match callee_nodes with
| [] ->
@ -633,7 +634,7 @@ let specialize_types_proc callee_pdesc resolved_pdesc substitutions =
(* these are generated instructions that will be replaced by the preanalysis *)
None
in
let f_instr_list instrs = List.filter_map ~f:convert_instr instrs in
let f_instr_list instrs = Instrs.filter_map ~f:convert_instr instrs in
convert_cfg ~callee_pdesc ~resolved_pdesc ~f_instr_list
@ -752,8 +753,8 @@ let specialize_with_block_args_instrs resolved_pdesc substitutions =
(instrs, id_map)
in
let f_instr_list instrs =
let instrs, _ = List.fold ~f:convert_instr ~init:([], Ident.Map.empty) instrs in
List.rev instrs
let rev_instrs, _ = Instrs.fold ~f:convert_instr ~init:([], Ident.Map.empty) instrs in
Instrs.of_rev_list rev_instrs
in
f_instr_list

@ -68,7 +68,7 @@ module Node : sig
val get_id : t -> id
(** Get the unique id of the node *)
val get_instrs : t -> Sil.instr list
val get_instrs : t -> Instrs.t
(** Get the instructions to be executed *)
val get_kind : t -> nodekind

@ -436,10 +436,6 @@ let is_block_pvar pvar = Typ.has_block_prefix (Mangled.to_string (Pvar.get_name
(** Dump an instruction. *)
let d_instr (i: instr) = L.add_print_action (L.PTinstr, Obj.repr i)
let pp_instr_list pe fmt instrs =
List.iter instrs ~f:(fun instr -> F.fprintf fmt "%a;@\n" (pp_instr pe) instr)
let pp_atom pe0 f a =
let pe, changed = color_pre_wrapper pe0 f a in
( match a with

@ -307,9 +307,6 @@ val pp_instr : Pp.env -> F.formatter -> instr -> unit
val d_instr : instr -> unit
(** Dump an instruction. *)
val pp_instr_list : Pp.env -> F.formatter -> instr list -> unit
(** Pretty print a list of instructions. *)
val pp_atom : Pp.env -> F.formatter -> atom -> unit
(** Pretty print an atom. *)

@ -66,16 +66,19 @@ struct
let update_inv_map pre ~visit_count =
let compute_post pre instr = TransferFunctions.exec_instr pre proc_data node instr in
(* hack to ensure that we call `exec_instr` on a node even if it has no instructions *)
let instrs = match CFG.instrs node with [] -> [Sil.skip_instr] | l -> l in
let instrs =
let instrs = CFG.instrs node in
if Instrs.is_empty instrs then Instrs.single Sil.skip_instr else instrs
in
if debug then
NodePrinter.start_session
~pp_name:(TransferFunctions.pp_session_name node)
(CFG.underlying_node node) ;
let astate_post = List.fold ~f:compute_post ~init:pre instrs in
let astate_post = Instrs.fold ~f:compute_post ~init:pre instrs in
if debug then (
L.d_strln
(Format.asprintf "PRE: %a@.INSTRS: %aPOST: %a@." Domain.pp pre
(Sil.pp_instr_list Pp.(html Green))
(Instrs.pp Pp.(html Green))
instrs Domain.pp astate_post) ;
NodePrinter.finish_session (CFG.underlying_node node) ) ;
let inv_map' = InvariantMap.add node_id {pre; post= astate_post; visit_count} inv_map in

@ -118,13 +118,13 @@ let get_vararg_type_names tenv (call_node: Procdesc.Node.t) (ivar: Pvar.t) : str
(* Is this the node creating ivar? *)
let initializes_array instrs =
instrs
|> List.find_map ~f:(function
|> Instrs.find_map ~f:(function
| Sil.Store (Exp.Lvar iv, _, Exp.Var t2, _) when Pvar.equal ivar iv ->
Some t2
| _ ->
None )
|> Option.exists ~f:(fun t2 ->
List.exists instrs ~f:(function
Instrs.exists instrs ~f:(function
| Sil.Call ((t1, _), Exp.Const (Const.Cfun pn), _, _, _) ->
Ident.equal t1 t2
&& Typ.Procname.equal pn (Typ.Procname.from_string_c_fun "__new_array")
@ -135,7 +135,7 @@ let get_vararg_type_names tenv (call_node: Procdesc.Node.t) (ivar: Pvar.t) : str
let added_type_name instrs =
let nvar_type_name nvar =
instrs
|> List.find_map ~f:(function
|> Instrs.find_map ~f:(function
| Sil.Load (nv, e, t, _) when Ident.equal nv nvar ->
Some (e, t)
| _ ->
@ -148,7 +148,7 @@ let get_vararg_type_names tenv (call_node: Procdesc.Node.t) (ivar: Pvar.t) : str
in
let added_nvar array_nvar =
instrs
|> List.find_map ~f:(function
|> Instrs.find_map ~f:(function
| Sil.Store (Exp.Lindex (Exp.Var iv, _), _, Exp.Var nvar, _)
when Ident.equal iv array_nvar ->
Some (nvar_type_name nvar)
@ -161,7 +161,7 @@ let get_vararg_type_names tenv (call_node: Procdesc.Node.t) (ivar: Pvar.t) : str
in
let array_nvar =
instrs
|> List.find_map ~f:(function
|> Instrs.find_map ~f:(function
| Sil.Load (nv, Exp.Lvar iv, _, _) when Pvar.equal iv ivar ->
Some nv
| _ ->
@ -255,7 +255,7 @@ let java_get_vararg_values node pvar idenv =
acc
in
let values_of_node acc n =
Procdesc.Node.get_instrs n |> List.fold ~f:values_of_instr ~init:acc
Procdesc.Node.get_instrs n |> Instrs.fold ~f:values_of_instr ~init:acc
in
match Errdesc.find_program_variable_assignment node pvar with
| Some (node', _) ->
@ -279,7 +279,7 @@ let proc_calls resolve_attributes pdesc filter : (Typ.Procname.t * ProcAttribute
in
let do_node node =
let instrs = Procdesc.Node.get_instrs node in
List.iter ~f:(do_instruction node) instrs
Instrs.iter ~f:(do_instruction node) instrs
in
let nodes = Procdesc.get_nodes pdesc in
List.iter ~f:do_node nodes ;

@ -113,7 +113,7 @@ module type S = sig
include Node with type t := node
val instrs : node -> Sil.instr list
val instrs : node -> Instrs.t
(** get the instructions from a node *)
val fold_succs : t -> (node, node, 'accum) Container.fold
@ -268,7 +268,7 @@ end
module Backward (Base : S) = struct
include Base
let instrs n = List.rev (Base.instrs n)
let instrs n = Instrs.reverse_order (Base.instrs n)
let fold_succs = Base.fold_preds
@ -304,12 +304,13 @@ struct
and module IdSet = InstrNode.IdSet )
let instrs (node, index) =
match Base.instrs node with [] -> [] | instrs -> [List.nth_exn instrs index]
let instrs = Base.instrs node in
if Instrs.is_empty instrs then Instrs.empty else Instrs.nth_exn instrs index |> Instrs.single
let first_of_node node = (node, 0)
let last_of_node node = (node, max 0 (List.length (Base.instrs node) - 1))
let last_of_node node = (node, max 0 (Instrs.count (Base.instrs node) - 1))
let fold_normal_succs _ _ ~init:_ ~f:_ = (* not used *) assert false
@ -317,7 +318,7 @@ struct
let fold_succs cfg (node, index) ~init ~f =
let succ_index = index + 1 in
if IList.mem_nth (Base.instrs node) succ_index then f init (node, succ_index)
if Instrs.nth_exists (Base.instrs node) succ_index then f init (node, succ_index)
else
let f acc node = f acc (first_of_node node) in
Base.fold_succs cfg node ~init ~f
@ -347,11 +348,11 @@ struct
let fold_nodes cfg ~init ~f =
let f init node =
match Base.instrs node with
| [] ->
match Base.instrs node |> Instrs.count with
| 0 ->
f init (node, 0)
| instrs ->
List.foldi instrs ~init ~f:(fun index acc _instr -> f acc (node, index))
| nb_instrs ->
IContainer.forto nb_instrs ~init ~f:(fun acc index -> f acc (node, index))
in
Base.fold_nodes cfg ~init ~f

@ -46,7 +46,7 @@ module type S = sig
include Node with type t := node
val instrs : node -> Sil.instr list
val instrs : node -> Instrs.t
(** get the instructions from a node *)
val fold_succs : t -> (node, node, 'accum) Container.fold

@ -1125,7 +1125,7 @@ let pp_cfgnodelabel pdesc fmt (n: Procdesc.Node.t) =
Escape.escape_dotty str
in
let pp_instrs fmt instrs =
List.iter ~f:(fun i -> F.fprintf fmt " %s\\n " (instr_string i)) instrs
Instrs.iter ~f:(fun i -> F.fprintf fmt " %s\\n " (instr_string i)) instrs
in
let instrs = Procdesc.Node.get_instrs n in
F.fprintf fmt "%d: %a \\n %a" (Procdesc.Node.get_id n :> int) pp_label n pp_instrs instrs

@ -86,7 +86,7 @@ let find_in_node_or_preds =
| node :: nodes when not (Procdesc.NodeSet.mem node visited)
-> (
let instrs = Procdesc.Node.get_instrs node in
match List.find_map ~f:(f node) instrs with
match Instrs.find_map ~f:(f node) instrs with
| Some res ->
Some res
| None ->
@ -172,7 +172,7 @@ let rec find_boolean_assignment node pvar true_branch : Procdesc.Node.t option =
| _ ->
false
in
List.exists ~f:filter (Procdesc.Node.get_instrs n)
Instrs.exists ~f:filter (Procdesc.Node.get_instrs n)
in
match Procdesc.Node.get_preds node with
| [pred_node] ->
@ -566,13 +566,15 @@ let explain_leak tenv hpred prop alloc_att_opt bucket =
L.d_str "explain_leak: found nullify before Abstract for pvar " ;
Pvar.d pvar ;
L.d_ln () ) ;
[pvar]
Some pvar
| _ ->
[]
None
in
let rev_nullify_pvars =
IContainer.rev_filter_map_to_list ~fold:Instrs.fold ~f:get_nullify node_instrs
in
let nullify_pvars = List.concat_map ~f:get_nullify node_instrs in
let nullify_pvars_notmp =
List.filter ~f:(fun pvar -> not (Pvar.is_frontend_tmp pvar)) nullify_pvars
List.rev_filter ~f:(fun pvar -> not (Pvar.is_frontend_tmp pvar)) rev_nullify_pvars
in
value_str_from_pvars_vpath nullify_pvars_notmp vpath
| Some (Sil.Store (lexp, _, _, _)) when is_none vpath

@ -73,8 +73,7 @@ module NullifyTransferFunctions = struct
let last_instr_in_node node =
let get_last_instr () =
let instrs = CFG.instrs node in
List.last instrs |> Option.value ~default:Sil.skip_instr
CFG.instrs node |> Instrs.last |> Option.value ~default:Sil.skip_instr
in
if phys_equal node !cache_node then !cache_instr
else

@ -198,12 +198,12 @@ let force_delayed_print fmt =
i Io_infer.Html.pp_end_color ()
else Sil.pp_instr Pp.text fmt i
| L.PTinstr_list, il ->
let il : Sil.instr list = Obj.obj il in
let il : Instrs.t = Obj.obj il in
if Config.write_html then
F.fprintf fmt "%a%a%a" Io_infer.Html.pp_start_color Pp.Green
(Sil.pp_instr_list (Pp.html Green))
(Instrs.pp (Pp.html Green))
il Io_infer.Html.pp_end_color ()
else Sil.pp_instr_list Pp.text fmt il
else Instrs.pp Pp.text fmt il
| L.PTjprop_list, shallow_jpl ->
let (shallow: bool), (jpl: Prop.normal BiabductionSummary.Jprop.t list) =
Obj.obj shallow_jpl

@ -85,7 +85,7 @@ let check_access access_opt de_opt =
| _ ->
()
in
List.iter ~f:process_formal_letref node_instrs ;
Instrs.iter ~f:process_formal_letref node_instrs ;
!formal_ids
in
let formal_param_used_in_call = ref false in
@ -112,7 +112,7 @@ let check_access access_opt de_opt =
| _ ->
false
in
List.exists ~f:filter (Procdesc.Node.get_instrs node)
Instrs.exists ~f:filter (Procdesc.Node.get_instrs node)
in
let local_access_found = ref false in
let do_node node =

@ -22,7 +22,7 @@ let execute___builtin_va_arg {Builtin.pdesc; tenv; prop_; path; args; loc; exe_e
match args with
| [_; _; (lexp3, typ3)] ->
let instr' = Sil.Store (lexp3, typ3, Exp.zero, loc) in
SymExec.instrs ~mask_errors:true exe_env tenv pdesc [instr'] [(prop_, path)]
SymExec.instrs ~mask_errors:true exe_env tenv pdesc (Instrs.single instr') [(prop_, path)]
| _ ->
raise (Exceptions.Wrong_argument_number __POS__)
@ -630,7 +630,7 @@ let execute___cxx_typeid ({Builtin.pdesc; tenv; prop_; args; loc; exe_env} as r)
let set_instr =
Sil.Store (field_exp, Typ.mk Tvoid, Exp.Const (Const.Cstr typ_string), loc)
in
SymExec.instrs ~mask_errors:true exe_env tenv pdesc [set_instr] res
SymExec.instrs ~mask_errors:true exe_env tenv pdesc (Instrs.single set_instr) res
| _ ->
res )
| _ ->
@ -760,7 +760,7 @@ let execute___infer_fail {Builtin.pdesc; tenv; prop_; path; args; loc; exe_env}
let set_instr =
Sil.Store (Exp.Lvar Sil.custom_error, Typ.mk Tvoid, Exp.Const (Const.Cstr error_str), loc)
in
SymExec.instrs ~mask_errors:true exe_env tenv pdesc [set_instr] [(prop_, path)]
SymExec.instrs ~mask_errors:true exe_env tenv pdesc (Instrs.single set_instr) [(prop_, path)]
(* translate builtin assertion failure *)
@ -775,7 +775,7 @@ let execute___assert_fail {Builtin.pdesc; tenv; prop_; path; args; loc; exe_env}
let set_instr =
Sil.Store (Exp.Lvar Sil.custom_error, Typ.mk Tvoid, Exp.Const (Const.Cstr error_str), loc)
in
SymExec.instrs ~mask_errors:true exe_env tenv pdesc [set_instr] [(prop_, path)]
SymExec.instrs ~mask_errors:true exe_env tenv pdesc (Instrs.single set_instr) [(prop_, path)]
let execute_objc_alloc_no_fail symb_state typ alloc_fun_opt
@ -794,7 +794,7 @@ let execute_objc_alloc_no_fail symb_state typ alloc_fun_opt
Sil.Call
(ret_id_typ, alloc_fun, [(sizeof_typ, ptr_typ)] @ alloc_fun_exp, loc, CallFlags.default)
in
SymExec.instrs exe_env tenv pdesc [alloc_instr] symb_state
SymExec.instrs exe_env tenv pdesc (Instrs.single alloc_instr) symb_state
(* NSArray models *)

@ -99,8 +99,8 @@ let get_node () = !gs.last_node
(** simple key for a node: just look at the instructions *)
let node_simple_key node =
let add_instr key instr =
if Sil.instr_is_auxiliary instr then key
let add_instr instr =
if Sil.instr_is_auxiliary instr then None
else
let instr_key =
match instr with
@ -121,9 +121,10 @@ let node_simple_key node =
| Sil.Declare_locals _ ->
8
in
instr_key :: key
Some instr_key
in
Procdesc.Node.get_instrs node |> List.fold ~init:[] ~f:add_instr |> Utils.better_hash
Procdesc.Node.get_instrs node |> IContainer.rev_filter_map_to_list ~fold:Instrs.fold ~f:add_instr
|> Utils.better_hash
(** key for a node: look at the current node, successors and predecessors *)
@ -142,7 +143,7 @@ let node_key node =
let instrs_normalize instrs =
let bound_ids =
let do_instr = function Sil.Load (id, _, _, _) -> Some id | _ -> None in
List.rev_filter_map instrs ~f:do_instr
IContainer.rev_filter_map_to_list ~fold:Instrs.fold ~f:do_instr instrs
in
let subst =
let count = ref Int.min_value in
@ -152,7 +153,8 @@ let instrs_normalize instrs =
in
Sil.subst_of_list (List.rev_map ~f:(fun id -> (id, Exp.Var (gensym id))) bound_ids)
in
List.rev_map ~f:(Sil.instr_sub subst) instrs
let subst_and_add acc instr = Sil.instr_sub subst instr :: acc in
Instrs.fold instrs ~init:[] ~f:subst_and_add
(** Create a function to find duplicate nodes.

@ -1432,7 +1432,7 @@ and instrs ?(mask_errors= false) exe_env tenv pdesc instrs ppl =
[(p, path)]
in
let f plist instr = List.concat_map ~f:(exe_instr instr) plist in
List.fold ~f ~init:ppl instrs
Instrs.fold ~f ~init:ppl instrs
and add_constraints_on_actuals_by_ref tenv caller_pdesc prop actuals_by_ref callee_pname callee_loc =
@ -1625,7 +1625,8 @@ and check_variadic_sentinel ?(fails_on_nil= false) n_formals (sentinel, null_pos
(* simulate a Load for [lexp] *)
let tmp_id_deref = Ident.create_fresh Ident.kprimed in
let load_instr = Sil.Load (tmp_id_deref, lexp, typ, loc) in
try instrs exe_env tenv pdesc [load_instr] result with e when SymOp.exn_not_failure e ->
try instrs exe_env tenv pdesc (Instrs.single load_instr) result
with e when SymOp.exn_not_failure e ->
IExn.reraise_if e ~f:(fun () -> fails_on_nil) ;
let deref_str = Localise.deref_str_nil_argument_in_variadic_method proc_name nargs i in
let err_desc =
@ -1723,7 +1724,7 @@ and sym_exec_alloc_model exe_env pname ret_typ tenv ret_id_typ pdesc loc prop pa
let alloc_fun = Exp.Const (Const.Cfun BuiltinDecl.malloc_no_fail) in
let alloc_instr = Sil.Call (ret_id_typ, alloc_fun, args, loc, CallFlags.default) in
L.d_strln "No spec found, method should be model as alloc, executing alloc... " ;
instrs exe_env tenv pdesc [alloc_instr] [(prop, path)]
instrs exe_env tenv pdesc (Instrs.single alloc_instr) [(prop, path)]
(** Perform symbolic execution for a function call *)
@ -1829,7 +1830,7 @@ and sym_exec_wrapper exe_env handle_exn tenv proc_cfg instr ((prop: Prop.normal
() ) ;
let node_has_abstraction node =
let instr_is_abstraction = function Sil.Abstract _ -> true | _ -> false in
List.exists ~f:instr_is_abstraction (ProcCfg.Exceptional.instrs node)
Instrs.exists ~f:instr_is_abstraction (ProcCfg.Exceptional.instrs node)
in
let curr_node = State.get_node () in
match ProcCfg.Exceptional.kind curr_node with
@ -1896,4 +1897,4 @@ let node handle_exn exe_env tenv proc_cfg (node: ProcCfg.Exceptional.node) (pset
let exe_instr_pset pset instr =
Paths.PathSet.fold (exe_instr_prop instr) pset Paths.PathSet.empty
in
List.fold ~f:exe_instr_pset ~init:pset (ProcCfg.Exceptional.instrs node)
Instrs.fold ~f:exe_instr_pset ~init:pset (ProcCfg.Exceptional.instrs node)

@ -18,7 +18,7 @@ val node :
(** Symbolic execution of the instructions of a node, lifted to sets of propositions. *)
val instrs :
?mask_errors:bool -> Exe_env.t -> Tenv.t -> Procdesc.t -> Sil.instr list
?mask_errors:bool -> Exe_env.t -> Tenv.t -> Procdesc.t -> Instrs.t
-> (Prop.normal Prop.t * Paths.Path.t) list -> (Prop.normal Prop.t * Paths.Path.t) list
(** Symbolic execution of a sequence of instructions.
If errors occur and [mask_errors] is true, just treat as skip. *)

@ -374,7 +374,7 @@ let instrs_get_normal_vars instrs =
Exp.free_vars e |> Sequence.filter ~f:Ident.is_normal
|> Ident.hashqueue_of_sequence ~init:res )
in
List.fold_left ~init:(Ident.HashQueue.create ()) ~f:do_instr instrs |> Ident.HashQueue.keys
Instrs.fold ~init:(Ident.HashQueue.create ()) ~f:do_instr instrs |> Ident.HashQueue.keys
(** Perform symbolic execution for a node starting from an initial prop *)
@ -556,8 +556,12 @@ let compute_visited vset =
let res = ref BiabductionSummary.Visitedset.empty in
let node_get_all_lines n =
let node_loc = Procdesc.Node.get_loc n in
let instrs_loc = List.map ~f:Sil.instr_get_loc (ProcCfg.Exceptional.instrs n) in
let lines = List.map ~f:(fun loc -> loc.Location.line) (node_loc :: instrs_loc) in
let lines =
node_loc.Location.line
:: IContainer.rev_map_to_list ~fold:Instrs.fold
~f:(fun instr -> (Sil.instr_get_loc instr).Location.line)
(ProcCfg.Exceptional.instrs n)
in
List.remove_consecutive_duplicates ~equal:Int.equal (List.sort ~compare:Int.compare lines)
in
let do_node n =

@ -320,7 +320,7 @@ module Report = struct
* or of a block (goes directly to a node with multiple predecessors)
*)
let rec is_end_of_block_or_procedure (cfg: CFG.t) node rem_instrs =
List.for_all rem_instrs ~f:Sil.instr_is_auxiliary
Instrs.for_all rem_instrs ~f:Sil.instr_is_auxiliary
&&
match IContainer.singleton_or_more node ~fold:(CFG.fold_succs cfg) with
| IContainer.Empty ->
@ -462,25 +462,28 @@ module Report = struct
let check_instrs
: Summary.t -> Procdesc.t -> Tenv.t -> CFG.t -> CFG.node -> Sil.instr list
: Summary.t -> Procdesc.t -> Tenv.t -> CFG.t -> CFG.node -> Instrs.t
-> Dom.Mem.astate AbstractInterpreter.state -> PO.ConditionSet.t -> PO.ConditionSet.t =
fun summary pdesc tenv cfg node instrs state cond_set ->
match (state, instrs) with
| _, [] | {AbstractInterpreter.pre= Bottom}, _ :: _ ->
match state with
| _ when Instrs.is_empty instrs ->
cond_set
| {AbstractInterpreter.pre= NonBottom _ as pre; post}, [instr] ->
| {AbstractInterpreter.pre= Bottom} ->
cond_set
| {AbstractInterpreter.pre= NonBottom _ as pre; post} ->
if Instrs.nth_exists instrs 1 then
L.(die InternalError) "Did not expect several instructions" ;
let instr = Instrs.nth_exn instrs 0 in
let () =
match post with
| Bottom ->
check_unreachable_code summary tenv cfg node instr []
check_unreachable_code summary tenv cfg node instr Instrs.empty
| NonBottom _ ->
()
in
let cond_set = check_instr pdesc tenv node instr pre cond_set in
print_debug_info instr pre cond_set ;
cond_set
| _, _ :: _ :: _ ->
L.(die InternalError) "Did not expect several instructions"
let check_node

@ -128,7 +128,7 @@ let report_deps data_map =
let report_data_deps data_map node =
List.iter (Procdesc.Node.get_instrs node) ~f:(fun instr ->
Instrs.iter (Procdesc.Node.get_instrs node) ~f:(fun instr ->
List.iter (Sil.instr_get_exps instr) ~f:(fun exp ->
L.(debug Analysis Medium)
"@\n>>>Data dependencies of node = %a @\n" Procdesc.Node.pp node ;
@ -140,7 +140,7 @@ let report_data_deps data_map node =
let report_control_deps control_map node =
List.iter (Procdesc.Node.get_instrs node) ~f:(fun instr ->
Instrs.iter (Procdesc.Node.get_instrs node) ~f:(fun instr ->
L.(debug Analysis Medium) "@\n>>>Control dependencies of node = %a @\n" Procdesc.Node.pp node ;
List.iter (Sil.instr_get_exps instr) ~f:(fun exp ->
L.(debug Analysis Medium)

@ -76,7 +76,7 @@ let node_throws pdesc node (proc_throws: Typ.Procname.t -> throws) : throws =
res := t
in
let do_instr instr = update_res (instr_throws instr) in
List.iter ~f:do_instr (Procdesc.Node.get_instrs node) ;
Instrs.iter ~f:do_instr (Procdesc.Node.get_instrs node) ;
!res

@ -194,7 +194,7 @@ let checker {Callbacks.tenv; summary; proc_desc} : Summary.t =
VarSet.empty
in
let node_id = CFG.id node in
List.iter (CFG.instrs node) ~f:(fun instr ->
Instrs.iter (CFG.instrs node) ~f:(fun instr ->
match Analyzer.extract_pre node_id invariant_map with
| Some live_vars ->
report_dead_store live_vars captured_by_ref_vars instr

@ -139,22 +139,24 @@ let check_printf_args_ok tenv (node: Procdesc.Node.t) (instr: Sil.instr)
let array_ivar instrs nvar =
match nvar with
| Exp.Var nid ->
List.find_map_exn instrs ~f:(function
Instrs.find_map instrs ~f:(function
| Sil.Load (id, Exp.Lvar iv, _, _) when Ident.equal id nid ->
Some iv
| _ ->
None )
|> IOption.find_value_exn
| _ ->
raise Caml.Not_found
in
let fixed_nvar_type_name instrs nvar =
match nvar with
| Exp.Var nid ->
List.find_map_exn instrs ~f:(function
Instrs.find_map instrs ~f:(function
| Sil.Load (id, Exp.Lvar _, t, _) when Ident.equal id nid ->
Some (PatternMatch.get_type_name t)
| _ ->
None )
|> IOption.find_value_exn
| Exp.Const c ->
PatternMatch.java_get_const_type_name c
| _ ->

@ -126,7 +126,7 @@ let check_condition tenv case_zero find_canonical_duplicate curr_pdesc node e ty
in
let do_node n =
if Location.equal loc (Procdesc.Node.get_loc n) then
List.iter ~f:do_instr (Procdesc.Node.get_instrs n)
Instrs.iter ~f:do_instr (Procdesc.Node.get_instrs n)
in
Procdesc.iter_nodes do_node pdesc ; !throwable_found
in

@ -659,7 +659,7 @@ let typecheck_instr tenv ext calls_this checks (node: Procdesc.Node.t) idenv get
()
(* FIXME: silenced warning may be legit *)
in
List.iter ~f:do_instr (Procdesc.Node.get_instrs cond_node)
Instrs.iter ~f:do_instr (Procdesc.Node.get_instrs cond_node)
in
let handle_optional_isPresent node' e =
match convert_complex_exp_to_pvar node' false e typestate' loc with
@ -1037,7 +1037,7 @@ let typecheck_instr tenv ext calls_this checks (node: Procdesc.Node.t) idenv get
| _ ->
()
in
List.iter ~f:do_instr (Procdesc.Node.get_instrs prev_node) ;
Instrs.iter ~f:do_instr (Procdesc.Node.get_instrs prev_node) ;
!found
| _ ->
None
@ -1118,7 +1118,7 @@ let typecheck_node tenv ext calls_this checks idenv get_proc_desc curr_pname cur
(* Reset 'always' field for forall errors to false. *)
(* This is used to track if it is set to true for all visit to the node. *)
TypeErr.node_reset_forall canonical_node ;
let typestate_succ = List.fold ~f:(do_instruction ext) ~init:typestate instrs in
let typestate_succ = Instrs.fold ~f:(do_instruction ext) ~init:typestate instrs in
let dont_propagate =
Procdesc.Node.equal_nodekind (Procdesc.Node.get_kind node) Procdesc.Node.exn_sink_kind
(* don't propagate exceptions *)

@ -23,3 +23,14 @@ let mem_nth ~fold t index =
fold t ~init:index ~f:(fun index _ -> if index <= 0 then return true else index - 1)
in
false )
let forto excl ~init ~f =
let rec aux excl ~f acc i = if i >= excl then acc else aux excl ~f (f acc i) (i + 1) in
aux excl ~f init 0
let rev_map_to_list ~fold t ~f = fold t ~init:[] ~f:(fun acc item -> f item :: acc)
let rev_filter_map_to_list ~fold t ~f =
fold t ~init:[] ~f:(fun acc item -> IList.opt_cons (f item) acc)

@ -15,3 +15,10 @@ val singleton_or_more :
fold:('t, 'a, 'a singleton_or_more) Container.fold -> 't -> 'a singleton_or_more
val mem_nth : fold:('t, _, int) Container.fold -> 't -> int -> bool
val forto : (int, int, 'accum) Container.fold
val rev_map_to_list : fold:('t, 'a, 'b list) Container.fold -> 't -> f:('a -> 'b) -> 'b list
val rev_filter_map_to_list :
fold:('t, 'a, 'b list) Container.fold -> 't -> f:('a -> 'b option) -> 'b list

@ -0,0 +1,12 @@
(*
* Copyright (c) 2018 - 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
let find_value_exn = function None -> raise Caml.Not_found | Some v -> v

@ -0,0 +1,13 @@
(*
* Copyright (c) 2018 - 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
val find_value_exn : 'a option -> 'a
(** Like [Option.value_exn] but raises [Caml.Not_found] when called with [None]. *)

@ -57,20 +57,21 @@ let tests =
in
let instr_test =
let instr_test_ _ =
( match ProcCfg.Normal.instrs n1 with
let list_of_instrs instrs = Instrs.fold instrs ~init:[] ~f:(fun l i -> i :: l) |> List.rev in
( match ProcCfg.Normal.instrs n1 |> list_of_instrs with
| [instr1; instr2] ->
assert_bool "First instr should be dummy_instr1" (phys_equal instr1 dummy_instr1) ;
assert_bool "Second instr should be dummy_instr2" (phys_equal instr2 dummy_instr2)
| _ ->
assert_failure "Expected exactly two instructions" ) ;
( match BackwardCfg.instrs n1 with
( match BackwardCfg.instrs n1 |> list_of_instrs with
| [instr1; instr2] ->
assert_bool "First instr should be dummy_instr2" (phys_equal instr1 dummy_instr2) ;
assert_bool "Second instr should be dummy_instr1" (phys_equal instr2 dummy_instr1)
| _ ->
assert_failure "Expected exactly two instructions" ) ;
let instr_n1 = InstrCfg.of_underlying_node n1 in
( match InstrCfg.instrs instr_n1 with
( match InstrCfg.instrs instr_n1 |> list_of_instrs with
| [instr] ->
assert_bool "Only instr should be dummy_instr1" (phys_equal instr dummy_instr1)
| _ ->
@ -78,7 +79,7 @@ let tests =
let n1' = InstrCfg.underlying_node instr_n1 in
assert_bool "underlying_node should return node of underlying CFG type" (phys_equal n1 n1') ;
let backward_instr_n1 = BackwardInstrCfg.of_underlying_node n1 in
( match BackwardInstrCfg.instrs backward_instr_n1 with
( match BackwardInstrCfg.instrs backward_instr_n1 |> list_of_instrs with
| [instr] ->
assert_bool "Only instr should be dummy_instr1" (phys_equal instr dummy_instr1)
| _ ->
@ -93,12 +94,14 @@ let tests =
| _ ->
assert_failure "Expected exactly one node"
in
check_backward_instr_ BackwardInstrCfg.fold_preds backward_instr_n1 [dummy_instr2] ;
check_backward_instr_ BackwardInstrCfg.fold_preds backward_instr_n1
(Instrs.single dummy_instr2) ;
let backward_instr_n2 = BackwardInstrCfg.of_underlying_node n2 in
check_backward_instr_ BackwardInstrCfg.fold_preds backward_instr_n2 [] ;
check_backward_instr_ BackwardInstrCfg.fold_preds backward_instr_n2 Instrs.empty ;
let backward_instr_n3 = BackwardInstrCfg.of_underlying_node n3 in
check_backward_instr_ BackwardInstrCfg.fold_preds backward_instr_n3 [] ;
check_backward_instr_ BackwardInstrCfg.fold_normal_succs backward_instr_n2 [dummy_instr2]
check_backward_instr_ BackwardInstrCfg.fold_preds backward_instr_n3 Instrs.empty ;
check_backward_instr_ BackwardInstrCfg.fold_normal_succs backward_instr_n2
(Instrs.single dummy_instr2)
in
"instr_test" >:: instr_test_
in

@ -16,7 +16,7 @@ module MockNode = struct
type id = int
let instrs _ = []
let instrs _ = Instrs.empty
let hash = Hashtbl.hash

Loading…
Cancel
Save