You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
4.3 KiB

(*
* Copyright (c) 2016 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
open! Utils
module F = Format
module type S = sig
type astate
val initial : astate
val is_bottom : astate -> bool
val (<=) : lhs:astate -> rhs:astate -> bool (* fst \sqsubseteq snd? *)
val join : astate -> astate -> astate
val widen : prev:astate -> next:astate -> num_iters:int -> astate
val pp : F.formatter -> astate -> unit
end
(** Lift a domain without bottom to a domain with bottom. *)
module BottomLifted (Domain : S) = struct
type astate =
| Bottom
| NonBottom of Domain.astate
let is_bottom astate =
astate = Bottom
let initial = NonBottom Domain.initial
let (<=) ~lhs ~rhs =
if lhs == rhs
then true
else
match lhs, rhs with
| Bottom, _ -> true
| _ , Bottom -> false
| NonBottom lhs, NonBottom rhs -> Domain.(<=) ~lhs ~rhs
let join astate1 astate2 =
if astate1 == astate2
then astate1
else
match astate1, astate2 with
| Bottom, _ -> astate2
| _, Bottom -> astate1
| NonBottom a1, NonBottom a2 -> NonBottom (Domain.join a1 a2)
let widen ~prev ~next ~num_iters =
if prev == next
then prev
else
match prev, next with
| Bottom, _ -> next
| _, Bottom -> prev
| NonBottom prev, NonBottom next -> NonBottom (Domain.widen ~prev ~next ~num_iters)
let pp fmt = function
| Bottom -> F.fprintf fmt "_|_"
| NonBottom astate -> Domain.pp fmt astate
end
(** Cartestian product of two domains. *)
module Pair (Domain1 : S) (Domain2 : S) = struct
type astate = Domain1.astate * Domain2.astate
let is_bottom (astate1, astate2) =
Domain1.is_bottom astate1 || Domain2.is_bottom astate2
let initial = Domain1.initial, Domain2.initial
let (<=) ~lhs ~rhs =
if lhs == rhs
then true
else
match is_bottom lhs, is_bottom rhs with
| true, _ -> true
| _, true -> false
| _ ->
Domain1.(<=) ~lhs:(fst lhs) ~rhs:(fst rhs) && Domain2.(<=) ~lhs:(snd lhs) ~rhs:(snd rhs)
let join astate1 astate2 =
if astate1 == astate2
then astate1
else
match is_bottom astate1, is_bottom astate2 with
| true, _ -> astate2
| _, true -> astate1
| _ -> Domain1.join (fst astate1) (fst astate2), Domain2.join (snd astate1) (snd astate2)
let widen ~prev ~next ~num_iters =
if prev == next
then prev
else
Domain1.widen ~prev:(fst prev) ~next:(fst next) ~num_iters,
Domain2.widen ~prev:(snd prev) ~next:(snd next) ~num_iters
let pp fmt (astate1, astate2) =
F.fprintf fmt "(%a, %a)" Domain1.pp astate1 Domain2.pp astate2
end
(** Lift a set to a domain. The elements of the set should be drawn from a *finite* collection of
possible values, since the widening operator here is just union. *)
module FiniteSet (S : PrettyPrintable.PPSet) = struct
include S
type astate = t
let initial = empty
let is_bottom _ = false
let (<=) ~lhs ~rhs =
if lhs == rhs
then true
else subset lhs rhs
let join astate1 astate2 =
if astate1 == astate2
then astate1
else union astate1 astate2
let widen ~prev ~next ~num_iters:_ =
join prev next
end
(** Lift a map whose value type is an abstract domain to a domain. *)
module Map (M : PrettyPrintable.PPMap) (ValueDomain : S) = struct
include M
type astate = ValueDomain.astate M.t
let initial = M.empty
let is_bottom _ = false
(** true if all keys in [lhs] are in [rhs], and each lhs value <= corresponding rhs value *)
let (<=) ~lhs ~rhs =
if lhs == rhs
then true
else
M.for_all
(fun k lhs_v ->
try ValueDomain.(<=) ~lhs:lhs_v ~rhs:(M.find k rhs)
with Not_found -> false)
lhs
let join astate1 astate2 =
if astate1 == astate2
then astate1
else
M.merge
(fun _ v1_opt v2_opt -> match v1_opt, v2_opt with
| Some v1, Some v2 -> Some (ValueDomain.join v1 v2)
| Some v, _ | _, Some v -> Some v
| None, None -> None)
astate1
astate2
let widen ~prev ~next ~num_iters:_ =
join prev next
let pp fmt astate =
M.pp ~pp_value:ValueDomain.pp fmt astate
end