-
David Bühler authored
Do not run Eva without warning when the Ivette component is open.
David Bühler authoredDo not run Eva without warning when the Ivette component is open.
server_interface.ml 10.44 KiB
(**************************************************************************)
(* *)
(* This file is part of Frama-C. *)
(* *)
(* Copyright (C) 2007-2020 *)
(* 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 Server
open Data
open Dive_types
let package = Package.package ~plugin:"dive" ~title:"Dive Services" ()
(* -------------------------------------------------------------------------- *)
(* --- State handling --- *)
(* -------------------------------------------------------------------------- *)
(* TODO: project state *)
let get_context =
let context = ref None in
fun () ->
match !context with
| Some c -> c
| None ->
if Db.Value.is_computed () then
let c = Context.create () in
context := Some c;
c
else
begin
Self.error ~once:true
"A prior Eva analysis is required to build the graphs.";
Server.Data.failure "Eva analysis not computed"
end
let global_window = ref {
perception = { backward = Some 2 ; forward = Some 1 };
horizon = { backward = None ; forward = None };
}
(* -------------------------------------------------------------------------- *)
(* --- Data types --- *)
(* -------------------------------------------------------------------------- *)
module Range : Data.S with type t = int option range =
struct
type t = int option range
let name = "range"
let descr = Markdown.plain "Parametrization of the exploration range."
let sign : t Record.signature = Record.signature ()
module Fields =
struct
let backward = Record.field sign
~name:"backward"
~descr:(Markdown.plain "range for the write dependencies")
(module Joption (Jint))
let forward = Record.field sign
~name:"forward"
~descr:(Markdown.plain "range for the read dependencies")
(module Joption (Jint))
end
module Record = (val Record.publish ~package ~name ~descr sign)
let jtype = Record.jtype
let to_json r =
Record.default |>
Record.set Fields.backward r.backward |>
Record.set Fields.forward r.forward |>
Record.to_json
let of_json js =
let r = Record.of_json js in
{
backward = Record.get Fields.backward r;
forward = Record.get Fields.forward r;
}
end
module Window : Data.S with type t = window =
struct
type t = window
let name = "explorationWindow"
let descr = Markdown.plain "Global parametrization of the exploration."
let sign : t Record.signature = Record.signature ()
module Fields =
struct
let perception = Record.field sign
~name:"perception"
~descr:(Markdown.plain "how far dive will explore from root nodes ; \
must be a finite range")
(module Range)
let horizon = Record.field sign
~name:"horizon"
~descr:(Markdown.plain "range beyond which the nodes must be hidden")
(module Range)
end
module Record = (val Record.publish ~package ~name ~descr sign)
let jtype = Record.jtype
let to_json w =
Record.default |>
Record.set Fields.perception w.perception |>
Record.set Fields.horizon w.horizon |>
Record.to_json
let of_json js =
let r = Record.of_json js in
{
perception = Record.get Fields.perception r;
horizon = Record.get Fields.horizon r;
}
end
module NodeId =
struct
type t = node
let name = "nodeId"
let descr = Markdown.plain "A node identifier in the graph"
let jtype = Data.declare ~package ~name ~descr Data.Jint.jtype
let _to_json node =
`Int node.node_key
let of_json json =
let node_key = Data.Jint.of_json json in
try
Context.find_node (get_context ()) node_key
with Not_found ->
Data.failure "no node '%d' in the current graph" node_key
end
module Callsite =
struct
let name = "callsite"
let descr = Markdown.plain "A callsite"
let jtype = Data.declare ~package ~name ~descr (Jrecord [
"fun", Jstring;
"instr", Junion [ Jnumber ; Jstring ];
])
end
module Callstack =
struct
let name = "callstack"
let descr = Markdown.plain "The callstack context for a node"
let jtype = Data.declare ~package ~name ~descr (Jarray Callsite.jtype)
end
module NodeLocality =
struct
let name = "nodeLocality"
let descr = Markdown.plain "The description of a node locality"
let jtype = Data.declare ~package ~name ~descr (Jrecord [
"file", Jstring;
"callstack", Joption (Callstack.jtype)
])
end
module Node =
struct
let name = "node"
let descr = Markdown.plain "A graph node"
let jtype = Data.declare ~package ~name ~descr (Jrecord [
"id", NodeId.jtype;
"label", Jstring;
"kind", Jstring;
"locality", NodeLocality.jtype;
"is_root", Jboolean;
"backward_explored", Jstring;
"forward_explored", Jstring;
"writes", Jarray Kernel_ast.KfMarker.jtype;
"values", Joption Jstring;
"range", Junion [ Jnumber ; Jstring ];
"type", Joption Jstring
])
end
module Dependency =
struct
let name = "dependency"
let descr = Markdown.plain "The dependency between two nodes"
let jtype = Data.declare ~package ~name ~descr (Jrecord [
"id", Jnumber ;
"src", NodeId.jtype ;
"dst", NodeId.jtype ;
"kind", Jstring ;
"origins", Jarray Kernel_ast.KfMarker.jtype
])
end
module Graph =
struct
type t = Dive_graph.t
let name = "graphData"
let descr = Markdown.plain "The whole graph being built"
let jtype = Data.declare ~package ~name ~descr (Jrecord [
"nodes", Jarray Node.jtype;
"deps", Jarray Dependency.jtype
])
let to_json = Dive_graph.to_json
end
module GraphDiff =
struct
type t = Dive_graph.t * graph_diff
let name = "diffData"
let descr = Markdown.plain "Graph differences from the last action."
let jtype = Data.declare ~package ~name ~descr (Jrecord [
"root", Joption NodeId.jtype;
"add", Jrecord [
"nodes", Jarray Node.jtype;
"deps", Jarray Dependency.jtype
];
"sub", Jarray NodeId.jtype
])
let to_json = fun (g,d) -> Dive_graph.diff_to_json g d
end
(* -------------------------------------------------------------------------- *)
(* --- Actions --- *)
(* -------------------------------------------------------------------------- *)
let result context last_root =
let diff = Context.take_last_diff context in
Context.get_graph context, { diff with last_root }
let finalize' context node_opt =
begin match node_opt with
| None -> ()
| Some node ->
let may_explore f =
Extlib.may (fun depth -> f ~depth context node)
in
may_explore Build.explore_backward !global_window.perception.backward;
may_explore Build.explore_forward !global_window.perception.forward;
let horizon = !global_window.horizon in
if Extlib.has_some horizon.forward ||
Extlib.has_some horizon.backward
then
Build.reduce_to_horizon context horizon node
end;
result context node_opt
let finalize context node =
finalize' context (Some node)
let () = Request.register ~package
~kind:`SET ~name:"window"
~descr:(Markdown.plain "Set the exploration window")
~input:(module Window) ~output:(module Data.Junit)
(fun window -> global_window := window)
let () = Request.register ~package
~kind:`GET ~name:"graph"
~descr:(Markdown.plain "Retrieve the whole graph")
~input:(module Data.Junit) ~output:(module Graph)
(fun () -> Context.get_graph (get_context ()))
let () = Request.register ~package
~kind:`EXEC ~name:"clear"
~descr:(Markdown.plain "Erase the graph and start over with an empty one")
~input:(module Data.Junit) ~output:(module Data.Junit)
(fun () -> Context.clear (get_context ()))
let () = Request.register ~package
~kind:`EXEC ~name:"add"
~descr:(Markdown.plain "Add a node to the graph")
~input:(module Kernel_ast.Marker) ~output:(module GraphDiff)
begin fun loc ->
let context = get_context () in
finalize' context (Build.add_localizable context loc)
end
let () = Request.register ~package
~kind:`EXEC ~name:"explore"
~descr:(Markdown.plain "Explore the graph starting from an existing vertex")
~input:(module NodeId) ~output:(module GraphDiff)
begin fun node ->
let context = get_context () in
Build.show context node;
finalize context node
end
let () = Request.register ~package
~kind:`EXEC ~name:"show"
~descr:(Markdown.plain "Show the dependencies of an existing vertex")
~input:(module NodeId) ~output:(module GraphDiff)
begin fun node ->
let context = get_context () in
Build.show context node;
Build.explore_backward ~depth:1 context node;
finalize' context None
end
let () = Request.register ~package
~kind:`EXEC ~name:"hide"
~descr:(Markdown.plain "Hide the dependencies of an existing vertex")
~input:(module NodeId) ~output:(module GraphDiff)
begin fun node ->
let context = get_context () in
Build.hide context node;
finalize' context None
end