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.

310 lines
7.8 KiB

/*
* vim: set ft=rust:
* vim: set ft=reason:
*
* Copyright (c) 2009 - 2013 Monoidics ltd.
* Copyright (c) 2013 - 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;
/** The Smallfoot Intermediate Language */
let module L = Logging;
let module F = Format;
/** Kind of global variables */
type pvar_kind =
| Local_var of Procname.t /** local variable belonging to a function */
| Callee_var of Procname.t /** local variable belonging to a callee */
| Abducted_retvar of Procname.t Location.t /** synthetic variable to represent return value */
| Abducted_ref_param of Procname.t t Location.t
/** synthetic variable to represent param passed by reference */
| Global_var /** gloval variable */
| Seed_var /** variable used to store the initial value of formal parameters */
/** Names for program variables. */
and t = {pv_name: Mangled.t, pv_kind: pvar_kind};
let rec pvar_kind_compare k1 k2 =>
switch (k1, k2) {
| (Local_var n1, Local_var n2) => Procname.compare n1 n2
| (Local_var _, _) => (-1)
| (_, Local_var _) => 1
| (Callee_var n1, Callee_var n2) => Procname.compare n1 n2
| (Callee_var _, _) => (-1)
| (_, Callee_var _) => 1
| (Abducted_retvar p1 l1, Abducted_retvar p2 l2) =>
let n = Procname.compare p1 p2;
if (n != 0) {
n
} else {
Location.compare l1 l2
}
| (Abducted_retvar _, _) => (-1)
| (_, Abducted_retvar _) => 1
| (Abducted_ref_param p1 pv1 l1, Abducted_ref_param p2 pv2 l2) =>
let n = Procname.compare p1 p2;
if (n != 0) {
n
} else {
let n = compare pv1 pv2;
if (n != 0) {
n
} else {
Location.compare l1 l2
}
}
| (Abducted_ref_param _, _) => (-1)
| (_, Abducted_ref_param _) => 1
| (Global_var, Global_var) => 0
| (Global_var, _) => (-1)
| (_, Global_var) => 1
| (Seed_var, Seed_var) => 0
}
and compare pv1 pv2 => {
let n = Mangled.compare pv1.pv_name pv2.pv_name;
if (n != 0) {
n
} else {
pvar_kind_compare pv1.pv_kind pv2.pv_kind
}
};
let equal pvar1 pvar2 => compare pvar1 pvar2 == 0;
let rec _pp f pv => {
let name = pv.pv_name;
switch pv.pv_kind {
| Local_var n =>
if !Config.pp_simple {
F.fprintf f "%a" Mangled.pp name
} else {
F.fprintf f "%a$%a" Procname.pp n Mangled.pp name
}
| Callee_var n =>
if !Config.pp_simple {
F.fprintf f "%a|callee" Mangled.pp name
} else {
F.fprintf f "%a$%a|callee" Procname.pp n Mangled.pp name
}
| Abducted_retvar n l =>
if !Config.pp_simple {
F.fprintf f "%a|abductedRetvar" Mangled.pp name
} else {
F.fprintf f "%a$%a%a|abductedRetvar" Procname.pp n Location.pp l Mangled.pp name
}
| Abducted_ref_param n pv l =>
if !Config.pp_simple {
F.fprintf f "%a|%a|abductedRefParam" _pp pv Mangled.pp name
} else {
F.fprintf f "%a$%a%a|abductedRefParam" Procname.pp n Location.pp l Mangled.pp name
}
| Global_var => F.fprintf f "#GB$%a" Mangled.pp name
| Seed_var => F.fprintf f "old_%a" Mangled.pp name
}
};
/** Pretty print a program variable in latex. */
let pp_latex f pv => {
let name = pv.pv_name;
switch pv.pv_kind {
| Local_var _ => Latex.pp_string Latex.Roman f (Mangled.to_string name)
| Callee_var _ =>
F.fprintf
f
"%a_{%a}"
(Latex.pp_string Latex.Roman)
(Mangled.to_string name)
(Latex.pp_string Latex.Roman)
"callee"
| Abducted_retvar _ =>
F.fprintf
f
"%a_{%a}"
(Latex.pp_string Latex.Roman)
(Mangled.to_string name)
(Latex.pp_string Latex.Roman)
"abductedRetvar"
| Abducted_ref_param _ =>
F.fprintf
f
"%a_{%a}"
(Latex.pp_string Latex.Roman)
(Mangled.to_string name)
(Latex.pp_string Latex.Roman)
"abductedRefParam"
| Global_var => Latex.pp_string Latex.Boldface f (Mangled.to_string name)
| Seed_var =>
F.fprintf
f
"%a^{%a}"
(Latex.pp_string Latex.Roman)
(Mangled.to_string name)
(Latex.pp_string Latex.Roman)
"old"
}
};
/** Pretty print a pvar which denotes a value, not an address */
let pp_value pe f pv =>
switch pe.pe_kind {
| PP_TEXT => _pp f pv
| PP_HTML => _pp f pv
| PP_LATEX => pp_latex f pv
};
/** Pretty print a program variable. */
let pp pe f pv => {
let ampersand =
switch pe.pe_kind {
| PP_TEXT => "&"
| PP_HTML => "&"
| PP_LATEX => "\\&"
};
F.fprintf f "%s%a" ampersand (pp_value pe) pv
};
/** Dump a program variable. */
let d (pvar: t) => L.add_print_action (L.PTpvar, Obj.repr pvar);
/** Pretty print a list of program variables. */
let pp_list pe f pvl => F.fprintf f "%a" (pp_seq (fun f e => F.fprintf f "%a" (pp pe) e)) pvl;
/** Dump a list of program variables. */
let d_list pvl =>
IList.iter
(
fun pv => {
d pv;
L.d_str " "
}
)
pvl;
let get_name pv => pv.pv_name;
let to_string pv => Mangled.to_string pv.pv_name;
let get_simplified_name pv => {
let s = Mangled.to_string pv.pv_name;
switch (string_split_character s '.') {
| (Some s1, s2) =>
switch (string_split_character s1 '.') {
| (Some _, s4) => s4 ^ "." ^ s2
| _ => s
}
| _ => s
}
};
/** Check if the pvar is an abucted return var or param passed by ref */
let is_abducted pv =>
switch pv.pv_kind {
| Abducted_retvar _
| Abducted_ref_param _ => true
| _ => false
};
/** Turn a pvar into a seed pvar (which stored the initial value) */
let to_seed pv => {...pv, pv_kind: Seed_var};
/** Check if the pvar is a local var */
let is_local pv =>
switch pv.pv_kind {
| Local_var _ => true
| _ => false
};
/** Check if the pvar is a callee var */
let is_callee pv =>
switch pv.pv_kind {
| Callee_var _ => true
| _ => false
};
/** Check if the pvar is a seed var */
let is_seed pv =>
switch pv.pv_kind {
| Seed_var => true
| _ => false
};
/** Check if the pvar is a global var */
let is_global pv => pv.pv_kind == Global_var;
/** Check if a pvar is the special "this" var */
let is_this pvar => Mangled.equal (get_name pvar) (Mangled.from_string "this");
/** Check if the pvar is a return var */
let is_return pv => get_name pv == Ident.name_return;
/** Turn an ordinary program variable into a callee program variable */
let to_callee pname pvar =>
switch pvar.pv_kind {
| Local_var _ => {...pvar, pv_kind: Callee_var pname}
| Global_var => pvar
| Callee_var _
| Abducted_retvar _
| Abducted_ref_param _
| Seed_var =>
L.d_str "Cannot convert pvar to callee: ";
d pvar;
L.d_ln ();
assert false
};
/** [mk name proc_name] creates a program var with the given function name */
let mk (name: Mangled.t) (proc_name: Procname.t) :t => {
pv_name: name,
pv_kind: Local_var proc_name
};
let get_ret_pvar pname => mk Ident.name_return pname;
/** [mk_callee name proc_name] creates a program var
for a callee function with the given function name */
let mk_callee (name: Mangled.t) (proc_name: Procname.t) :t => {
pv_name: name,
pv_kind: Callee_var proc_name
};
/** create a global variable with the given name */
let mk_global (name: Mangled.t) :t => {pv_name: name, pv_kind: Global_var};
/** create an abducted return variable for a call to [proc_name] at [loc] */
let mk_abducted_ret (proc_name: Procname.t) (loc: Location.t) :t => {
let name = Mangled.from_string ("$RET_" ^ Procname.to_unique_id proc_name);
{pv_name: name, pv_kind: Abducted_retvar proc_name loc}
};
let mk_abducted_ref_param (proc_name: Procname.t) (pv: t) (loc: Location.t) :t => {
let name = Mangled.from_string ("$REF_PARAM_" ^ Procname.to_unique_id proc_name);
{pv_name: name, pv_kind: Abducted_ref_param proc_name pv loc}
};