diff --git a/Makefile b/Makefile index 977133d5e9174b99399ca09663454ca41ffe7830..9e3ac8f0cafacd58b0c57683ce897f7783cb62fa 100644 --- a/Makefile +++ b/Makefile @@ -861,7 +861,7 @@ endif # General rules for ordering files within PLUGIN_CMO: # - try to keep the legacy Value before Eva -PLUGIN_CMO:= partitioning/split_strategy value_parameters \ +PLUGIN_CMO:= partitioning/split_strategy domains/domain_mode value_parameters \ utils/value_perf utils/eva_annotations \ utils/value_util utils/red_statuses \ utils/mark_noresults \ diff --git a/headers/header_spec.txt b/headers/header_spec.txt index 2ada5971dd26cf43899de59b063ce5ae0a7d88f5..9c669cda9390d35e43e048e6019ba9aa82c09c84 100644 --- a/headers/header_spec.txt +++ b/headers/header_spec.txt @@ -1224,6 +1224,8 @@ src/plugins/value/domains/cvalue/locals_scoping.ml: CEA_LGPL_OR_PROPRIETARY src/plugins/value/domains/cvalue/locals_scoping.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/value/domains/cvalue/warn.ml: CEA_LGPL_OR_PROPRIETARY src/plugins/value/domains/cvalue/warn.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/value/domains/domain_mode.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/value/domains/domain_mode.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/value/domains/domain_builder.ml: CEA_LGPL_OR_PROPRIETARY src/plugins/value/domains/domain_builder.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/value/domains/domain_lift.ml: CEA_LGPL_OR_PROPRIETARY diff --git a/src/plugins/value/domains/domain_builder.ml b/src/plugins/value/domains/domain_builder.ml index b8b6ee7d87af66a98e082b94a747271b4aafaf24..876e52a7428012dde40a3c9d51b2a4a1e8ad0563 100644 --- a/src/plugins/value/domains/domain_builder.ml +++ b/src/plugins/value/domains/domain_builder.ml @@ -254,45 +254,29 @@ end (* -------------------------------------------------------------------------- *) -type permission = { read: bool; write: bool; } -type mode = { current: permission; calls: permission; } - -module Mode = struct - - let all = - let p = { read = true; write = true } in - { current = p; calls = p; } - - - include Datatype.Make_with_collections - (struct - include Datatype.Serializable_undefined - type t = mode - let name = "Domain_builder.Restrict_State" - let reprs = [ all ] - let compare _ _ = 0 - let equal _ _ = true - let hash _ = 0 - end) - - let merge f m1 m2 = - let merge_perm p1 p2 = - { read = f p1.read p2.read; - write = f p1.write p2.write; } - in - { current = merge_perm m1.current m2.current; - calls = merge_perm m1.calls m2.calls; } - - let join = merge (&&) - let narrow = merge (||) -end - module Restrict (Value: Abstract_value.S) (Domain: Abstract.Domain.Internal with type value = Value.t) - (Scope: sig val functions: (Kernel_function.t * mode) list end) + (Scope: sig val functions: Domain_mode.function_mode list end) = struct + open Domain_mode + + module Mode = struct + include Mode + + let merge f m1 m2 = + let merge_perm p1 p2 = + { read = f p1.read p2.read; + write = f p1.write p2.write; } + in + { current = merge_perm m1.current m2.current; + calls = merge_perm m1.calls m2.calls; } + + let join = merge (&&) + let narrow = merge (||) + end + let functions_map = List.fold_left (fun map (kf, mode) -> Kernel_function.Map.add kf mode map) diff --git a/src/plugins/value/domains/domain_builder.mli b/src/plugins/value/domains/domain_builder.mli index f63aa2e6ded7d83ca69f3557686c92cd2e09ce61..718c0c5febdfddf28f144850e7d09c4d7f588087 100644 --- a/src/plugins/value/domains/domain_builder.mli +++ b/src/plugins/value/domains/domain_builder.mli @@ -56,17 +56,9 @@ module Complete_Simple_Cvalue and type location = Precise_locs.precise_location and type state = Domain.t - -type permission = { read: bool; write: bool; } -type mode = { current: permission; calls: permission; } - -module Mode : sig - val all: mode -end - module Restrict (Value: Abstract_value.S) (Domain: Abstract.Domain.Internal with type value = Value.t) - (Scope: sig val functions: (Kernel_function.t * mode) list end) + (Scope: sig val functions: Domain_mode.function_mode list end) : Abstract.Domain.Internal with type value = Value.t and type location = Domain.location diff --git a/src/plugins/value/domains/domain_mode.ml b/src/plugins/value/domains/domain_mode.ml new file mode 100644 index 0000000000000000000000000000000000000000..4363dba60257b8e84b8cc47c7b796e82b1db34d4 --- /dev/null +++ b/src/plugins/value/domains/domain_mode.ml @@ -0,0 +1,96 @@ +(**************************************************************************) +(* *) +(* 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). *) +(* *) +(**************************************************************************) + +type permission = { read: bool; write: bool; } +type mode = { current: permission; calls: permission; } + +type function_mode = Kernel_function.t * mode + +module Mode = struct + let all_permission = { read = true; write = true; } + let no_permission = { read = false; write = false; } + + let default = { current = all_permission; calls = no_permission; } + let all = { current = all_permission; calls = all_permission; } + let none = { current = no_permission; calls = no_permission; } + + include Datatype.Make_with_collections + (struct + include Datatype.Serializable_undefined + type t = mode + let name = "Domain_mode.Mode" + let reprs = [ default ] + let compare = Transitioning.Stdlib.compare + let equal = Datatype.from_compare + let hash = Hashtbl.hash + end) + + let check str = + String.iter + (fun c -> + if Char.lowercase_ascii c <> 'r' && Char.lowercase_ascii c <> 'w' + then raise (Invalid_argument ("invalid mode " ^ str))) + str + + let of_string str = + check str; + let calls = + { read = String.contains str 'R'; + write = String.contains str 'W'; } + and current = + { read = String.contains str 'r'; + write = String.contains str 'w'; } + in + { current; calls; } + + let to_string t = + let string_if c b = if b then c else "" in + string_if "r" t.current.read ^ string_if "w" t.current.write ^ + string_if "R" t.calls.read ^ string_if "W" t.calls.write +end + +module Function_Mode = struct + include Datatype.Pair (Kernel_function) (Mode) + type key = string + + let of_string ~key ~prev:_ str = + match str with + | None -> raise (Invalid_argument ("no value bound to " ^ key)) + | Some str -> + let get_function str = + try Globals.Functions.find_by_name str + with Not_found -> raise (Invalid_argument ("no function " ^ str)) + in + match String.split_on_char '-' str with + | [ kf; "" ] -> Some (get_function kf, Mode.none) + | _ -> + match String.split_on_char '+' str with + | [ kf ] -> Some (get_function kf, Mode.default) + | [ kf; "" ] -> Some (get_function kf, Mode.all) + | [ kf; mode ] -> Some (get_function kf, Mode.of_string mode) + | _ -> raise (Invalid_argument ("invalid argument " ^ str)) + + let to_string ~key:_ = function + | None -> None + | Some (kf, mode) -> + Some (Kernel_function.get_name kf ^ "+" ^ Mode.to_string mode) +end diff --git a/src/plugins/value/domains/domain_mode.mli b/src/plugins/value/domains/domain_mode.mli new file mode 100644 index 0000000000000000000000000000000000000000..b56b75e73890cc55c0c916d499863e6060a7f891 --- /dev/null +++ b/src/plugins/value/domains/domain_mode.mli @@ -0,0 +1,35 @@ +(**************************************************************************) +(* *) +(* 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). *) +(* *) +(**************************************************************************) + +type permission = { read: bool; write: bool; } +type mode = { current: permission; calls: permission; } + +module Mode : sig + include Datatype.S_with_collections with type t = mode + val all: t +end + +type function_mode = Kernel_function.t * mode + +module Function_Mode: + Parameter_sig.Multiple_value_datatype with type key = string + and type t = function_mode diff --git a/src/plugins/value/engine/abstractions.ml b/src/plugins/value/engine/abstractions.ml index 7c7e4524ac864fd00f7c1fe4af6d68daec1ed9ce..c231134e548eea4fea2d86a977f73a8a0a5d5097 100644 --- a/src/plugins/value/engine/abstractions.ml +++ b/src/plugins/value/engine/abstractions.ml @@ -85,12 +85,14 @@ module Config = struct let configure () = let aux config (Flag domain as flag) = if Value_parameters.Domains.mem domain.name + || Value_parameters.DomainsFunction.mem domain.name then add flag config else config in let config = List.fold_left aux empty !abstractions in let aux config (Dynamic { name; experimental; priority; abstraction; }) = if Value_parameters.Domains.mem name + || Value_parameters.DomainsFunction.mem name then let abstraction = abstraction () in let flag = Flag { name; experimental; priority; abstraction; } in @@ -307,7 +309,7 @@ let eq_value: | Abstract.Value.Leaf (key, _) -> Abstract.Value.eq_type key V.key | _ -> None -let add_domain (type v) (abstraction: v abstraction) (module Acc: Acc) = +let add_domain (type v) name (abstraction: v abstraction) (module Acc: Acc) = let domain : (module internal_domain with type value = Acc.Val.t) = match abstraction.domain with | Functor make -> @@ -327,6 +329,19 @@ let add_domain (type v) (abstraction: v abstraction) (module Acc: Acc) = let module Convert = Internal_Value.Convert (Acc.Val) (Struct) in (module Domain_lift.Make (Domain) (Convert)) in + let domain : (module internal_domain with type value = Acc.Val.t) = + try + let kf_modes = Value_parameters.DomainsFunction.find name in + let module Scope = struct let functions = kf_modes end in + let module Domain = + Domain_builder.Restrict + (Acc.Val) + ((val domain)) + (Scope) + in + (module Domain) + with Not_found -> domain + in let domain : (module internal_domain with type value = Acc.Val.t) = match Abstract.Domain.(eq_structure Acc.Dom.structure Unit) with | Some _ -> domain @@ -349,7 +364,7 @@ let warn_experimental flag = let build_domain config abstract = let build (Flag flag) acc = warn_experimental flag; - add_domain flag.abstraction acc + add_domain flag.name flag.abstraction acc in (* Domains in the [config] are sorted by increasing priority: domains with higher priority are added last: they will be at the top of the domains diff --git a/src/plugins/value/value_parameters.ml b/src/plugins/value/value_parameters.ml index 70311f618f178f9f657a7a05ea338807f30a14d1..bbe2cb24f8be0976029f6d00fac8af91f560821c 100644 --- a/src/plugins/value/value_parameters.ml +++ b/src/plugins/value/value_parameters.ml @@ -213,9 +213,31 @@ let () = Domains.add_set_hook (fun _old domains -> Datatype.String.Set.iter check_domain domains) +let () = Parameter_customize.set_group domains +module DomainsFunction = + String_multiple_map + (struct + include Domain_mode.Function_Mode + let of_string ~key ~prev str = + check_domain key; + try of_string ~key ~prev str + with Invalid_argument msg -> raise (Cannot_build msg) + end) + (struct + let option_name = "-eva-domains-function" + let help = "Enables a domain only for the given functions. \ + <d:f+> enables the domain [d] from function [f] \ + (the domain is enabled in all functions called from [f]). \ + <d:f-> disables the domain [d] from function [f]." + let arg_name = "d:f" + let default = Datatype.String.Map.empty + end) +let () = add_precision_dep DomainsFunction.parameter + (* Set of parameters defining the abstractions used in an Eva analysis. *) let parameters_abstractions = - ref (Typed_parameter.Set.singleton Domains.parameter) + ref (Typed_parameter.Set.of_list + [Domains.parameter; DomainsFunction.parameter]) let () = Parameter_customize.set_group domains diff --git a/src/plugins/value/value_parameters.mli b/src/plugins/value/value_parameters.mli index 8c338bbc48c349b742d7af40e1ae39b0e4a580b8..828113a3c87ef0927de1d90ebda9ca0fe3ee5ca0 100644 --- a/src/plugins/value/value_parameters.mli +++ b/src/plugins/value/value_parameters.mli @@ -29,6 +29,9 @@ module OracleDepth: Parameter_sig.Int module ReductionDepth: Parameter_sig.Int module Domains: Parameter_sig.String_set +module DomainsFunction: Parameter_sig.Multiple_map + with type key = string + and type value = Domain_mode.function_mode module EqualityCall: Parameter_sig.String module EqualityCallFunction: