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.

199 lines
5.7 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! IStd
module F = Format
module AccessPathSetDomain = AbstractDomain.InvertedSet(AccessPath.UntypedRawSet)
module TraceElem = struct
module Kind = AccessPath.Raw
type t = {
site : CallSite.t;
kind : Kind.t;
} [@@deriving compare]
let call_site { site; } = site
let kind { kind; } = kind
let make kind site = { kind; site; }
let with_callsite t site = { t with site; }
let pp fmt { site; kind; } =
F.fprintf fmt "Unprotected access to %a at %a" Kind.pp kind CallSite.pp site
module Set = PrettyPrintable.MakePPSet (struct
type nonrec t = t
let compare = compare
let pp = pp
end)
end
let make_access kind loc =
let site = CallSite.make Procname.empty_block loc in
TraceElem.make kind site
module LocksDomain = AbstractDomain.BooleanAnd
module PathDomain = SinkTrace.Make(TraceElem)
module Attribute = struct
type t =
| OwnedIf of int option
| Functional
[@@deriving compare]
let pp fmt = function
| OwnedIf None -> F.fprintf fmt "Owned"
| OwnedIf (Some formal_index) -> F.fprintf fmt "Owned if formal %d is owned" formal_index
| Functional -> F.fprintf fmt "Functional"
let unconditionally_owned = OwnedIf None
module Set = PrettyPrintable.MakePPSet(struct
type nonrec t = t
let compare = compare
let pp = pp
end)
end
module AttributeSetDomain = AbstractDomain.InvertedSet (Attribute.Set)
module AttributeMapDomain = struct
include AbstractDomain.InvertedMap (AccessPath.UntypedRawMap) (AttributeSetDomain)
let has_attribute access_path attribute t =
try
find access_path t
|> AttributeSetDomain.mem attribute
with Not_found ->
false
let add_attribute access_path attribute t =
let attribute_set =
(try find access_path t
with Not_found -> AttributeSetDomain.empty)
|> AttributeSetDomain.add attribute in
add access_path attribute_set t
end
module AccessPrecondition = struct
type t =
| Protected
| ProtectedIf of int option
[@@deriving compare]
let unprotected = ProtectedIf None
let pp fmt = function
| Protected -> F.fprintf fmt "Protected"
| ProtectedIf (Some index) -> F.fprintf fmt "ProtectedIf %d" index
| ProtectedIf None -> F.fprintf fmt "Unprotected"
module Map = PrettyPrintable.MakePPMap(struct
type nonrec t = t
let compare = compare
let pp = pp
end)
end
module AccessDomain = struct
include AbstractDomain.Map (AccessPrecondition.Map) (PathDomain)
let add_access precondition access_path t =
let precondition_accesses =
try find precondition t
with Not_found -> PathDomain.empty in
let precondition_accesses' = PathDomain.add_sink access_path precondition_accesses in
add precondition precondition_accesses' t
let get_accesses precondition t =
try find precondition t
with Not_found -> PathDomain.empty
end
type astate =
{
locks : LocksDomain.astate;
reads : PathDomain.astate;
writes : AccessDomain.astate;
id_map : IdAccessPathMapDomain.astate;
attribute_map : AttributeMapDomain.astate;
}
type summary =
LocksDomain.astate * PathDomain.astate * AccessDomain.astate * AttributeSetDomain.astate
let empty =
let locks = false in
let reads = PathDomain.empty in
let writes = AccessDomain.empty in
let id_map = IdAccessPathMapDomain.empty in
let attribute_map = AccessPath.UntypedRawMap.empty in
{ locks; reads; writes; id_map; attribute_map; }
let (<=) ~lhs ~rhs =
if phys_equal lhs rhs
then true
else
LocksDomain.(<=) ~lhs:lhs.locks ~rhs:rhs.locks &&
PathDomain.(<=) ~lhs:lhs.reads ~rhs:rhs.reads &&
AccessDomain.(<=) ~lhs:lhs.writes ~rhs:rhs.writes &&
IdAccessPathMapDomain.(<=) ~lhs:lhs.id_map ~rhs:rhs.id_map &&
AttributeMapDomain.(<=) ~lhs:lhs.attribute_map ~rhs:rhs.attribute_map
let join astate1 astate2 =
if phys_equal astate1 astate2
then
astate1
else
let locks = LocksDomain.join astate1.locks astate2.locks in
let reads = PathDomain.join astate1.reads astate2.reads in
let writes = AccessDomain.join astate1.writes astate2.writes in
let id_map = IdAccessPathMapDomain.join astate1.id_map astate2.id_map in
let attribute_map = AttributeMapDomain.join astate1.attribute_map astate2.attribute_map in
{ locks; reads; writes; id_map; attribute_map; }
let widen ~prev ~next ~num_iters =
if phys_equal prev next
then
prev
else
let locks = LocksDomain.widen ~prev:prev.locks ~next:next.locks ~num_iters in
let reads = PathDomain.widen ~prev:prev.reads ~next:next.reads ~num_iters in
let writes = AccessDomain.widen ~prev:prev.writes ~next:next.writes ~num_iters in
let id_map = IdAccessPathMapDomain.widen ~prev:prev.id_map ~next:next.id_map ~num_iters in
let attribute_map =
AttributeMapDomain.widen ~prev:prev.attribute_map ~next:next.attribute_map ~num_iters in
{ locks; reads; writes; id_map; attribute_map; }
let pp_summary fmt (locks, reads, writes, return_attributes) =
F.fprintf
fmt
"Locks: %a Reads: %a Writes: %a Return Attributes: %a"
LocksDomain.pp locks
PathDomain.pp reads
AccessDomain.pp writes
AttributeSetDomain.pp return_attributes
let pp fmt { locks; reads; writes; id_map; attribute_map; } =
F.fprintf
fmt
"Locks: %a Reads: %a Writes: %a Id Map: %a Attribute Map:\
%a"
LocksDomain.pp locks
PathDomain.pp reads
AccessDomain.pp writes
IdAccessPathMapDomain.pp id_map
AttributeMapDomain.pp attribute_map