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.
349 lines
9.2 KiB
349 lines
9.2 KiB
(*
|
|
* 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.
|
|
*)
|
|
|
|
(** Module for Names and Identifiers *)
|
|
|
|
module L = Logging
|
|
module F = Format
|
|
open Utils
|
|
|
|
type name = string
|
|
|
|
type fieldname =
|
|
{ fpos : int;
|
|
fname : Mangled.t }
|
|
|
|
type kind = int
|
|
|
|
let kprimed = - 1
|
|
let knormal = 0
|
|
let kfootprint = 1
|
|
|
|
type t =
|
|
{ kind: int;
|
|
name: name;
|
|
stamp: int }
|
|
|
|
type _ident = t
|
|
|
|
(** {2 Comparison Functions} *)
|
|
|
|
let name_compare = string_compare
|
|
|
|
let fieldname_compare fn1 fn2 =
|
|
let n = int_compare fn1.fpos fn2.fpos in
|
|
if n <> 0 then n else Mangled.compare fn1.fname fn2.fname
|
|
|
|
let name_equal = string_equal
|
|
|
|
let kind_equal k1 k2 = k1 == k2
|
|
|
|
let compare i1 i2 =
|
|
let n = i2.kind - i1.kind
|
|
in if n <> 0 then n
|
|
else
|
|
let n = name_compare i1.name i2.name
|
|
in if n <> 0 then n
|
|
else int_compare i1.stamp i2.stamp
|
|
|
|
let equal i1 i2 =
|
|
i1.stamp == i2.stamp && i1.kind == i2.kind && name_equal i1.name i2.name (* most unlikely first *)
|
|
|
|
let fieldname_equal fn1 fn2 =
|
|
fieldname_compare fn1 fn2 = 0
|
|
|
|
let rec ident_list_compare il1 il2 = match il1, il2 with
|
|
| [],[] -> 0
|
|
| [], _ -> - 1
|
|
| _,[] -> 1
|
|
| i1:: l1, i2:: l2 ->
|
|
let n = compare i1 i2
|
|
in if n <> 0 then n
|
|
else ident_list_compare l1 l2
|
|
|
|
let ident_list_equal ids1 ids2 = (ident_list_compare ids1 ids2 = 0)
|
|
|
|
(** {2 Set for identifiers} *)
|
|
|
|
module IdentSet = Set.Make
|
|
(struct
|
|
type t = _ident
|
|
let compare = compare
|
|
end)
|
|
|
|
module IdentHash =
|
|
Hashtbl.Make(struct
|
|
type t = _ident
|
|
let equal = equal
|
|
let hash (id: t) = Hashtbl.hash id
|
|
end)
|
|
|
|
module FieldSet = Set.Make(struct
|
|
type t = fieldname
|
|
let compare = fieldname_compare
|
|
end)
|
|
|
|
module FieldMap = Map.Make(struct
|
|
type t = fieldname
|
|
let compare = fieldname_compare
|
|
end)
|
|
|
|
let idlist_to_idset ids =
|
|
list_fold_left (fun set id -> IdentSet.add id set) IdentSet.empty ids
|
|
|
|
(** {2 Conversion between Names and Strings} *)
|
|
|
|
module StringHash =
|
|
Hashtbl.Make(struct
|
|
type t = string
|
|
let equal (s1: string) (s2: string) = s1 = s2
|
|
let hash = Hashtbl.hash
|
|
end)
|
|
|
|
module NameHash =
|
|
Hashtbl.Make(struct
|
|
type t = name
|
|
let equal = name_equal
|
|
let hash = Hashtbl.hash
|
|
end)
|
|
|
|
(** Convert a string to a name *)
|
|
let string_to_name (s: string) =
|
|
s
|
|
|
|
(** Create a field name with the given position (field number in the CSU) *)
|
|
let create_fieldname (n: Mangled.t) (position: int) =
|
|
{ fpos = position;
|
|
fname = n }
|
|
|
|
(** Convert a name to a string. *)
|
|
let name_to_string (name: name) =
|
|
name
|
|
|
|
(** Convert a fieldname to a string. *)
|
|
let fieldname_to_string fn = Mangled.to_string fn.fname
|
|
|
|
(** Convert a fieldname to a simplified string with at most one-level path. *)
|
|
let fieldname_to_simplified_string fn =
|
|
let s = Mangled.to_string fn.fname in
|
|
match string_split_character s '.' with
|
|
| Some s1, s2 ->
|
|
(match string_split_character s1 '.' with
|
|
| Some s3, s4 -> s4 ^ "." ^ s2
|
|
| _ -> s)
|
|
| _ -> s
|
|
|
|
(** Convert a fieldname to a flat string without path. *)
|
|
let fieldname_to_flat_string fn =
|
|
let s = Mangled.to_string fn.fname in
|
|
match string_split_character s '.' with
|
|
| Some s1, s2 -> s2
|
|
| _ -> s
|
|
|
|
(** Returns the class part of the fieldname *)
|
|
let java_fieldname_get_class fn =
|
|
let fn = fieldname_to_string fn in
|
|
let ri = String.rindex fn '.' in
|
|
String.sub fn 0 ri
|
|
|
|
(** Returns the last component of the fieldname *)
|
|
let java_fieldname_get_field fn =
|
|
let fn = fieldname_to_string fn in
|
|
let ri = 1 + String.rindex fn '.' in
|
|
String.sub fn ri (String.length fn - ri)
|
|
|
|
(** Check if the field is the synthetic this$n of a nested class, used to access the n-th outher instance. *)
|
|
let java_fieldname_is_outer_instance fn =
|
|
let fn = fieldname_to_string fn in
|
|
let fn_len = String.length fn in
|
|
let this = ".this$" in
|
|
let this_len = String.length this in
|
|
let zero_to_nine s = s >= "0" && s <= "9" in
|
|
fn_len > this_len &&
|
|
String.sub fn (fn_len - this_len - 1) this_len = this &&
|
|
zero_to_nine (String.sub fn (fn_len - 1) 1)
|
|
|
|
let fieldname_offset fn = fn.fpos
|
|
|
|
(** hidded fieldname constant *)
|
|
let fieldname_hidden = create_fieldname (Mangled.from_string ".hidden") 0
|
|
|
|
(** hidded fieldname constant *)
|
|
let fieldname_is_hidden fn =
|
|
fieldname_equal fn fieldname_hidden
|
|
|
|
(** {2 Functions and Hash Tables for Managing Stamps} *)
|
|
|
|
(** Set the stamp of the identifier *)
|
|
let set_stamp i stamp =
|
|
{ i with stamp = stamp }
|
|
|
|
(** Get the stamp of the identifier *)
|
|
let get_stamp i =
|
|
i.stamp
|
|
|
|
(** Map from names to stamps. *)
|
|
let name_map = NameHash.create 1000
|
|
|
|
(** Name used for primed tmp variables *)
|
|
let name_primed = string_to_name "t"
|
|
|
|
(** Name used for normal tmp variables *)
|
|
let name_normal = string_to_name "n"
|
|
|
|
(** Name used for footprint tmp variables *)
|
|
let name_footprint = string_to_name "f"
|
|
|
|
(** Name used for spec variables *)
|
|
let name_spec = string_to_name "val"
|
|
|
|
(** Name used for the return variable *)
|
|
let name_return = Mangled.from_string "return"
|
|
|
|
(** Return the standard name for the given kind *)
|
|
let standard_name kind =
|
|
if kind == knormal then name_normal
|
|
else if kind == kfootprint then name_footprint
|
|
else name_primed
|
|
|
|
(** Every identifier with a given stamp should unltimately be created using this function *)
|
|
let create_with_stamp kind name stamp =
|
|
let update_name_hash () = (* make sure that fresh ids after whis one will be with different stamps *)
|
|
try
|
|
let curr_stamp = NameHash.find name_map name in
|
|
let new_stamp = max curr_stamp stamp in
|
|
NameHash.replace name_map name new_stamp
|
|
with Not_found ->
|
|
NameHash.add name_map name stamp in
|
|
update_name_hash ();
|
|
{ kind = kind; name = name; stamp = stamp }
|
|
|
|
(** Create an identifier with default name for the given kind *)
|
|
let create kind stamp =
|
|
create_with_stamp kind (standard_name kind) stamp
|
|
|
|
(** Generate a normal identifier with the given name and stamp *)
|
|
let create_normal name stamp =
|
|
create_with_stamp knormal name stamp
|
|
|
|
(** Generate a primed identifier with the given name and stamp *)
|
|
let create_primed name stamp =
|
|
create_with_stamp kprimed name stamp
|
|
|
|
(** Generate a footprint identifier with the given name and stamp *)
|
|
let create_footprint name stamp =
|
|
create_with_stamp kfootprint name stamp
|
|
|
|
(** {2 Functions for Identifiers} *)
|
|
|
|
(** Get a name of an identifier *)
|
|
let get_name id =
|
|
id.name
|
|
|
|
let get_kind id =
|
|
id.kind
|
|
|
|
let is_primed (id: t) =
|
|
id.kind == kprimed
|
|
|
|
let is_normal (id: t) =
|
|
id.kind == knormal
|
|
|
|
let is_footprint (id: t) =
|
|
id.kind == kfootprint
|
|
|
|
(* timestamp for a path identifier *)
|
|
let path_ident_stamp = - 3
|
|
|
|
let is_path (id: t) =
|
|
id.kind == knormal && id.stamp = path_ident_stamp
|
|
|
|
let make_ident_primed id =
|
|
if id.kind == kprimed then assert false
|
|
else { id with kind = kprimed }
|
|
|
|
let make_unprimed id =
|
|
if id.kind <> kprimed then assert false
|
|
else { id with kind = knormal }
|
|
|
|
(** Reset the name generator *)
|
|
let reset_name_generator () =
|
|
NameHash.clear name_map
|
|
|
|
(** Update the name generator so that the given id's are not generated again *)
|
|
let update_name_generator ids =
|
|
let upd id = ignore (create_with_stamp id.kind id.name id.stamp) in
|
|
list_iter upd ids
|
|
|
|
(** Create a fresh identifier with the given kind and name. *)
|
|
let create_fresh_ident kind name =
|
|
let stamp =
|
|
try
|
|
let stamp = NameHash.find name_map name in
|
|
NameHash.replace name_map name (stamp + 1);
|
|
stamp + 1
|
|
with Not_found ->
|
|
NameHash.add name_map name 0;
|
|
0 in
|
|
{ kind = kind; name = name; stamp = stamp }
|
|
|
|
(** Create a fresh identifier with default name for the given kind. *)
|
|
let create_fresh kind =
|
|
create_fresh_ident kind (standard_name kind)
|
|
|
|
(** Generate a normal identifier whose name encodes a path given as a string. *)
|
|
let create_path pathstring =
|
|
create_normal (string_to_name ("%path%" ^ pathstring)) path_ident_stamp
|
|
|
|
(** {2 Pretty Printing} *)
|
|
|
|
(** Convert an identifier to a string. *)
|
|
let to_string id =
|
|
let base_name = name_to_string id.name in
|
|
let prefix =
|
|
if id.kind == kfootprint then "@"
|
|
else if id.kind == knormal then ""
|
|
else "_" in
|
|
let suffix = "$" ^ (string_of_int id.stamp)
|
|
in prefix ^ base_name ^ suffix
|
|
|
|
(** Pretty print a name. *)
|
|
let pp_name f name =
|
|
F.fprintf f "%s" (name_to_string name)
|
|
|
|
let pp_fieldname f fn =
|
|
(* only use for debug F.fprintf f "%a#%d" pp_name fn.fname fn.fpos *)
|
|
Mangled.pp f fn.fname
|
|
|
|
(** Pretty print a name in latex. *)
|
|
let pp_name_latex style f (name: name) =
|
|
Latex.pp_string style f (name_to_string name)
|
|
|
|
let pp_fieldname_latex style f fn =
|
|
Latex.pp_string style f (Mangled.to_string fn.fname)
|
|
|
|
(** Pretty print an identifier. *)
|
|
let pp pe f id = match pe.pe_kind with
|
|
| PP_TEXT | PP_HTML ->
|
|
F.fprintf f "%s" (to_string id)
|
|
| PP_LATEX ->
|
|
let base_name = name_to_string id.name in
|
|
let style =
|
|
if id.kind = kfootprint then Latex.Boldface
|
|
else if id.kind = knormal then Latex.Roman
|
|
else Latex.Roman in
|
|
F.fprintf f "%a_{%s}" (Latex.pp_string style) base_name (string_of_int id.stamp)
|
|
|
|
(** pretty printer for lists of identifiers *)
|
|
let pp_list pe = pp_comma_seq (pp pe)
|
|
|
|
(** pretty printer for lists of names *)
|
|
let pp_name_list = pp_comma_seq pp_name
|