Reviewed By: mbouaziz Differential Revision: D3544575 fbshipit-source-id: 17fa411master
parent
15534be574
commit
05505b55fd
@ -0,0 +1,104 @@
|
|||||||
|
(*
|
||||||
|
* 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 L = Logging
|
||||||
|
|
||||||
|
(** tree of (trace, access path) associations organized by structure of access paths *)
|
||||||
|
module Make (TraceDomain : AbstractDomain.S) = struct
|
||||||
|
|
||||||
|
module AccessMap = PrettyPrintable.MakePPMap(struct
|
||||||
|
type t = AccessPath.access
|
||||||
|
let compare = AccessPath.access_compare
|
||||||
|
let pp_key = AccessPath.pp_access
|
||||||
|
end)
|
||||||
|
|
||||||
|
module BaseMap = PrettyPrintable.MakePPMap(struct
|
||||||
|
type t = AccessPath.base
|
||||||
|
let compare = AccessPath.base_compare
|
||||||
|
let pp_key = AccessPath.pp_base
|
||||||
|
end)
|
||||||
|
|
||||||
|
type node = TraceDomain.astate * tree
|
||||||
|
and tree =
|
||||||
|
| Subtree of node AccessMap.t (* map from access -> nodes. a leaf is encoded as an empty map *)
|
||||||
|
| Star (* special leaf for starred access paths *)
|
||||||
|
|
||||||
|
(* map from base var -> access subtree *)
|
||||||
|
type t = node BaseMap.t
|
||||||
|
|
||||||
|
(** Here's how to represent a few different kinds of trace * access path associations:
|
||||||
|
(x, T) := { x |-> (T, Subtree {}) }
|
||||||
|
(x.f, T) := { x |-> (empty, Subtree { f |-> (T, Subtree {}) }) }
|
||||||
|
(x*, T) := { x |-> (T, Star) }
|
||||||
|
(x.f*, T) := { x |-> (empty, Subtree { f |-> (T, Star) }) }
|
||||||
|
(x, T1), (y, T2) := { x |-> (T1, Subtree {}), y |-> (T2, Subtree {}) }
|
||||||
|
(x.f, T1), (x.g, T2) := { x |-> (empty, Subtree { f |-> (T1, Subtree {}),
|
||||||
|
g |-> (T2, Subtree {}) }) }
|
||||||
|
*)
|
||||||
|
|
||||||
|
let empty = BaseMap.empty
|
||||||
|
|
||||||
|
let make_node trace subtree =
|
||||||
|
trace, Subtree subtree
|
||||||
|
|
||||||
|
let empty_node =
|
||||||
|
make_node TraceDomain.initial AccessMap.empty
|
||||||
|
|
||||||
|
let make_normal_leaf trace =
|
||||||
|
make_node trace AccessMap.empty
|
||||||
|
|
||||||
|
let make_starred_leaf trace =
|
||||||
|
trace, Star
|
||||||
|
|
||||||
|
let make_access_node base_trace access trace =
|
||||||
|
make_node base_trace (AccessMap.singleton access (make_normal_leaf trace))
|
||||||
|
|
||||||
|
let make_empty_trace_access_node trace access =
|
||||||
|
make_access_node TraceDomain.initial access trace
|
||||||
|
|
||||||
|
(** find all of the traces in [tree] and join them with [orig_trace] *)
|
||||||
|
let rec join_all_traces orig_trace tree =
|
||||||
|
let node_join_traces _ (trace, node) trace_acc =
|
||||||
|
let trace_acc' = TraceDomain.join trace_acc trace in
|
||||||
|
match node with
|
||||||
|
| Star -> trace_acc'
|
||||||
|
| Subtree subtree -> join_all_traces trace_acc' subtree in
|
||||||
|
AccessMap.fold node_join_traces tree orig_trace
|
||||||
|
|
||||||
|
(** retrieve the trace associated with [ap] from [tree] *)
|
||||||
|
let get_trace ap tree =
|
||||||
|
let rec accesses_get_trace access_list trace tree =
|
||||||
|
match access_list, tree with
|
||||||
|
| _, Star ->
|
||||||
|
trace, Star
|
||||||
|
| [], (Subtree _ as tree) ->
|
||||||
|
trace, tree
|
||||||
|
| access :: accesses, Subtree subtree ->
|
||||||
|
let access_trace, access_subtree = AccessMap.find access subtree in
|
||||||
|
accesses_get_trace accesses access_trace access_subtree in
|
||||||
|
let get_trace_ base accesses tree =
|
||||||
|
let base_trace, base_tree = BaseMap.find base tree in
|
||||||
|
accesses_get_trace accesses base_trace base_tree in
|
||||||
|
let base, accesses = AccessPath.extract ap in
|
||||||
|
match get_trace_ base accesses tree with
|
||||||
|
| trace, Star ->
|
||||||
|
Some trace
|
||||||
|
| trace, Subtree subtree ->
|
||||||
|
if AccessPath.is_exact ap
|
||||||
|
then Some trace
|
||||||
|
else
|
||||||
|
(* input query was [ap]*, and [trace] is the trace associated with [ap]. get the traces
|
||||||
|
associated with the children of [ap] in [tree] and join them with [trace] *)
|
||||||
|
Some (join_all_traces trace subtree)
|
||||||
|
| exception Not_found ->
|
||||||
|
None
|
||||||
|
end
|
@ -0,0 +1,134 @@
|
|||||||
|
(*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
(* string set domain we use to ensure we're getting the expected traces *)
|
||||||
|
module MockTraceDomain =
|
||||||
|
AbstractDomain.FiniteSet
|
||||||
|
(PrettyPrintable.MakePPSet(struct
|
||||||
|
include String
|
||||||
|
let pp_element fmt s = Format.fprintf fmt "%s" s
|
||||||
|
end))
|
||||||
|
|
||||||
|
module Domain = AccessTree.Make (MockTraceDomain)
|
||||||
|
|
||||||
|
let make_base base_str =
|
||||||
|
Pvar.mk (Mangled.from_string base_str) Procname.empty_block, Typ.Tvoid
|
||||||
|
|
||||||
|
let make_field_access access_str =
|
||||||
|
AccessPath.FieldAccess (Ident.create_fieldname (Mangled.from_string access_str) 0, Typ.Tvoid)
|
||||||
|
|
||||||
|
let make_access_path base_str accesses =
|
||||||
|
let rec make_accesses accesses_acc = function
|
||||||
|
| [] -> accesses_acc
|
||||||
|
| access_str :: l -> make_accesses ((make_field_access access_str) :: accesses_acc) l in
|
||||||
|
let accesses = make_accesses [] accesses in
|
||||||
|
make_base base_str, IList.rev accesses
|
||||||
|
|
||||||
|
let tests =
|
||||||
|
let x_base = make_base "x" in
|
||||||
|
let y_base = make_base "y" in
|
||||||
|
let z_base = make_base "z" in
|
||||||
|
|
||||||
|
let f = make_field_access "f" in
|
||||||
|
let g = make_field_access "g" in
|
||||||
|
|
||||||
|
let xF = AccessPath.Exact (make_access_path "x" ["f"]) in
|
||||||
|
let xG = AccessPath.Exact (make_access_path "x" ["g"]) in
|
||||||
|
let xFG = AccessPath.Exact (make_access_path "x" ["f"; "g"]) in
|
||||||
|
let yF = AccessPath.Exact (make_access_path "y" ["f"]) in
|
||||||
|
let yG = AccessPath.Exact (make_access_path "y" ["g"]) in
|
||||||
|
let yFG = AccessPath.Exact (make_access_path "y" ["f"; "g"]) in
|
||||||
|
let z = AccessPath.Exact (make_access_path "z" []) in
|
||||||
|
let zF = AccessPath.Exact (make_access_path "z" ["f"]) in
|
||||||
|
let zFG = AccessPath.Exact (make_access_path "z" ["f"; "g"]) in
|
||||||
|
|
||||||
|
let a_star = AccessPath.Abstracted (make_access_path "a" []) in
|
||||||
|
let x_star = AccessPath.Abstracted (make_access_path "x" []) in
|
||||||
|
let xF_star = AccessPath.Abstracted (make_access_path "x" ["f"]) in
|
||||||
|
let xG_star = AccessPath.Abstracted (make_access_path "x" ["g"]) in
|
||||||
|
let y_star = AccessPath.Abstracted (make_access_path "y" []) in
|
||||||
|
let yF_star = AccessPath.Abstracted (make_access_path "y" ["f"]) in
|
||||||
|
let z_star = AccessPath.Abstracted (make_access_path "z" []) in
|
||||||
|
|
||||||
|
let x_trace = MockTraceDomain.singleton "x" in
|
||||||
|
let y_trace = MockTraceDomain.singleton "y" in
|
||||||
|
let z_trace = MockTraceDomain.singleton "z" in
|
||||||
|
let xF_trace = MockTraceDomain.singleton "xF" in
|
||||||
|
let yF_trace = MockTraceDomain.singleton "yF" in
|
||||||
|
let xFG_trace = MockTraceDomain.singleton "xFG" in
|
||||||
|
|
||||||
|
let x_tree =
|
||||||
|
let g_subtree = Domain.make_access_node xF_trace g xFG_trace in
|
||||||
|
Domain.AccessMap.singleton f g_subtree
|
||||||
|
|> Domain.make_node x_trace in
|
||||||
|
let y_tree =
|
||||||
|
let yF_subtree = Domain.make_starred_leaf yF_trace in
|
||||||
|
Domain.AccessMap.singleton f yF_subtree
|
||||||
|
|> Domain.make_node y_trace in
|
||||||
|
let z_tree = Domain.make_starred_leaf z_trace in
|
||||||
|
let tree =
|
||||||
|
Domain.BaseMap.singleton x_base x_tree
|
||||||
|
|> Domain.BaseMap.add y_base y_tree
|
||||||
|
|> Domain.BaseMap.add z_base z_tree in
|
||||||
|
|
||||||
|
(* [tree] is:
|
||||||
|
x |-> ("x",
|
||||||
|
f |-> ("Xf",
|
||||||
|
g |-> ("xFG", {})))
|
||||||
|
y |-> ("y",
|
||||||
|
f |-> ("yF", * ))
|
||||||
|
z |-> ("z", * )
|
||||||
|
*)
|
||||||
|
|
||||||
|
let open OUnit2 in
|
||||||
|
let no_trace = "NONE" in
|
||||||
|
|
||||||
|
let get_trace_str access_path tree =
|
||||||
|
match Domain.get_trace access_path tree with
|
||||||
|
| Some trace -> pp_to_string MockTraceDomain.pp trace
|
||||||
|
| None -> no_trace in
|
||||||
|
|
||||||
|
let assert_traces_eq access_path tree expected_trace_str =
|
||||||
|
let actual_trace_str = get_trace_str access_path tree in
|
||||||
|
let pp_diff fmt (actual, expected) =
|
||||||
|
F.fprintf fmt "Expected to retrieve trace %s but got %s" expected actual in
|
||||||
|
assert_equal ~pp_diff actual_trace_str expected_trace_str in
|
||||||
|
|
||||||
|
let assert_trace_not_found access_path tree =
|
||||||
|
assert_traces_eq access_path tree no_trace in
|
||||||
|
|
||||||
|
let get_trace_test =
|
||||||
|
let get_trace_test_ _ =
|
||||||
|
(* exact access path tests *)
|
||||||
|
assert_traces_eq z tree "{ z }";
|
||||||
|
assert_traces_eq xF tree "{ xF }";
|
||||||
|
assert_traces_eq yF tree "{ yF }";
|
||||||
|
assert_traces_eq xFG tree "{ xFG }";
|
||||||
|
assert_trace_not_found xG tree;
|
||||||
|
|
||||||
|
(* starred access path tests *)
|
||||||
|
assert_traces_eq x_star tree "{ x, xF, xFG }";
|
||||||
|
assert_traces_eq xF_star tree "{ xF, xFG }";
|
||||||
|
assert_trace_not_found xG_star tree;
|
||||||
|
assert_trace_not_found a_star tree;
|
||||||
|
|
||||||
|
(* starred tree tests *)
|
||||||
|
assert_traces_eq zF tree "{ z }";
|
||||||
|
assert_traces_eq zFG tree "{ z }";
|
||||||
|
assert_traces_eq z_star tree "{ z }";
|
||||||
|
assert_traces_eq y_star tree "{ y, yF }";
|
||||||
|
assert_traces_eq yF_star tree "{ yF }";
|
||||||
|
assert_traces_eq yFG tree "{ yF }";
|
||||||
|
assert_trace_not_found yG tree in
|
||||||
|
"get_trace">::get_trace_test_ in
|
||||||
|
"access_tree_suite">:::[get_trace_test]
|
Loading…
Reference in new issue