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.
651 lines
23 KiB
651 lines
23 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.
|
|
*)
|
|
|
|
open! Utils
|
|
|
|
(** Generate unit tests automatically from specs *)
|
|
|
|
module L = Logging
|
|
module F = Format
|
|
|
|
let (++) = Sil.Int.add
|
|
let (--) = Sil.Int.sub
|
|
|
|
module IdMap = Map.Make (Ident) (** maps from identifiers *)
|
|
|
|
(** Constraint solving module *)
|
|
module Constraint : sig
|
|
(** Collect constraints on [vars] from [pi], and return a satisfying instantiation *)
|
|
val solve_from_pure : Sil.atom list -> Ident.t list -> Sil.Int.t IdMap.t
|
|
end = struct
|
|
(** flag for debug mode of the module *)
|
|
let debug = false
|
|
|
|
(** denote a range of values [bottom] <= val <= [top], except [excluded] *)
|
|
type range =
|
|
{ mutable bottom: Sil.Int.t option; (** lower bound *)
|
|
mutable excluded: Sil.Int.t list; (** individual values not in range *)
|
|
mutable top: Sil.Int.t option; (** upper bound *) }
|
|
|
|
type eval = range IdMap.t (** evaluation for variables *)
|
|
|
|
(** create a new range *)
|
|
let new_range () =
|
|
{ bottom = None; excluded = []; top = None }
|
|
|
|
(** pretty print a range *)
|
|
let pp_range id fmt rng =
|
|
let pp_opt fmt = function
|
|
| None -> F.fprintf fmt "_"
|
|
| Some n -> Sil.Int.pp fmt n in
|
|
F.fprintf fmt "%a <= %a <= %a [%a]" pp_opt rng.bottom (Ident.pp pe_text) id pp_opt rng.top (pp_comma_seq Sil.Int.pp) rng.excluded
|
|
|
|
(** pretty print an evaluation *)
|
|
let pp_eval fmt ev =
|
|
let do_id id rng =
|
|
F.fprintf fmt "%a@." (pp_range id) rng in
|
|
IdMap.iter do_id ev
|
|
|
|
(** create a new evaluation *)
|
|
let new_eval vars : eval =
|
|
let ev = ref IdMap.empty in
|
|
let add_var id =
|
|
ev := IdMap.add id (new_range ()) !ev in
|
|
IList.iter add_var vars;
|
|
!ev
|
|
|
|
let gt_bottom i r =
|
|
match r.bottom with
|
|
| None -> true
|
|
| Some j -> Sil.Int.gt i j
|
|
|
|
let geq_bottom i r =
|
|
match r.bottom with
|
|
| None -> true
|
|
| Some j -> Sil.Int.geq i j
|
|
|
|
let lt_top i r =
|
|
match r.top with
|
|
| None -> true
|
|
| Some j -> Sil.Int.lt i j
|
|
|
|
let leq_top i r =
|
|
match r.top with
|
|
| None -> true
|
|
| Some j -> Sil.Int.leq i j
|
|
|
|
(** normalize [r]: the excluded elements must be strictly between bottom and top *)
|
|
let normalize r =
|
|
r.excluded <- IList.filter (fun i -> geq_bottom i r && leq_top i r) r.excluded;
|
|
let rec normalize_bottom () = match r.bottom with
|
|
| None -> ()
|
|
| Some i ->
|
|
if IList.mem Sil.Int.eq i r.excluded then begin
|
|
r.excluded <- IList.filter (Sil.Int.neq i) r.excluded;
|
|
r.bottom <- Some (i ++ Sil.Int.one);
|
|
normalize_bottom ()
|
|
end in
|
|
let rec normalize_top () = match r.top with
|
|
| None -> ()
|
|
| Some i ->
|
|
if IList.mem Sil.Int.eq i r.excluded then begin
|
|
r.excluded <- IList.filter (Sil.Int.neq i) r.excluded;
|
|
r.top <- Some (i -- Sil.Int.one);
|
|
normalize_top ()
|
|
end in
|
|
normalize_bottom ();
|
|
normalize_top ()
|
|
|
|
(** global variable set to true every time a range is changed *)
|
|
let changed = ref false
|
|
|
|
let mark_changed id r =
|
|
changed := true;
|
|
if debug then F.fprintf F.std_formatter "changed: %a@." (pp_range id) r
|
|
|
|
(** exclude one element from the range *)
|
|
let add_excluded r id i =
|
|
if geq_bottom i r && leq_top i r && not (IList.mem Sil.Int.eq i r.excluded)
|
|
then begin
|
|
r.excluded <- i :: r.excluded;
|
|
normalize r;
|
|
mark_changed id r;
|
|
end
|
|
|
|
(** make the bottom of the range >= [i] *)
|
|
let add_bottom r id i =
|
|
if gt_bottom i r then
|
|
begin
|
|
r.bottom <- Some i;
|
|
normalize r;
|
|
mark_changed id r
|
|
end
|
|
|
|
(** make the top of the range <= [i] *)
|
|
let add_top r id i =
|
|
if lt_top i r then
|
|
begin
|
|
r.top <- Some i;
|
|
normalize r;
|
|
mark_changed id r
|
|
end
|
|
|
|
(** choose an element in the range *)
|
|
let choose id rng =
|
|
if debug then F.fprintf F.std_formatter "choosing %a@." (pp_range id) rng;
|
|
let found = ref None in
|
|
let num_iter = IList.length rng.excluded in
|
|
let try_candidate candidate =
|
|
if geq_bottom candidate rng && leq_top candidate rng && not (IList.mem Sil.Int.eq candidate rng.excluded)
|
|
then (found := Some candidate; rng.bottom <- Some candidate; rng.top <- Some candidate; rng.excluded <- []) in
|
|
let search_up () =
|
|
let base = match rng.bottom with None -> Sil.Int.zero | Some n -> n in
|
|
for i = 0 to num_iter do
|
|
if !found = None then
|
|
let candidate = Sil.Int.add base (Sil.Int.of_int i) in
|
|
try_candidate candidate
|
|
done in
|
|
let search_down () =
|
|
let base = match rng.top with None -> Sil.Int.zero | Some n -> n in
|
|
for i = 0 to num_iter do
|
|
if !found = None then
|
|
let candidate = Sil.Int.sub base (Sil.Int.of_int i) in
|
|
try_candidate candidate
|
|
done in
|
|
search_up ();
|
|
if !found = None then search_down ();
|
|
if !found = None then
|
|
(L.err "Constraint Error: empty range %a@." (pp_range id) rng;
|
|
rng.top <- Some Sil.Int.zero;
|
|
rng.bottom <- Some Sil.Int.zero;
|
|
rng.excluded <- [])
|
|
|
|
(** return the solution if the id is solved (has unique solution) *)
|
|
let solved ev id =
|
|
let rng = IdMap.find id ev in
|
|
match rng.bottom, rng.top with
|
|
| Some n1, Some n2 when Sil.Int.eq n1 n2 -> Some n1
|
|
| _ -> None
|
|
|
|
let rec pi_iter do_le do_lt do_neq pi =
|
|
let do_atom a = match a with
|
|
| Sil.Aeq (Sil.BinOp (Sil.Le, e1, e2), Sil.Const (Sil.Cint i)) when Sil.Int.isone i ->
|
|
do_le e1 e2
|
|
| Sil.Aeq (Sil.BinOp (Sil.Lt, e1, e2), Sil.Const (Sil.Cint i)) when Sil.Int.isone i ->
|
|
do_lt e1 e2
|
|
| Sil.Aeq _ -> ()
|
|
| Sil.Aneq (e1, e2) ->
|
|
do_neq e1 e2 in
|
|
changed := false;
|
|
IList.iter do_atom pi;
|
|
if !changed then pi_iter do_le do_lt do_neq pi
|
|
|
|
(** Collect constraints on [vars] from [pi], and return a satisfying instantiation *)
|
|
let solve_from_pure pi vars =
|
|
if debug then F.fprintf F.std_formatter "solve_from_pure pi: %a@." (Prop.pp_pi pe_text) pi;
|
|
let vars_fav = Sil.fav_from_list vars in
|
|
let atom_is_relevant a =
|
|
let fav = Sil.atom_fav a in
|
|
Sil.fav_for_all fav (fun id -> Sil.fav_mem vars_fav id) in
|
|
let pi_relevant = IList.filter atom_is_relevant pi in
|
|
let ev = new_eval vars in
|
|
let update_top rng id n_op = match rng.top, n_op with
|
|
| Some _, Some n -> add_top rng id n
|
|
| _ -> () in
|
|
let update_bottom rng id n_op = match rng.bottom, n_op with
|
|
| Some _, Some n -> add_bottom rng id n
|
|
| _ -> () in
|
|
let (+++) n1_op n2_op = match n1_op, n2_op with
|
|
| Some n1, Some n2 -> Some (Sil.Int.add n1 n2)
|
|
| _ -> None in
|
|
let (---) n1_op n2_op = match n1_op, n2_op with
|
|
| Some n1, Some n2 -> Some (Sil.Int.sub n1 n2)
|
|
| _ -> None in
|
|
let do_le e1 e2 = match e1, e2 with
|
|
| Sil.Var id, Sil.Const (Sil.Cint n) ->
|
|
let rng = IdMap.find id ev in
|
|
add_top rng id n
|
|
| Sil.BinOp (Sil.MinusA, Sil.Var id1, Sil.Var id2), Sil.Const (Sil.Cint n) ->
|
|
let rng1 = IdMap.find id1 ev in
|
|
let rng2 = IdMap.find id2 ev in
|
|
update_top rng1 id1 (rng2.top +++ (Some n));
|
|
update_bottom rng2 id2 (rng1.bottom --- (Some n))
|
|
| Sil.BinOp (Sil.PlusA, Sil.Var id1, Sil.Var id2), Sil.Const (Sil.Cint n) ->
|
|
let rng1 = IdMap.find id1 ev in
|
|
let rng2 = IdMap.find id2 ev in
|
|
update_top rng1 id1 (Some n --- rng2.bottom);
|
|
update_top rng2 id2 (Some n --- rng1.bottom)
|
|
| _ -> if debug then assert false in
|
|
let do_lt e1 e2 = match e1, e2 with
|
|
| Sil.Const (Sil.Cint n), Sil.Var id ->
|
|
let rng = IdMap.find id ev in
|
|
add_bottom rng id (n ++ Sil.Int.one)
|
|
| Sil.Const (Sil.Cint n), Sil.BinOp (Sil.PlusA, Sil.Var id1, Sil.Var id2) ->
|
|
let rng1 = IdMap.find id1 ev in
|
|
let rng2 = IdMap.find id2 ev in
|
|
update_bottom rng1 id1 (Some (n ++ Sil.Int.one) --- rng2.top);
|
|
update_bottom rng2 id2 (Some (n ++ Sil.Int.one) --- rng1.top)
|
|
| _ -> if debug then assert false in
|
|
let rec do_neq e1 e2 = match e1, e2 with
|
|
| Sil.Var id, Sil.Const (Sil.Cint n)
|
|
| Sil.Const(Sil.Cint n), Sil.Var id ->
|
|
let rng = IdMap.find id ev in
|
|
add_excluded rng id n
|
|
| Sil.Var id1, Sil.Var id2 ->
|
|
(match solved ev id1, solved ev id2 with
|
|
| None, None -> ()
|
|
| Some _, Some _ -> ()
|
|
| Some n1, None ->
|
|
do_neq (Sil.exp_int n1) e2
|
|
| None, Some n2 ->
|
|
do_neq e1 (Sil.exp_int n2))
|
|
| Sil.Var id1, Sil.BinOp(Sil.PlusA, Sil.Var id2, Sil.Const (Sil.Cint n)) ->
|
|
(match solved ev id1, solved ev id2 with
|
|
| None, None -> ()
|
|
| Some _, Some _ -> ()
|
|
| Some n1, None ->
|
|
do_neq (Sil.exp_int (n1 -- n)) (Sil.Var id2)
|
|
| None, Some n2 ->
|
|
do_neq (Sil.Var id1) (Sil.exp_int (n2 ++ n)))
|
|
| _ -> if debug then assert false in
|
|
let do_ident id =
|
|
if debug then F.fprintf F.std_formatter "constraints before doing %a:@.%a@." (Ident.pp pe_text) id pp_eval ev;
|
|
let rng = IdMap.find id ev in
|
|
pi_iter do_le do_lt do_neq pi_relevant;
|
|
choose id rng in
|
|
IList.iter do_ident vars;
|
|
if debug then F.fprintf F.std_formatter "solution to pure constraints:@.%a@." pp_eval ev;
|
|
let solution = IdMap.map (function { bottom = Some n } -> n | _ -> assert false) ev in
|
|
solution
|
|
end
|
|
|
|
type varinfo =
|
|
{ typ: Sil.typ; (* type of the variable *)
|
|
alloc: bool (* whether the variable needs allocation (on lhs of |->, lists) *)
|
|
}
|
|
|
|
type idmap = varinfo IdMap.t (* map from identifier to varinfo *)
|
|
|
|
let extend_idmap id vinfo idmap =
|
|
let vinfo' =
|
|
try
|
|
let old_vinfo = IdMap.find id !idmap in
|
|
{ old_vinfo with alloc = old_vinfo.alloc || vinfo.alloc }
|
|
with Not_found -> vinfo in
|
|
idmap := IdMap.add id vinfo' !idmap
|
|
|
|
let pe = pe_text
|
|
|
|
(** Given a sigma, create an idmap *)
|
|
let create_idmap sigma : idmap =
|
|
let idmap = ref IdMap.empty in
|
|
let rec do_exp e typ = match e, typ with
|
|
| Sil.Const _, _ -> ()
|
|
| Sil.Var id, _ ->
|
|
extend_idmap id { typ = typ; alloc = false } idmap
|
|
| Sil.BinOp (Sil.PlusA, e1, e2), _ ->
|
|
do_exp e1 typ;
|
|
do_exp e2 typ
|
|
| Sil.BinOp (Sil.PlusPI, e1, e2), _ ->
|
|
do_exp e1 typ;
|
|
do_exp e2 (Sil.Tint Sil.IULong)
|
|
| Sil.Lfield (e1, _, _), _ ->
|
|
do_exp e1 typ
|
|
| Sil.Sizeof _, _ -> ()
|
|
| _ ->
|
|
L.err "Unmatched exp: %a : %a@." (Sil.pp_exp pe) e (Sil.pp_typ_full pe) typ;
|
|
assert false in
|
|
let rec do_se se typ = match se, typ with
|
|
| Sil.Eexp (e, _), _ ->
|
|
do_exp e typ
|
|
| Sil.Estruct (fsel, _), Sil.Tstruct { Sil.instance_fields } ->
|
|
do_struct fsel instance_fields
|
|
| Sil.Earray (size, esel, _), Sil.Tarray (typ, _) ->
|
|
do_se (Sil.Eexp (size, Sil.inst_none)) (Sil.Tint Sil.IULong);
|
|
do_array esel typ
|
|
| _ ->
|
|
L.err "Unmatched sexp: %a : %a@." (Sil.pp_sexp pe) se (Sil.pp_typ_full pe) typ;
|
|
assert false
|
|
and do_struct fsel ftal = match fsel, ftal with
|
|
| [], _ -> ()
|
|
| (f1, se) :: fsel', (f2, typ, _) :: ftl' when Ident.fieldname_equal f1 f2 ->
|
|
do_se se typ;
|
|
do_struct fsel' ftl'
|
|
| _ :: _, _ :: ftal' ->
|
|
do_struct fsel ftal'
|
|
| _:: _, [] -> assert false
|
|
and do_array esel typ = match esel with
|
|
| (e, se):: esel' ->
|
|
do_se (Sil.Eexp (e, Sil.inst_none)) (Sil.Tint Sil.IULong);
|
|
do_se se typ;
|
|
do_array esel' typ
|
|
| [] -> () in
|
|
let do_lhs_e e t = match e with
|
|
| Sil.Var id ->
|
|
extend_idmap id { typ = t; alloc = true } idmap
|
|
| _ -> () in
|
|
let do_hpred = function
|
|
| Sil.Hpointsto (e, se, Sil.Sizeof (typ, _)) ->
|
|
do_lhs_e e (Sil.Tptr (typ, Sil.Pk_pointer));
|
|
do_se se typ
|
|
| Sil.Hlseg (_, _, e, f, el) ->
|
|
do_lhs_e e (Sil.Tptr (Sil.Tvoid, Sil.Pk_pointer));
|
|
do_se (Sil.Eexp (f, Sil.inst_none)) (Sil.Tptr (Sil.Tvoid, Sil.Pk_pointer));
|
|
IList.iter (fun e -> do_se (Sil.Eexp (e, Sil.inst_none)) Sil.Tvoid) el
|
|
| hpred ->
|
|
L.err "do_hpred not implemented %a@." (Sil.pp_hpred pe) hpred in
|
|
IList.iter do_hpred sigma;
|
|
!idmap
|
|
|
|
module Code : sig
|
|
type t
|
|
val add_from_pp : t -> (Format.formatter -> unit -> unit) -> unit
|
|
val add_line : t -> string -> unit
|
|
val append : t -> t -> unit
|
|
val empty : unit -> t
|
|
val pp : F.formatter -> t -> unit
|
|
val set_indent : string -> unit
|
|
(* val to_list : t -> string list *)
|
|
end = struct
|
|
type t = string list ref
|
|
let indent = ref ""
|
|
let to_list code =
|
|
IList.rev !code
|
|
let pp fmt code =
|
|
let doit line = F.fprintf fmt "%s@\n" line in
|
|
IList.iter doit (to_list code);
|
|
F.fprintf fmt "@."
|
|
let empty () = ref []
|
|
let add_line code l =
|
|
code := (!indent ^ l) :: !code
|
|
let add_from_pp code pp =
|
|
add_line code (pp_to_string pp ())
|
|
let append code1 code2 =
|
|
code1 := !code2 @ !code1
|
|
let set_indent s =
|
|
indent := s
|
|
end
|
|
|
|
type code = Code.t
|
|
|
|
(** pretty print generated code *)
|
|
let pp_code = Code.pp
|
|
|
|
(** pretty print an ident in C *)
|
|
let pp_id_c fmt id =
|
|
let name = Ident.get_name id in
|
|
let stamp = Ident.get_stamp id in
|
|
let varname = Ident.name_to_string name in
|
|
F.fprintf fmt "%s%d" varname stamp
|
|
|
|
(** pretty print an expression in C *)
|
|
let rec pp_exp_c pe fmt = function
|
|
| Sil.Lfield (e, f, _) ->
|
|
F.fprintf fmt "&(%a->%a)" (pp_exp_c pe) e Ident.pp_fieldname f
|
|
| Sil.Var id ->
|
|
pp_id_c fmt id
|
|
| e ->
|
|
Sil.pp_exp pe fmt e
|
|
|
|
(** pretty print a type in C *)
|
|
let pp_typ_c pe typ =
|
|
let pp_nil _ () = () in
|
|
Sil.pp_type_decl pe pp_nil pp_exp_c typ
|
|
|
|
(** Convert a pvar to a string by just extracting the name *)
|
|
let to_string pvar =
|
|
Mangled.to_string (Pvar.get_name pvar)
|
|
|
|
(** pretty print an expression list in C *)
|
|
let pp_exp_list_c pe f expl =
|
|
(pp_seq (pp_exp_c pe)) f expl
|
|
|
|
(** Name of the recusive function for a specific list para *)
|
|
let mk_lseg_name id proc_name spec_num =
|
|
"mk_lseg_" ^ string_of_int id ^ Procname.to_string proc_name ^ "_spec" ^ string_of_int spec_num
|
|
|
|
let mk_size_name id =
|
|
"_size_" ^ string_of_int id
|
|
|
|
let pp_texp_for_malloc fmt =
|
|
let rec handle_arr_size typ = match typ with
|
|
| Sil.Tvar _ | Sil.Tint _ | Sil.Tfloat _ | Sil.Tvoid | Sil.Tfun _ ->
|
|
typ
|
|
| Sil.Tptr (t, pk) ->
|
|
Sil.Tptr (handle_arr_size t, pk)
|
|
| Sil.Tstruct struct_typ ->
|
|
let instance_fields =
|
|
IList.map (fun (f, t, a) -> (f, handle_arr_size t, a)) struct_typ.Sil.instance_fields in
|
|
Sil.Tstruct { struct_typ with Sil.instance_fields }
|
|
| Sil.Tarray (t, e) ->
|
|
Sil.Tarray (handle_arr_size t, e) in
|
|
function
|
|
| Sil.Sizeof (typ, _) ->
|
|
let typ' = handle_arr_size typ in
|
|
F.fprintf fmt "sizeof(%a)" (pp_typ_c pe) typ'
|
|
| e -> pp_exp_c pe fmt e
|
|
|
|
(* generate code for sigma *)
|
|
let gen_sigma code proc_name spec_num env sigma =
|
|
let post_code = Code.empty () in
|
|
let rec do_strexp code' base need_deref = function
|
|
| Sil.Eexp (e, _) ->
|
|
let lhs = if need_deref then "(*"^base^")" else base in
|
|
let pp f () = F.fprintf f "%s = %a;" lhs (pp_exp_c pe) e in
|
|
Code.add_from_pp code' pp
|
|
| Sil.Estruct (fsel, _) ->
|
|
let accessor = if need_deref then "->" else "." in
|
|
IList.iter (fun (f, se) -> do_strexp code' (base ^ accessor ^ Ident.fieldname_to_string f) false se) fsel
|
|
| Sil.Earray (_, esel, _) ->
|
|
IList.iter (fun (e, se) ->
|
|
let pp f () = F.fprintf f "%a" (pp_exp_c pe) e in
|
|
let index = pp_to_string pp () in
|
|
do_strexp code' (base ^ "[" ^ index ^ "]") false se) esel in
|
|
|
|
let gen_hpred = function
|
|
| Sil.Hpointsto (Sil.Lvar pvar, se, _) ->
|
|
let base = to_string pvar in
|
|
do_strexp post_code base false se
|
|
| Sil.Hpointsto (Sil.Var id, se, te) ->
|
|
let pp1 f () =
|
|
F.fprintf f "%a = malloc(%a);" pp_id_c id pp_texp_for_malloc te in
|
|
let pp2 f () =
|
|
F.fprintf f "if(%a == NULL) exit(12);" pp_id_c id in
|
|
Code.add_from_pp code pp1;
|
|
Code.add_from_pp code pp2;
|
|
let pp3 f () = F.fprintf f "%a" pp_id_c id in
|
|
let base = pp_to_string pp3 () in
|
|
do_strexp post_code base true se
|
|
| Sil.Hlseg (_, hpar, Sil.Var id, f, el) ->
|
|
let hpara_id = Sil.Predicates.get_hpara_id env hpar in
|
|
let size_var = mk_size_name hpara_id in
|
|
let mk_name = mk_lseg_name hpara_id proc_name spec_num in
|
|
let pp_el fmt el =
|
|
if el != [] then pp_exp_list_c pe fmt el in
|
|
let pp1 fmt () =
|
|
F.fprintf fmt "int %s = 42;" size_var in
|
|
let pp2 fmt () =
|
|
F.fprintf fmt "%a = %s(%s, %a%a);" pp_id_c id mk_name size_var (pp_exp_c pe) f pp_el el in
|
|
Code.add_from_pp code pp1;
|
|
Code.add_from_pp code pp2
|
|
| hpred ->
|
|
L.err "gen_hpred not implemented: %a@." (Sil.pp_hpred pe) hpred in
|
|
IList.iter gen_hpred sigma;
|
|
Code.append code post_code
|
|
|
|
(* generate code corresponding to equalities in the pure part *)
|
|
let gen_init_equalities code pure =
|
|
let do_atom = function
|
|
| Sil.Aeq (Sil.Var id, e) ->
|
|
let pp f () = F.fprintf f "%a = %a;" pp_id_c id (pp_exp_c pe) e in
|
|
Code.add_from_pp code pp
|
|
| _ -> () in
|
|
IList.iter do_atom pure
|
|
|
|
(** generate variable declarations *)
|
|
let gen_var_decl code idmap parameters =
|
|
let do_parameter (name, typ) =
|
|
let pp_name f () = Mangled.pp f name in
|
|
let pp f () = F.fprintf f "%a;" (Sil.pp_type_decl pe pp_name pp_exp_c) typ in
|
|
Code.add_from_pp code pp in
|
|
let do_vinfo id { typ } =
|
|
let pp_var f () = pp_id_c f id in
|
|
let pp f () = F.fprintf f "%a;" (Sil.pp_type_decl pe pp_var pp_exp_c) typ in
|
|
Code.add_from_pp code pp in
|
|
IList.iter do_parameter parameters;
|
|
IdMap.iter do_vinfo idmap
|
|
|
|
(** initialize variables not requiring allocation *)
|
|
let gen_init_vars code solutions idmap =
|
|
let get_const id c =
|
|
try Sil.Cint (IdMap.find id solutions)
|
|
with Not_found -> c in
|
|
let do_vinfo id { typ = typ; alloc = alloc } =
|
|
if not alloc then
|
|
let const = match typ with
|
|
| Sil.Tint _ | Sil.Tvoid ->
|
|
get_const id (Sil.Cint Sil.Int.zero)
|
|
| Sil.Tfloat _ ->
|
|
Sil.Cfloat 0.0
|
|
| Sil.Tptr _ ->
|
|
get_const id (Sil.Cint Sil.Int.zero)
|
|
| Sil.Tfun _ ->
|
|
Sil.Cint Sil.Int.zero
|
|
| typ ->
|
|
L.err "do_vinfo type undefined: %a@." (Sil.pp_typ_full pe) typ;
|
|
assert false in
|
|
let pp fmt () =
|
|
F.fprintf fmt "%a = (%a) %a;"
|
|
pp_id_c id (Sil.pp_typ_full pe) typ (Sil.pp_exp pe) (Sil.Const const) in
|
|
Code.add_from_pp code pp in
|
|
IdMap.iter do_vinfo idmap
|
|
|
|
(** apply a filter to the identifiers of an idmap *)
|
|
let filter_idmap filter idmap =
|
|
let idmap' = ref IdMap.empty in
|
|
IdMap.iter (fun id x -> if filter id then idmap' := IdMap.add id x !idmap') idmap;
|
|
!idmap'
|
|
|
|
let pp_svars fmt svars =
|
|
if svars != [] then F.fprintf fmt "%a" (pp_comma_seq pp_id_c) svars
|
|
|
|
|
|
let gen_hpara code proc_name spec_num env id hpara =
|
|
let mk_name = mk_lseg_name id proc_name spec_num in
|
|
let size_name = mk_size_name id in
|
|
let pp1 f () =
|
|
F.fprintf f "void* %s(int %s, void* %a%a) {"
|
|
mk_name size_name pp_id_c hpara.Sil.next pp_svars hpara.Sil.svars in
|
|
let pp2 f () =
|
|
F.fprintf f "%a= %s(%s -1 , %a%a);"
|
|
pp_id_c hpara.Sil.next mk_name size_name pp_id_c hpara.Sil.next pp_svars hpara.Sil.svars in
|
|
let line1 = pp_to_string pp1 () in
|
|
let idmap = create_idmap hpara.Sil.body in
|
|
let idmap_ex =
|
|
let filter i =
|
|
IList.exists (Ident.equal i) hpara.Sil.evars in
|
|
filter_idmap filter idmap in
|
|
let idmap_no_next =
|
|
let filter i =
|
|
not (Ident.equal i hpara.Sil.next) in
|
|
filter_idmap filter idmap in
|
|
let line11 = "if ("^size_name^" == 0) {" in
|
|
let line12 = "return " ^ (pp_to_string pp_id_c hpara.Sil.next) ^ ";" in
|
|
let line13 ="} else {" in
|
|
let line14 = pp_to_string pp2 () in
|
|
let line2 = "return " ^ (pp_to_string pp_id_c hpara.Sil.root) ^ ";" in
|
|
let line3 = "}" in
|
|
Code.add_line code line1;
|
|
Code.set_indent " ";
|
|
gen_var_decl code idmap_no_next [];
|
|
Code.add_line code line11;
|
|
Code.set_indent " ";
|
|
Code.add_line code line12;
|
|
Code.set_indent " ";
|
|
Code.add_line code line13;
|
|
Code.set_indent " ";
|
|
Code.add_line code line14;
|
|
gen_init_vars code IdMap.empty idmap_ex;
|
|
gen_sigma code proc_name spec_num env hpara.Sil.body;
|
|
Code.add_line code line2;
|
|
Code.set_indent " ";
|
|
Code.add_line code line3;
|
|
Code.set_indent "";
|
|
Code.add_line code line3;
|
|
Code.add_line code ""
|
|
|
|
let gen_hpara_dll _ _ _ _ _ _ = assert false
|
|
|
|
(** Generate epilog for the test case *)
|
|
let gen_epilog code proc_name (parameters : (Mangled.t * Sil.typ) list) =
|
|
let pp_parameter fmt (name, _) = Mangled.pp fmt name in
|
|
let pp f () = F.fprintf f "%a(%a);" Procname.pp proc_name (pp_comma_seq pp_parameter) parameters in
|
|
let line1 = pp_to_string pp () in
|
|
let line2 = "}" in
|
|
Code.add_line code line1;
|
|
Code.set_indent "";
|
|
Code.add_line code line2
|
|
|
|
let test_function_name proc_name spec_num =
|
|
"unit_test_" ^ Procname.to_string proc_name ^ "_spec" ^ string_of_int spec_num
|
|
|
|
(** Generate prolog for test case *)
|
|
let gen_prolog code fname proc_name spec_num =
|
|
let pp f () =
|
|
let fun_name = test_function_name proc_name spec_num in
|
|
F.fprintf f "/**@\n *@\n * Procedure: %a()@\n * File: %s@\n" Procname.pp proc_name fname;
|
|
F.fprintf f " * Unit Test: %s()@\n * Created: %a@\n *@\n */@\n@\n" fun_name pp_current_time ();
|
|
F.fprintf f "void %s() {" fun_name in
|
|
Code.add_from_pp code pp;
|
|
Code.set_indent " "
|
|
|
|
let solve_constraints pure idmap =
|
|
let vars = ref [] in
|
|
let do_vinfo id { alloc } =
|
|
if not alloc then vars := !vars @ [id] in
|
|
IdMap.iter do_vinfo idmap;
|
|
Constraint.solve_from_pure pure !vars
|
|
|
|
(** generate a unit test form a spec *)
|
|
let genunit fname proc_name spec_num parameters spec =
|
|
let pre = Specs.Jprop.to_prop spec.Specs.pre in
|
|
let pure = Prop.get_pure pre in
|
|
let sigma = Prop.get_sigma pre in
|
|
let env = Prop.prop_pred_env pre in
|
|
let idmap = create_idmap sigma in
|
|
let code = Code.empty () in
|
|
Sil.Predicates.iter env
|
|
(gen_hpara code proc_name spec_num env)
|
|
(gen_hpara_dll code proc_name spec_num env);
|
|
gen_prolog code fname proc_name spec_num;
|
|
gen_var_decl code idmap parameters;
|
|
gen_init_vars code (solve_constraints pure idmap) idmap;
|
|
gen_init_equalities code pure;
|
|
gen_sigma code proc_name spec_num env sigma;
|
|
gen_epilog code proc_name parameters;
|
|
code
|
|
|
|
(** generate code for a main calling all the unit test functions passed as argument *)
|
|
let genmain proc_numspecs_list =
|
|
let code = Code.empty () in
|
|
let do_one_proc (proc_name, num_specs) =
|
|
for i = 1 to num_specs do
|
|
let test_fun_name = test_function_name proc_name i in
|
|
let line = test_fun_name ^ "();" in
|
|
Code.add_line code line done in
|
|
Code.add_line code "int main() {";
|
|
Code.set_indent " ";
|
|
IList.iter do_one_proc proc_numspecs_list;
|
|
Code.add_line code "printf(\"unit test terminated\\n\");";
|
|
Code.add_line code "return 0;";
|
|
Code.set_indent "";
|
|
Code.add_line code "}";
|
|
code
|