package mopsa
MOPSA: A Modular and Open Platform for Static Analysis using Abstract Interpretation
Install
Dune Dependency
Authors
Maintainers
Sources
mopsa-analyzer-v1.1.tar.gz
md5=fdee20e988343751de440b4f6b67c0f4
sha512=f5cbf1328785d3f5ce40155dada2d95e5de5cce4f084ea30cfb04d1ab10cc9403a26cfb3fa55d0f9da72244482130fdb89c286a9aed0d640bba46b7c00e09500
doc/src/toplevel/toplevel.ml.html
Source file toplevel.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 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
(****************************************************************************) (* *) (* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *) (* *) (* Copyright (C) 2017-2019 The MOPSA Project. *) (* *) (* This program is free software: 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, either version 3 of the License, or *) (* (at your option) any later version. *) (* *) (* This program 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. *) (* *) (* You should have received a copy of the GNU Lesser General Public License *) (* along with this program. If not, see <http://www.gnu.org/licenses/>. *) (* *) (****************************************************************************) (** Toplevel abstraction There are two main differences with domains. First, transfer functions are indexed by paths to enable a faster access. Second, transfer functions are not partial functions and return always a result. *) open Mopsa_utils open Core.All open Sig.Combiner.Domain (** Signature of the toplevel abstraction *) module type TOPLEVEL = sig (** {2 Abstraction header} *) (** ********************** *) type t val bottom: t val top: t val is_bottom: t -> bool (** {2 Lattice operators} *) (** ********************* *) val subset: (t, t) man -> t ctx -> t -> t -> bool val join: (t, t) man -> t ctx -> t -> t -> t val meet: (t, t) man -> t ctx -> t -> t -> t val widen: (t, t) man -> t ctx -> t -> t -> t val merge : t -> t * change_map -> t * change_map -> t (** {2 Transfer functions} *) (** ********************** *) val init : program -> (t, t) man -> t flow val exec : ?route:route -> stmt -> (t, t) man -> t flow -> t post val eval : ?route:route -> ?translate:semantic -> ?translate_when:(semantic*(expr->bool)) list -> expr -> (t, t) man -> t flow -> t eval val ask : ?route:route -> (t,'r) query -> (t, t) man -> t flow -> (t, 'r) cases (** {2 Pretty printing} *) (** ******************* *) val print_state : ?route:route -> printer -> t -> unit val print_expr : ?route:route -> (t,t) man -> t flow -> printer -> expr -> unit (***************) (** Exceptions *) (***************) exception SysBreak of t flow exception GoBackward end (*==========================================================================*) (** {2 Domain encapsulation into an abstraction} *) (*==========================================================================*) let debug fmt = Debug.debug ~channel:"framework.abstraction.toplevel" fmt (** Encapsulate a domain into a top-level abstraction *) module Make(Domain:DOMAIN_COMBINER) : TOPLEVEL with type t = Domain.t = struct let () = debug "routing table:@,%a" pp_routing_table Domain.routing_table (** {2 Abstraction header} *) (** ********************** *) type t = Domain.t let bottom = Domain.bottom let top = Domain.top let is_bottom = Domain.is_bottom (** {2 Lattice operators} *) (** ********************* *) let subset man ctx a a' = Domain.subset man ctx (a,a) (a',a') let join man ctx a a' = Domain.join man ctx (a,a) (a',a') let meet man ctx a a' = Domain.meet man ctx (a,a) (a',a') let widen man ctx a a' = Domain.widen man ctx (a,a) (a',a') let merge = Domain.merge empty_path (** {2 Caches and route maps} *) (** **************************** *) (* Cache of previous evaluations and post-conditions *) module Cache = Core.Cache.Make(struct type t = Domain.t end) (** Map giving transfer functions of each route *) module RouteMap = MapExt.Make(struct type t = route let compare = compare_route end) (** {2 Initialization} *) (** ****************** *) let init prog man : Domain.t flow = (* Initialize the context with an empty callstack *) let ctx = singleton_ctx Context.callstack_ctx_key Callstack.empty_callstack in (* The initial flow is a singleton ⊤ environment *) let flow0 = Flow.singleton ctx T_cur man.lattice.top in (* Initialize domains *) let res = match Domain.init prog man flow0 with | None -> flow0 | Some post -> post_to_flow man post in (* Initialize hooks *) let () = Hook.init () in let ctx = Hook.init_active_hooks (Flow.get_ctx res) in Flow.set_ctx ctx res (** {2 Statement execution} *) (** *********************** *) (** Build the map of exec functions *) let exec_map : (stmt -> (t,t) man -> t flow -> t post option) RouteMap.t = (* Add the initial implicit binding for toplevel route *) let map = RouteMap.singleton toplevel (Domain.exec None) in (* Iterate over all routes *) get_routes Domain.routing_table |> List.fold_left (fun map route -> if RouteMap.mem route map then map else let domains = resolve_route route Domain.routing_table in RouteMap.add route (Domain.exec (Some domains)) map ) map (** Hooks should not be activated within hooks exec/eval. The flag [inside_hook_flag] is set whenever the analyzer emits a hook event, in order to prevent subsequent exec/eval (i.e. called inside the hook) to emit hook events. *) let inside_hook_flag = ref false let inside_hook () = !inside_hook_flag let enter_hook () = assert(not (inside_hook())); inside_hook_flag := true let exit_hook () = assert(inside_hook ()); inside_hook_flag := false exception SysBreak of Domain.t flow exception GoBackward let exec ?(route = toplevel) (stmt: stmt) man (flow: Domain.t flow) : Domain.t post = let flow = if inside_hook () then flow else let () = enter_hook() in let x = Hook.on_before_exec route stmt man flow in let () = exit_hook() in match x with None -> flow | Some ctx -> Flow.set_ctx ctx flow in let fexec = try RouteMap.find route exec_map with Not_found -> Exceptions.panic_at stmt.srange "exec for %a not found" pp_route route in try let post = match skind stmt with | S_breakpoint _ -> Post.return flow | S_add_marker m when not (is_marker_enabled m) -> Post.return flow | _ -> match Cache.exec fexec route stmt man flow with | None -> if Flow.is_bottom man.lattice flow then Post.return flow else ( match skind stmt with | S_add_marker _ -> Post.return flow | _ -> Exceptions.panic_at stmt.srange "unable to analyze statement %a in %a" pp_stmt stmt pp_route route ) | Some post -> (* Check that all cases were handled *) let not_handled = Cases.exists (fun c flow -> match c with | NotHandled -> (* Not handled cases with empty flows are OK *) not (Flow.is_bottom man.lattice flow) | _ -> false ) post in if not_handled then Exceptions.panic_at stmt.srange "unable to analyze statement %a in %a" pp_stmt stmt pp_route route ; post in let clean_post = exec_cleaners man post in let minimized_post = Post.remove_duplicates man.lattice clean_post in if inside_hook () then minimized_post else let () = enter_hook() in let x = Hook.on_after_exec route stmt man flow minimized_post in let () = exit_hook () in match x with None -> minimized_post | Some ctx -> Cases.set_ctx ctx minimized_post with | Exceptions.Panic(msg, line) -> Printexc.raise_with_backtrace (Exceptions.PanicAtFrame(stmt.srange, (Flow.get_callstack flow),msg, line)) (Printexc.get_raw_backtrace()) | Exceptions.PanicAtLocation(range, msg, line) -> Printexc.raise_with_backtrace (Exceptions.PanicAtFrame(range, (Flow.get_callstack flow),msg, line)) (Printexc.get_raw_backtrace()) | Sys.Break -> raise (SysBreak flow) | Apron.Manager.Error exc -> Printexc.raise_with_backtrace (Exceptions.PanicAtFrame(stmt.srange, (Flow.get_callstack flow), Format.asprintf "Apron.Manager.Error(%a)" Apron.Manager.print_exclog exc, "")) (Printexc.get_raw_backtrace()) | e when (match e with Exit | Exceptions.PanicAtFrame _ | SysBreak _ | GoBackward -> false | _ -> true) -> Printexc.raise_with_backtrace (Exceptions.PanicAtFrame(stmt.srange, (Flow.get_callstack flow), Printexc.to_string e, "")) (Printexc.get_raw_backtrace()) (** {2 Evaluation of expressions} *) (** ***************************** *) (** Build the map of [eval] functions *) let eval_map : (expr -> (t,t) man -> t flow -> t eval option) RouteMap.t = (* Add the implicit eval for toplevel *) let map = RouteMap.singleton toplevel (Domain.eval None) in (* Iterate over all routes *) get_routes Domain.routing_table |> List.fold_left (fun map route -> if RouteMap.mem route map then map else let domains = resolve_route route Domain.routing_table in RouteMap.add route (Domain.eval (Some domains)) map ) map (** Get the actual route of the expression in case of a variable, since variable can have an intrinsic semantic *) let refine_route_with_var_semantic route e = if compare_route route toplevel = 0 then match ekind e with | E_var (v,_) -> Semantic v.vsemantic | _ -> route else route (** Evaluate sub-nodes of an expression and rebuild it *) let eval_sub_expressions ?(route=toplevel) exp man flow = let parts, builder = structure_of_expr exp in match parts with | {exprs = []; stmts = []} -> Eval.singleton exp flow | {exprs; stmts = []} -> (* Evaluate each sub-expression *) Cases.bind_list exprs (man.eval ~route) flow >>$ fun exprs flow -> (* Normalize translation maps by ensuring that sub-expression should be translated to the same semanitcs *) let semantics = List.fold_left (fun acc e -> SemanticSet.union acc (SemanticMap.bindings e.etrans |> List.map fst |> SemanticSet.of_list) ) SemanticSet.empty exprs in let exprs = List.map (SemanticSet.fold (fun s e -> if SemanticMap.mem s e.etrans then e else { e with etrans = SemanticMap.add s e e.etrans } ) semantics ) exprs in (* Rebuild the expression [exp] from its evaluated parts *) let exp = builder {exprs; stmts = []} in (* Rebuild also the translations of [exp] from the translations of its parts *) let etrans = SemanticSet.fold (fun s etrans -> let exprs = List.fold_left (fun acc e -> SemanticMap.find s e.etrans :: acc) [] exprs in let exp = builder {exprs = List.rev exprs; stmts=[]} in SemanticMap.add s exp etrans ) semantics SemanticMap.empty in Eval.singleton { exp with etrans } flow (* XXX sub-statements are not handled for the moment *) | _ -> Eval.singleton exp flow (** Evaluate not-handled cases *) let eval_not_handled ?(route=toplevel) exp man evl = (* Separate handled and not-handled cases *) let handled,not_handled = Cases.partition (fun c flow -> match c with NotHandled -> false | _ -> true) evl in let not_handled_ret = match not_handled with | None -> None | Some evl -> (* Evaluate sub-expressions of the not-handled cases *) let evl = (* Merge all not-handled cases into one *) Eval.remove_duplicates man.lattice evl >>= fun _ flow -> eval_sub_expressions ~route exp man flow in Some evl in (* Join handled and evaluated not-handled cases *) OptionExt.neutral2 Cases.join handled not_handled_ret |> OptionExt.none_to_exn (** Evaluation of expressions. *) let eval ?(route=toplevel) ?(translate=any_semantic) ?(translate_when=[]) exp man flow = (* Get the translation semantic if any *) let semantic = if not (is_any_semantic translate) then translate else match List.find_opt (fun (s,f) -> f exp) translate_when with | Some (s,_) -> s | None -> any_semantic in (* Notify hooks *) let flow = if inside_hook () then flow else let () = enter_hook() in let x = Hook.on_before_eval route semantic exp man flow in let () = exit_hook() in match x with | None -> flow | Some ctx -> Flow.set_ctx ctx flow in let route = refine_route_with_var_semantic route exp in let feval = try Cache.eval (RouteMap.find route eval_map) route with Not_found -> Exceptions.panic_at exp.erange "eval for %a not found" pp_route route in let evl = match feval exp man flow with | None -> eval_sub_expressions exp man flow | Some evl -> eval_not_handled ~route exp man evl in (* Update the expression history *) let ret = Cases.map_result (fun exp' -> if exp == exp' then exp' else let exp'' = { exp' with ehistory = exp :: exp'.ehistory } in (* Update history of the translations *) { exp'' with etrans = SemanticMap.map (fun e -> { e with ehistory = e.ehistory @ exp''.ehistory } ) exp''.etrans } ) evl in (** Return a translation if it was requested *) let ret' = if is_any_semantic semantic then ret else Cases.map_result (get_expr_translation semantic) ret in (* Notify hooks *) if inside_hook () then ret' else let () = enter_hook() in let x = Hook.on_after_eval route semantic exp man flow ret' in let () = exit_hook () in match x with | None -> ret' | Some ctx -> Cases.set_ctx ctx ret' (** {2 Handler of queries} *) (** ********************** *) let ask : type r. ?route:route -> (t,r) query -> (t,t) man -> t flow -> (t, r) cases = fun ?(route=toplevel) query man flow -> (* FIXME: the map of transfer functions indexed by routes is not constructed offline, due to the GADT query *) let domains = if compare_route route toplevel = 0 then None else Some (resolve_route route Domain.routing_table) in match Domain.ask domains query man flow with | Some r -> r | None -> match query with | Sig.Abstraction.Partitioning.Q_partition_predicate range -> Cases.singleton (mk_constant (C_bool true) ~etyp:T_bool range) flow | _ -> raise Not_found (** {2 Pretty printer of states} *) (** **************************** *) (** Build the map of [print_state] functions *) let print_state_map : (printer -> t -> unit) RouteMap.t = (* Add the implicit printer for toplevel *) let map = RouteMap.singleton toplevel (Domain.print_state None) in (* Iterate over all routes *) get_routes Domain.routing_table |> List.fold_left (fun map route -> if RouteMap.mem route map then map else let domains = resolve_route route Domain.routing_table in RouteMap.add route (Domain.print_state (Some domains)) map ) map (** Pretty print of states *) let print_state ?(route=toplevel) printer a = match RouteMap.find_opt route print_state_map with | Some f -> f printer a | None -> Exceptions.panic "pretty printer for %a not found" pp_route route (** {2 Pretty printer of expressions} *) (** ********************************* *) (** Build the map of [print_expr] functions *) let print_expr_map : ((t,t) man -> t flow -> printer -> expr -> unit) RouteMap.t = (* Add the implicit printer for toplevel *) let map = RouteMap.singleton toplevel (Domain.print_expr None) in (* Iterate over all routes *) get_routes Domain.routing_table |> List.fold_left (fun map route -> if RouteMap.mem route map then map else let domains = resolve_route route Domain.routing_table in RouteMap.add route (Domain.print_expr (Some domains)) map ) map (** Pretty print of expression values *) let print_expr ?(route=toplevel) man flow printer exp = if Print.mem_printed_expr printer exp then () else let route = refine_route_with_var_semantic route exp in match RouteMap.find_opt route print_expr_map with | Some f -> Print.add_printed_expr printer exp; f man flow printer exp | None -> Exceptions.panic_at exp.erange "pretty printer for %a not found" pp_route route end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>