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
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
|