(*
 * 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 PvarSet = PrettyPrintable.MakePPSet(struct
    type t = Pvar.t
    let compare = Pvar.compare
    let pp_element = (Pvar.pp pe_text)
  end)

module Domain = AbstractDomain.FiniteSet(PvarSet)

module TransferFunctions (CFG : ProcCfg.S) = struct
  module CFG = CFG
  module Domain = Domain
  type extras = ProcData.no_extras

  let rec add_address_taken_pvars exp astate = match exp with
    | Exp.Lvar pvar ->
        Domain.add pvar astate
    | Exp.Cast (_, e) | UnOp (_, e, _) | Lfield (e, _, _) ->
        add_address_taken_pvars e astate
    | Exp.BinOp (_, e1, e2) | Lindex (e1, e2) ->
        add_address_taken_pvars e1 astate
        |> add_address_taken_pvars e2
    | Exp.Exn _
    | Exp.Closure _
    | Exp.Const (Cint _ | Cfun _ | Cstr _ | Cfloat _ | Cclass _ | Cptr_to_fld _)
    | Exp.Var _ | Exp.Sizeof _ ->
        astate

  let exec_instr astate _ _ = function
    | Sil.Store (_, Tptr _, rhs_exp, _) ->
        add_address_taken_pvars rhs_exp astate
    | Sil.Call (_, _, actuals, _, _) ->
        let add_actual_by_ref astate_acc = function
          | actual_exp, Typ.Tptr _ -> add_address_taken_pvars actual_exp astate_acc
          | _ -> astate_acc in
        IList.fold_left add_actual_by_ref astate actuals
    | Sil.Store _ | Load _ | Prune _ | Nullify _ | Abstract _ | Remove_temps _
    | Declare_locals _ ->
        astate
end

module Analyzer =
  AbstractInterpreter.Make
    (ProcCfg.Exceptional) (Scheduler.ReversePostorder) (TransferFunctions)
