From f87dfdaa60c5117ece2c535594bdbe144d846518 Mon Sep 17 00:00:00 2001
From: Valentin Perrelle <valentin.perrelle@cea.fr>
Date: Thu, 3 Dec 2020 18:45:37 +0100
Subject: [PATCH] [Eva] api: Sketches of a new API for Eva

---
 Makefile                            |   2 +-
 src/plugins/value/utils/results.ml  | 428 ++++++++++++++++++++++++++++
 src/plugins/value/utils/results.mli |  90 ++++++
 3 files changed, 519 insertions(+), 1 deletion(-)
 create mode 100644 src/plugins/value/utils/results.ml
 create mode 100644 src/plugins/value/utils/results.mli

diff --git a/Makefile b/Makefile
index 0dd665f8262..478ee8f3ab6 100644
--- a/Makefile
+++ b/Makefile
@@ -908,7 +908,7 @@ PLUGIN_CMO:= partitioning/split_strategy domains/domain_mode value_parameters \
 	domains/taint_domain \
 	$(APRON_CMO) $(NUMERORS_CMO) \
 	api/general_requests api/values_request \
-	utils/unit_tests
+	utils/unit_tests utils/results
 PLUGIN_CMI:= values/abstract_value values/abstract_location \
 	domains/abstract_domain domains/simpler_domains
 PLUGIN_DEPENDENCIES:=Server
