package goblint
Static analysis framework for C
Install
Dune Dependency
Authors
Maintainers
Sources
goblint-2.4.0.tbz
sha256=99b78e6def71534d195eef9084baa26d8334b36084e120aa6afb300c9bf8afa6
sha512=f3162bd95a03c00358a2991f6152fc6169205bfb4c55e2c483e98cc3935673df9656d025b6f1ea0fa5f1bd0aee037d4f483966b0d2907e3fa9bf11a93a3392af
doc/src/goblint.config/gobConfig.ml.html
Source file gobConfig.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
(** Configuration access. *) (** New, untyped, path-based configuration subsystem. {v path' ::== \epsilon (* *) | . <field-name> path' (* field access *) | [ <index-nr> ] path' (* array index access *) | [ + ] path' (* cons to array *) | [ - ] path' (* cons away from array *) | [ * ] path' (* reset array *) path ::== path' (* *) | <field_name> path' (* you can leave out the first dot *) v} All functions [failwith] on error. Warnings are generated in [verbose] mode. There is a "conf" [trace] option that traces setting. *) open Batteries open Printf exception ConfigError of string let building_spec = ref false module Validator = JsonSchema.Validator (struct let schema = Options.schema end) module ValidatorRequireAll = JsonSchema.Validator (struct let schema = Options.require_all end) (** The type for [gobConfig] module. *) module type S = sig (** Get JSON value at a given path. *) val get_json : string -> Yojson.Safe.t (** Directly set a JSON value; the result must conform to the schema. *) val set_json : string -> Yojson.Safe.t -> unit (** Equivalent to [get_json ""]. *) val get_conf : unit -> Yojson.Safe.t (** Equivalent to [set_conf ""]. *) val set_conf : Yojson.Safe.t -> unit (** Functions to query conf variable of type int. *) val get_int : string -> int (** Functions to modify conf variables of type int. *) val set_int : string -> int -> unit (** Functions to query conf variable of type bool. *) val get_bool : string -> bool (** Functions to modify conf variables of type bool. *) val set_bool : string -> bool -> unit (** Functions to query conf variable of type string. *) val get_string : string -> string (** Functions to modify conf variables of type string. *) val set_string : string -> string -> unit (** Functions to modify conf variables by trying to parse the value. The second argument must be valid Json except single quotes represent double quotes. *) val set_auto : string -> string -> unit (** Get a list of values *) val get_list : string -> Yojson.Safe.t list (** Get a list of strings *) val get_string_list : string -> string list (** Set a list of values *) val set_list : string -> Yojson.Safe.t list -> unit (** Write the current configuration to [filename] *) val write_file: Fpath.t -> unit (** Merge configurations from a file with current. *) val merge_file : Fpath.t -> unit (** Merge configurations from a JSON object with current. *) val merge : Yojson.Safe.t -> unit (** Check whether modification of configuration is currently allowed. *) val is_immutable : unit -> bool (** Run the given computation with modification to configuration disabled. *) val with_immutable_conf : (unit -> 'a) -> 'a end (** The implementation of the [gobConfig] module. *) module Impl : S = struct (** raise when you cannot parse the path *) exception PathParseError (** raise when there is an type error *) exception ConfTypeError (** Type of the index *) type index = Int of int (** and integer *) | App (** prepend to the list *) | Rem (** remove from the list *) | New (** create a new list *) (** Type of the path *) type path = Here (** we are there *) | Select of string * path (** we need to select an field *) | Index of index * path (** we need to select an array index *) let show_path p = let rec helper = function | Here -> [] | Select (s, p) -> Printf.sprintf ".%s" s :: helper p | Index (Int i, p) -> Printf.sprintf "[%d]" i :: helper p | Index (App, p) -> "[+]" :: helper p | Index (Rem, p) -> "[-]" :: helper p | Index (New, p) -> "[*]" :: helper p in String.concat "" (helper p) (** raise when an attempt is made to modify the configuration while it is immutable *) exception Immutable of path let () = Printexc.register_printer @@ function | Immutable p -> Some (Printf.sprintf "Immutable(%s)" (show_path p)) | _ -> None (** Helper function [split c1 c2 xs] that splits [xs] on [c1] or [c2] *) let split c1 c2 xs = let l = String.length xs in let rec split' i = if i<l then begin if xs.[i]=c1 || xs.[i]=c2 then (Str.first_chars xs i, Str.string_after xs i) else split' (i+1) end else (xs,"") in split' 0 (** Parse an index. *) let parse_index s = try if s = "+" then App else if s = "*" then New else if s = "-" then Rem else Int (int_of_string s) with Failure _ -> raise PathParseError (** Parse a string path. *) let rec parse_path' (s:string) : path = if String.length s = 0 then Here else match s.[0] with | '.' -> let fld, pth = split '.' '[' (String.lchop s) in Select (fld, parse_path' pth) | '[' -> let idx, pth = String.split (String.lchop s) ~by:"]" in Index (parse_index idx, parse_path' pth) | _ -> raise PathParseError (** Parse a string path, but you may ignore the first dot. @raise Failure if path couldn't be parsed. *) let parse_path (s:string) : path = let s = String.trim s in try if String.length s = 0 then Here else begin let fld, pth = split '.' '[' s in if fld = "" then parse_path' pth else Select (fld, parse_path' pth) end with PathParseError -> Logs.error "Error: Couldn't parse the json path '%s'" s; failwith "parsing" (* TODO: return ParsePathError still? *) (** Here we store the actual configuration. *) let json_conf : Yojson.Safe.t ref = ref `Null (** Helper function to print the conf using [printf "%t"] and alike. *) let print ch : unit = GobYojson.print ch !json_conf let write_file filename = File.with_file_out (Fpath.to_string filename) print (** Main function to receive values from the conf. *) let rec get_value (o : Yojson.Safe.t) pth = match o, pth with | o, Here -> o | `Assoc m, Select (key,pth) -> begin try get_value (List.assoc key m) pth with Not_found -> try get_value (List.assoc Options.defaults_additional_field m) pth (* if schema specifies additionalProperties, then use the default from that *) with Not_found -> raise ConfTypeError end | `List a, Index (Int i, pth) -> begin try get_value (List.at a i) pth with Invalid_argument _ -> raise ConfTypeError end | _, _ -> raise ConfTypeError (** Recursively create the value for some new path. *) let rec create_new v = function | Here -> v | Select (key,pth) -> `Assoc [(key,create_new v pth)] | Index (_, pth) -> `List [create_new v pth] (** Helper function to decide if types in the json conf have changed. *) let json_type_equals x y = match x, y with | `String _, `String _ | `Int _, `Int _ | `Int _, `Intlit _ | `Intlit _, `Int _ | `Intlit _, `Intlit _ | `Assoc _, `Assoc _ | `List _, `List _ | `Bool _ , `Bool _ | `Null , `Null -> true (* TODO: other Yojson cases *) | _ -> false exception TypeError of Yojson.Safe.t * Yojson.Safe.t let () = Printexc.register_printer (function | TypeError (o, new_v) -> Some (sprintf2 "GobConfig.Impl.TypeError (%a, %a)" GobYojson.print o GobYojson.print new_v) | _ -> None (* for other exceptions *) ) (** (Global) flag to disallow modification of the configuration. *) let immutable = ref false let set_immutable = (:=) immutable let is_immutable () = !immutable let with_immutable_conf f = (* allow nesting *) if is_immutable () then f () else ( set_immutable true; Fun.protect ~finally:(fun () -> set_immutable false) f ) (** The main function to write new values into the conf. Use [set_value] to properly invalidate cache and check immutability. @raise Failure @raise TypeError @raise Invalid_argument @raise Json_encoding.Cannot_destruct *) let unsafe_set_value v o orig_pth = let rec set_value v o pth = match o, pth with | `Assoc m, Select (key,pth) -> let rec modify = function | [] -> [(key, create_new v pth)] (* create new key, validated by schema *) | (key', v') :: kvs when key' = key -> (key, set_value v v' pth) :: kvs | (key', v') :: kvs -> (key', v') :: modify kvs in `Assoc (modify m) | `List a, Index (Int i, pth) -> `List (List.modify_at i (fun o -> set_value v o pth) a) | `List a, Index (App, pth) -> `List (a @ [create_new v pth]) | `List a, Index (Rem, pth) -> let original_list = a in let excluded_elem = create_new v pth in let filtered_list = List.filter (fun elem -> match (elem, excluded_elem) with | (`String s1, `String s2) -> not (String.equal s1 s2) | (_, _) -> failwith "At the moment it's only possible to remove a string from an array." ) original_list in `List filtered_list | `List _, Index (New, pth) -> `List [create_new v pth] | `Null, _ -> create_new v pth | _ -> let new_v = create_new v pth in if not (json_type_equals o new_v) then raise (TypeError (o, new_v)); new_v in o := set_value v !o orig_pth; Validator.validate_exn !json_conf (** Helper function for reading values. Handles error messages. *) let get_path_string f st = try let st = String.trim st in let x = get_value !json_conf (parse_path st) in if Goblint_tracing.tracing then Goblint_tracing.trace "conf-reads" "Reading '%s', it is %a." st GobYojson.pretty x; try f x with Yojson.Safe.Util.Type_error (s, _) -> Logs.error "The value for '%s' has the wrong type: %s" st s; failwith "get_path_string" with ConfTypeError -> Logs.Batteries.error "Cannot find value '%s' in\n%t\nDid You forget to add default values to options.schema.json?" st print; failwith "get_path_string" let get_json : string -> Yojson.Safe.t = get_path_string Fun.id let get_conf () = get_json "" (** Convenience functions for reading values. *) (* memoize for each type with BatCache: *) let memo gen = BatCache.make_ht ~gen ~init_size:5 (* uses hashtable; fine since our options are bounded *) let memog f = memo @@ get_path_string f let memo_int = memog Yojson.Safe.Util.to_int let memo_bool = memog Yojson.Safe.Util.to_bool let memo_string = memog Yojson.Safe.Util.to_string let memo_list = memog Yojson.Safe.Util.to_list let drop_memo () = (* The explicit polymorphism is needed to make it compile *) let drop:'a. (string,'a) BatCache.manual_cache -> _ = fun m -> let r = m.enum () in BatEnum.force r; BatEnum.iter (fun (k,v) -> m.del k) r in drop memo_int; drop memo_bool; drop memo_string; drop memo_list let wrap_get f x = (* self-observe options, which Spec construction depends on *) if !building_spec && Goblint_tracing.tracing then Goblint_tracing.trace "config" "get during building_spec: %s" x; (* TODO: blacklist such building_spec option from server mode modification since it will have no effect (spec is already built) *) f x let get_int = wrap_get memo_int.get let get_bool = wrap_get memo_bool.get let get_string = wrap_get memo_string.get let get_list = wrap_get memo_list.get let get_string_list = List.map Yojson.Safe.Util.to_string % get_list (** Helper functions for writing values. *) (** Sets a value, preventing changes when the configuration is immutable and invalidating the cache. @raise Immutable *) let set_value v o pth = if is_immutable () then raise (Immutable pth); drop_memo (); unsafe_set_value v o pth (** Helper function for writing values. Handles the tracing. @raise Failure if path couldn't be parsed. @raise Immutable @raise TypeError @raise Invalid_argument @raise Json_encoding.Cannot_destruct *) let set_path_string st v = if Goblint_tracing.tracing then Goblint_tracing.trace "conf" "Setting '%s' to %a." st GobYojson.pretty v; set_value v json_conf (parse_path st) let set_json st j = (* can't validate before updating: JSON inserted somewhere else than the root doesn't need to conform to the schema *) set_path_string st j; ValidatorRequireAll.validate_exn !json_conf let set_conf = set_json "" (** Convenience functions for writing values. *) let set_int st i = set_path_string st (`Int i) let set_bool st i = set_path_string st (`Bool i) let set_string st i = set_path_string st (`String i) let set_list st l = set_path_string st (`List l) (** The ultimate convenience function for writing values. *) let one_quote = Str.regexp "\'" let set_auto st s = try try let s' = Str.global_replace one_quote "\"" s in let v = Yojson.Safe.from_string s' in set_path_string st v with Yojson.Json_error _ | TypeError _ -> set_string st s with | Failure _ | Immutable _ | TypeError _ | Invalid_argument _ | Json_encoding.Cannot_destruct _ as e -> let bt = Printexc.get_raw_backtrace () in Logs.error "Cannot set %s to '%s'." st s; Printexc.raise_with_backtrace e bt let merge json = Validator.validate_exn json; set_conf (GobYojson.merge !json_conf json) (** Merge configurations form a file with current. *) let merge_file fn = let cwd = Fpath.v (Sys.getcwd ()) in let config_dirs = cwd :: Fpath.(parent (v Sys.executable_name)) :: Goblint_sites.conf in let file = List.find_map_opt (fun custom_include_dir -> let path = Fpath.append custom_include_dir fn in if Sys.file_exists (Fpath.to_string path) then Some path else None ) config_dirs in match file with | Some fn -> let v = Yojson.Safe.from_channel % BatIO.to_input_channel |> File.with_file_in (Fpath.to_string fn) in merge v; if Goblint_tracing.tracing then Goblint_tracing.trace "conf" "Merging with '%a', resulting\n%a." GobFpath.pretty fn GobYojson.pretty !json_conf | None -> raise (Sys_error (Printf.sprintf "%s: No such file or diretory" (Fpath.to_string fn))) end include Impl let () = set_conf Options.defaults (** Another hack to see if earlyglobs is enabled *) let earlyglobs = ref false let jobs () = match get_int "jobs" with | 0 -> Cpu.numcores () | n -> n
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>