diff --git a/src/plugins/value/utils/results.ml b/src/plugins/value/utils/results.ml
new file mode 100644
index 00000000000..97f4dffd350
--- /dev/null
+++ b/src/plugins/value/utils/results.ml
@@ -0,0 +1,428 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2021                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Bottom.Type
+
+type 'a or_top_bottom = 'a Bottom.Top.or_top_bottom
+
+let (>>>-) t f = match t with
+| `Top -> `Top
+| `Bottom  -> `Bottom
+| `Value t -> f t
+
+exception Not_implemented
+
+module Callstack = Value_types.Callstack
+
+type callstack = Callstack.t
+type 'a by_callstack = (callstack * 'a) list
+
+type control_point =
+  | Initial
+  | Final
+  | Start of Cil_types.kernel_function
+  | End of Cil_types.kernel_function
+  | Before of Cil_types.stmt
+  | After of Cil_types.stmt
+
+type request = {
+  control_point : control_point;
+  selector : Callstack.Set.t option;
+  filter: (callstack -> bool) option;
+}
+
+type error = Bottom | Top | DisabledDomain
+type 'a result = ('a,error) Result.t
+
+(* Building requests *)
+
+let make control_point = {
+  control_point;
+  selector = None;
+  filter = None;
+}
+
+let before stmt = make (Before stmt)
+let after stmt = make (After stmt)
+let at_start_of kf = make (Start kf)
+let at_end_of kf = make (End kf)
+let at_start = make Initial
+let at_end = make Final
+
+let before_kinstr = function
+  | Cil_types.Kglobal -> at_start
+  | Kstmt stmt -> before stmt
+
+let after_kinstr = function
+  | Cil_types.Kglobal -> at_end
+  | Kstmt stmt -> after stmt
+
+let in_callstacks l req =
+  let set = Callstack.Set.of_list l in
+  {
+    req with
+    selector = Some (Option.fold
+      ~none:set
+      ~some:(Callstack.Set.inter set)
+      req.selector)
+  }
+
+let in_callstack cs req =
+  in_callstacks [cs] req
+  
+let filter_callstack f req = {
+    req with
+    filter = Some (Option.fold ~none:f ~some:(fun g x -> g x && f x) req.filter)
+  }
+
+
+(* Manipulating request results *)
+
+module Response =
+struct
+  type 'a t =
+    [ `Consolidated of 'a
+    | `ByCallstack of 'a by_callstack
+    | `Top
+    | `Bottom
+    ]
+
+  (* Constructors *)
+
+  let consolidated =
+    function
+    | `Bottom -> `Bottom
+    | `Value state -> `Consolidated state
+
+  let singleton cs =
+    function
+    | `Bottom -> `Bottom
+    | `Value state -> `ByCallstack [cs,state]
+
+  let by_callstack req table =
+    match table with
+    | `Top -> `Top
+    | `Bottom -> `Bottom
+    | `Value table ->
+      (* Selection *)
+      let l =
+        match req.selector with
+        | None ->
+          let add cs state acc =
+            (cs,state) :: acc
+          in
+          Callstack.Hashtbl.fold add table []
+        | Some selector ->
+          let add cs acc =
+            match Callstack.Hashtbl.find_opt table cs with
+            | Some state -> (cs,state) :: acc
+            | None -> acc
+          in
+          Callstack.Set.fold add selector []
+      in
+      (* Filter *)
+      let l =
+        match req.filter with
+        | None -> l
+        | Some filter -> List.filter (fun (cs,_) -> filter cs) l
+      in
+      `ByCallstack l
+
+  (* Accessors *)
+
+  let callstacks = function
+    | `ByCallstack l -> List.map fst l
+    | `Top | `Bottom -> [] (* What else to do when Top is given ? *) 
+
+  (* Map *)
+
+  let map f = function
+    | `Consolidated v -> `Consolidated (f v)
+    | `ByCallstack l -> `ByCallstack (List.map (fun (cs,x) -> cs,f x) l)
+    | `Top -> `Top
+    | `Bottom -> `Bottom
+
+  (* Reduction *)
+
+  let map_reduce
+      (default : [`Top | `Bottom] -> 'b)
+      (map : 'a -> 'b)
+      (reduce : 'b -> 'b -> 'b) : 'a t -> 'b =
+    function
+    | `Consolidated v -> map v
+    | `ByCallstack ((_,h) :: t) ->
+      List.fold_left (fun acc (_,x) -> reduce acc (map x)) (map h) t    
+    | `ByCallstack [] -> default `Bottom
+    | (`Top | `Bottom) as v -> default v
+
+  let map_join (map : 'a -> 'b) (join : 'b -> 'b -> 'b) =
+    let default = function
+      | `Bottom -> `Bottom
+      | `Top -> `Top
+    and map' x =
+      `Value (map x)
+    in
+    map_reduce default map' (Bottom.Top.join join)
+
+  let map_join' (map : 'a -> 'b or_top_bottom) (join : 'b -> 'b -> 'b) =
+    let default = function
+      | `Bottom -> `Bottom
+      | `Top -> `Top
+    and map' = (map :> 'a -> 'b or_top_bottom) in
+    map_reduce default map' (Bottom.Top.join join)
+end
+
+
+(* Extracting states and values *)
+
+module Make () =
+struct
+  module A = (val Analysis.current_analyzer ())
+  
+  type value = A.Val.t
+  type valuation = A.Eval.Valuation.t
+
+  type evaluation =
+  [ `LValue of Cil_types.lval *
+      (valuation * value Eval.flagged_value) Eval.evaluated Response.t 
+  | `Value of (valuation * value) Eval.evaluated Response.t
+  ]
+
+  let get_by_callstack req =
+    let open Response in
+    match req.control_point with
+    | Before stmt ->
+      A.get_stmt_state_by_callstack ~after:false stmt |> by_callstack req
+    | After stmt ->
+      A.get_stmt_state_by_callstack ~after:true stmt |> by_callstack req
+    | Initial ->
+      A.get_kinstr_state ~after:false Kglobal |> singleton []
+    | Start kf ->
+      A.get_initial_state_by_callstack kf |> by_callstack req
+    | Final | End _ ->
+      raise Not_implemented
+
+  let get req =
+    if Option.is_some req.filter || Option.is_some req.selector then
+      get_by_callstack req
+    else
+      let open Response in
+      match req.control_point with
+      | Before stmt ->
+        A.get_stmt_state ~after:false stmt |> consolidated
+      | After stmt ->
+        A.get_stmt_state ~after:true stmt |> consolidated
+      | _ ->
+        get_by_callstack req
+
+  let convert : 'a or_top_bottom -> 'a result = function
+    | `Top -> Result.error Top
+    | `Bottom -> Result.error Bottom
+    | `Value v -> Result.ok v
+
+  let callstacks req =
+    Response.callstacks (get_by_callstack req)
+
+  let equality_class _exp _req =
+    raise Not_implemented (* Where is the key for Equality_domain ? *)
+
+  let as_cvalue_model req =
+    match A.Dom.get Cvalue_domain.State.key with
+    | None ->
+      Result.error DisabledDomain
+    | Some extract ->
+      let extract' state =
+        fst (extract state)
+      in
+      convert (Response.map_join extract' Cvalue.Model.join (get req))
+
+  (* Evaluation *)
+
+  let eval_lval lval req =
+    let eval state =
+      A.Eval.copy_lvalue state lval
+    in
+    `LValue (lval, Response.map eval (get req))
+
+  let eval_exp exp req =
+    let eval state =
+      A.Eval.evaluate state exp
+    in
+    `Value (Response.map eval (get req))
+
+  let eval_address lval req =
+    let eval state =
+      A.Eval.lvaluate state lval
+    in
+    Response.map eval (get req)
+
+  (* Conversion *)
+
+  open Bottom.Type
+
+  let extract_value res =
+    match res with
+    | `LValue (_lval, r) ->
+      let extract (x, _alarms) =
+        x >>- (fun (_valuation,fv) -> fv.Eval.v)
+      in
+      Response.map extract r
+    | `Value r ->
+      let extract (x, _alarms) =
+        x >>-: (fun (_valuation,v) -> v)
+      in
+      Response.map extract r
+
+  let as_cvalue res =
+    match A.Val.get Main_values.CVal.key with
+    | None ->
+      Result.error DisabledDomain
+    | Some get ->
+      let join = Main_values.CVal.join in
+      let extract value =
+        (value >>-: get :> 'a or_top_bottom)
+      in
+      extract_value res |> Response.map_join' extract join |> convert
+
+  let as_functions res = 
+      let join = (@)
+      and extract value =
+        value >>>- fun v -> (fst (A.Val.resolve_functions v) :> 'a or_top_bottom)
+      in    
+      extract_value res |> Response.map_join' extract join |> convert
+end
+
+
+(* State requests *)
+
+let callstacks req =
+  let module E = Make () in
+  E.callstacks req
+
+let equality_class exp req = 
+  let module E = Make () in
+  E.equality_class exp req
+
+let as_cvalue_model req =
+  let module E = Make () in
+  E.as_cvalue_model req
+  
+(* Evaluation *)
+
+module type Evaluation =
+sig
+  type evaluation
+  val v : evaluation
+  val as_cvalue : evaluation -> Main_values.CVal.t result
+  val as_functions : evaluation -> Cil_types.kernel_function list result
+end
+
+type evaluation = (module Evaluation)
+type lvaluation
+
+let eval_lval lval req =
+  (module struct
+    include Make ()
+    let v = (eval_lval lval req :> evaluation)
+  end : Evaluation)
+
+let eval_var vi req =
+  eval_lval (Cil.var vi) req
+
+let eval_exp exp req =
+  (module struct
+    include Make ()
+    let v = (eval_exp exp req :> evaluation)
+  end : Evaluation)
+
+let eval_address _lval _req =
+  raise Not_implemented
+
+(* Value conversion *)
+
+let as_cvalue evaluation =
+  let module E = (val evaluation : Evaluation) in
+  E.as_cvalue E.v
+
+let as_ival evaluation =
+  try
+    Result.map Cvalue.V.project_ival (as_cvalue evaluation)
+  with Cvalue.V.Not_based_on_null ->
+    Result.error Top
+
+let as_fval evaluation =
+  let f ival =
+    if Ival.is_float ival
+    then Result.ok (Ival.project_float ival)
+    else Result.error Top
+  in
+  Result.bind (as_ival evaluation) f
+
+let as_float evaluation =
+  try
+    as_fval evaluation |>
+    Result.map Fval.project_float |>
+    Result.map Fval.F.to_float
+  with Fval.Not_Singleton_Float ->
+    Result.error Top
+
+let as_functions evaluation =
+  let module E = (val evaluation : Evaluation) in
+  E.as_functions E.v
+
+let as_integer evaluation =
+  try
+    Result.map Ival.project_int (as_ival evaluation)
+  with Ival.Not_Singleton_Int ->
+    Result.error Top
+
+let as_int evaluation =
+  try
+    Result.map Integer.to_int (as_integer evaluation)
+  with Z.Overflow ->
+    Result.error Top
+
+let as_kf _evaluation =
+  raise Not_implemented
+
+let as_location _lvaluation =
+  raise Not_implemented
+let as_zone _lvaluation =
+  raise Not_implemented
+
+(* Evaluation properties *)
+
+let is_initialized _evaluation =
+  raise Not_implemented
+let deps _evaluation =
+  raise Not_implemented
+let alarms _evaluation =
+  raise Not_implemented
+
+(* Bottomness *)
+
+let is_bottom _evaluation =
+  raise Not_implemented
+let is_called _kf =
+  raise Not_implemented
+let is_reachable _stmt =
+  raise Not_implemented
diff --git a/src/plugins/value/utils/results.mli b/src/plugins/value/utils/results.mli
new file mode 100644
index 00000000000..9c9e8efbada
--- /dev/null
+++ b/src/plugins/value/utils/results.mli
@@ -0,0 +1,90 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This fil2e is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2021                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+
+(* Usage sketch :
+
+  Eva.Results.(before stmt |> in_callstack cs |> eval_var vi |> as_int)
+
+  or, if you prefer
+
+  Eva.Results.(as_int (eval_var vi (in_callstack cs (before stmt))))
+*) 
+
+type callstack = (Cil_types.kernel_function * Cil_types.kinstr) list
+type 'a by_callstack = (callstack*'a) list
+
+type request
+type evaluation
+type lvaluation
+
+type error = Bottom | Top | DisabledDomain
+type 'a result = ('a,error) Result.t
+
+(* Control point selection *)
+val at_start : request
+val at_end : request
+val at_start_of : Cil_types.kernel_function -> request
+val at_end_of : Cil_types.kernel_function -> request
+val before : Cil_types.stmt -> request
+val after : Cil_types.stmt -> request
+val before_kinstr : Cil_types.kinstr -> request
+val after_kinstr : Cil_types.kinstr -> request
+
+(* Callstack selection *)
+val in_callstack : callstack -> request -> request
+val in_callstacks : callstack list -> request -> request
+val filter_callstack : (callstack -> bool) -> request -> request
+
+(* State requests *)
+val callstacks : request -> callstack list
+val equality_class : Cil_types.exp -> request -> Cil_types.exp list option
+val as_cvalue_model : request -> Cvalue.Model.t result
+
+(* Evaluation *)
+val eval_var : Cil_types.varinfo -> request -> evaluation
+val eval_lval : Cil_types.lval -> request -> evaluation
+val eval_exp : Cil_types.exp -> request -> evaluation
+
+val eval_address : Cil_types.lval -> request -> lvaluation
+
+(* Value conversion *)
+val as_int : evaluation -> int result
+val as_integer : evaluation -> Integer.t result
+val as_float : evaluation -> float result
+val as_functions : evaluation -> Cil_types.kernel_function list result
+val as_ival : evaluation -> Ival.t result
+val as_fval : evaluation -> Fval.t result
+val as_cvalue : evaluation -> Cvalue.V.t result
+
+val as_location : lvaluation -> Locations.location result
+val as_zone : lvaluation -> Locations.Zone.t result
+
+(* Evaluation properties *)
+val is_initialized : evaluation -> bool
+val deps : evaluation -> Locations.Zone.t
+val alarms : evaluation -> Alarms.t list
+
+(* Bottomness *)
+val is_bottom : evaluation -> bool
+val is_called : Cil_types.kernel_function -> bool
+val is_reachable : Cil_types.stmt -> bool
-- 
GitLab