[clang] Make backend use QualifiedCppName.t instead of string

Summary:
Don't pass names as strings in clang frontend. Instead use QualifiedCppName which preserves
each identifier of qualified name.

Done by
1. change return type of `Cast_utils.get_qualified_name` to return `QualifiedCppName.t`
2. change types in `Typ.Name.t` and `Typ.Procname.t` to use qualified names where applicable
3. Keep changing the code until it compiles

Reviewed By: jberdine

Differential Revision: D4754242

fbshipit-source-id: 9d723cb
master
Andrzej Kotulski 8 years ago committed by Facebook Github Bot
parent 11295967a3
commit 2cc972705a

@ -32,6 +32,8 @@ let of_qual_string = {
let to_qual_string = String.concat sep::"::"; let to_qual_string = String.concat sep::"::";
let pp fmt quals => Format.fprintf fmt "%s" (to_qual_string quals);
let module Match = { let module Match = {
type quals_matcher = Str.regexp; type quals_matcher = Str.regexp;
let regexp_string_of_qualifiers quals => Str.quote (String.concat sep::"::" quals) ^ "$"; let regexp_string_of_qualifiers quals => Str.quote (String.concat sep::"::" quals) ^ "$";

@ -16,6 +16,7 @@ let empty: t;
let equal: t => t => bool; let equal: t => t => bool;
/** attempts to parse the argument into a list::of::possibly::templated<T>::qualifiers */ /** attempts to parse the argument into a list::of::possibly::templated<T>::qualifiers */
let of_qual_string: string => t; let of_qual_string: string => t;
@ -27,6 +28,7 @@ let to_qual_string: t => string;
/** append qualifier to the end (innermost scope) of the qualified name */ /** append qualifier to the end (innermost scope) of the qualified name */
let append_qualifier: t => qual::string => t; let append_qualifier: t => qual::string => t;
/** returns list of qualifers */ /** returns list of qualifers */
let to_list: t => list string; let to_list: t => list string;
@ -34,6 +36,8 @@ let to_list: t => list string;
/** given list of qualifiers in normal order produce qualified name ["std", "move"] */ /** given list of qualifiers in normal order produce qualified name ["std", "move"] */
let of_list: list string => t; let of_list: list string => t;
let pp: Format.formatter => t => unit;
/* Module to match qualified C++ procnames "fuzzily", that is up to namescapes and templating. In /* Module to match qualified C++ procnames "fuzzily", that is up to namescapes and templating. In
particular, this deals with the following issues: particular, this deals with the following issues:

@ -135,16 +135,16 @@ let module T = {
| Tarray t static_length /** array type with statically fixed length */ | Tarray t static_length /** array type with statically fixed length */
[@@deriving compare] [@@deriving compare]
and name = and name =
| CStruct Mangled.t | CStruct QualifiedCppName.t
| CUnion Mangled.t | CUnion QualifiedCppName.t
| CppClass Mangled.t template_spec_info | CppClass QualifiedCppName.t template_spec_info
| JavaClass Mangled.t | JavaClass Mangled.t
| ObjcClass Mangled.t | ObjcClass QualifiedCppName.t
| ObjcProtocol Mangled.t | ObjcProtocol QualifiedCppName.t
[@@deriving compare] [@@deriving compare]
and template_spec_info = and template_spec_info =
| NoTemplate | NoTemplate
| Template (string, list (option t)) | Template (QualifiedCppName.t, list (option t))
[@@deriving compare]; [@@deriving compare];
let equal = [%compare.equal : t]; let equal = [%compare.equal : t];
let hash = Hashtbl.hash; let hash = Hashtbl.hash;
@ -160,9 +160,17 @@ let module Name = {
| CStruct name | CStruct name
| CUnion name | CUnion name
| CppClass name _ | CppClass name _
| JavaClass name
| ObjcClass name | ObjcClass name
| ObjcProtocol name => Mangled.to_string name; | ObjcProtocol name => QualifiedCppName.to_qual_string name
| JavaClass name => Mangled.to_string name;
let qual_name =
fun
| CStruct name
| CUnion name
| CppClass name _
| ObjcClass name
| ObjcProtocol name => name
| JavaClass _ => QualifiedCppName.empty;
let to_string tname => { let to_string tname => {
let prefix = let prefix =
fun fun
@ -192,8 +200,9 @@ let module Name = {
| _ => false | _ => false
}; };
let module C = { let module C = {
let from_string name_str => CStruct (Mangled.from_string name_str); let from_qual_name qual_name => CStruct qual_name;
let union_from_string name_str => CUnion (Mangled.from_string name_str); let from_string name_str => QualifiedCppName.of_qual_string name_str |> from_qual_name;
let union_from_qual_name qual_name => CUnion qual_name;
}; };
let module Java = { let module Java = {
let from_string name_str => JavaClass (Mangled.from_string name_str); let from_string name_str => JavaClass (Mangled.from_string name_str);
@ -212,17 +221,16 @@ let module Name = {
let java_lang_cloneable = from_string "java.lang.Cloneable"; let java_lang_cloneable = from_string "java.lang.Cloneable";
}; };
let module Cpp = { let module Cpp = {
let from_string name_str => CppClass (Mangled.from_string name_str) NoTemplate; let from_qual_name template_spec_info qual_name => CppClass qual_name template_spec_info;
let from_template_string template_spec_info name =>
CppClass (Mangled.from_string name) template_spec_info;
let is_class = let is_class =
fun fun
| CppClass _ => true | CppClass _ => true
| _ => false; | _ => false;
}; };
let module Objc = { let module Objc = {
let from_string name_str => ObjcClass (Mangled.from_string name_str); let from_qual_name qual_name => ObjcClass qual_name;
let protocol_from_string name_str => ObjcProtocol (Mangled.from_string name_str); let from_string name_str => QualifiedCppName.of_qual_string name_str |> from_qual_name;
let protocol_from_qual_name qual_name => ObjcProtocol qual_name;
let is_class = let is_class =
fun fun
| ObjcClass _ => true | ObjcClass _ => true
@ -407,7 +415,7 @@ let module Procname = {
[@@deriving compare]; [@@deriving compare];
/** Type of c procedure names. */ /** Type of c procedure names. */
type c = {name: string, mangled: option string, template_args: template_spec_info} type c = {name: QualifiedCppName.t, mangled: option string, template_args: template_spec_info}
[@@deriving compare]; [@@deriving compare];
type objc_cpp_method_kind = type objc_cpp_method_kind =
| CPPMethod (option string) /** with mangling */ | CPPMethod (option string) /** with mangling */
@ -492,12 +500,13 @@ let module Procname = {
| None => (None, package_classname) | None => (None, package_classname)
}; };
let split_typename typename => split_classname (Name.name typename); let split_typename typename => split_classname (Name.name typename);
let c (name: string) (mangled: string) (template_args: template_spec_info) => { let c (name: QualifiedCppName.t) (mangled: string) (template_args: template_spec_info) => {
name, name,
mangled: Some mangled, mangled: Some mangled,
template_args template_args
}; };
let from_string_c_fun (name: string) => C {name, mangled: None, template_args: NoTemplate}; let from_string_c_fun (name: string) =>
C {name: QualifiedCppName.of_qual_string name, mangled: None, template_args: NoTemplate};
let java class_name return_type method_name parameters kind => { let java class_name return_type method_name parameters kind => {
class_name, class_name,
return_type, return_type,
@ -576,7 +585,7 @@ let module Procname = {
let get_method = let get_method =
fun fun
| ObjC_Cpp name => name.method_name | ObjC_Cpp name => name.method_name
| C {name} => name | C {name} => QualifiedCppName.to_qual_string name
| Block name => name | Block name => name
| Java j => j.method_name | Java j => j.method_name
| Linters_dummy_method => "Linters_dummy_method"; | Linters_dummy_method => "Linters_dummy_method";
@ -783,15 +792,19 @@ let module Procname = {
}; };
let get_global_name_of_initializer = let get_global_name_of_initializer =
fun fun
| C {name} when String.is_prefix prefix::Config.clang_initializer_prefix name => { | C {name}
when
String.is_prefix
prefix::Config.clang_initializer_prefix (QualifiedCppName.to_qual_string name) => {
let name_str = QualifiedCppName.to_qual_string name;
let prefix_len = String.length Config.clang_initializer_prefix; let prefix_len = String.length Config.clang_initializer_prefix;
Some (String.sub name pos::prefix_len len::(String.length name - prefix_len)) Some (String.sub name_str pos::prefix_len len::(String.length name_str - prefix_len))
} }
| _ => None; | _ => None;
/** to_string for C_function type */ /** to_string for C_function type */
let to_readable_string (c1, c2) verbose => { let to_readable_string (c1, c2) verbose => {
let plain = c1; let plain = QualifiedCppName.to_qual_string c1;
if verbose { if verbose {
switch c2 { switch c2 {
| None => plain | None => plain
@ -884,12 +897,10 @@ let module Procname = {
/** Pretty print a set of proc names */ /** Pretty print a set of proc names */
let pp_set fmt set => Set.iter (fun pname => F.fprintf fmt "%a " pp pname) set; let pp_set fmt set => Set.iter (fun pname => F.fprintf fmt "%a " pp pname) set;
let objc_cpp_get_class_qualifiers objc_cpp => QualifiedCppName.of_qual_string ( let objc_cpp_get_class_qualifiers objc_cpp => Name.qual_name objc_cpp.class_name;
Name.name objc_cpp.class_name
);
let get_qualifiers pname => let get_qualifiers pname =>
switch pname { switch pname {
| C {name} => QualifiedCppName.of_qual_string name | C {name} => name
| ObjC_Cpp objc_cpp => | ObjC_Cpp objc_cpp =>
objc_cpp_get_class_qualifiers objc_cpp |> objc_cpp_get_class_qualifiers objc_cpp |>
QualifiedCppName.append_qualifier qual::objc_cpp.method_name QualifiedCppName.append_qualifier qual::objc_cpp.method_name

@ -81,16 +81,16 @@ type t =
| Tarray t static_length /** array type with statically fixed length */ | Tarray t static_length /** array type with statically fixed length */
[@@deriving compare] [@@deriving compare]
and name = and name =
| CStruct Mangled.t | CStruct QualifiedCppName.t
| CUnion Mangled.t | CUnion QualifiedCppName.t
| CppClass Mangled.t template_spec_info | CppClass QualifiedCppName.t template_spec_info
| JavaClass Mangled.t | JavaClass Mangled.t
| ObjcClass Mangled.t | ObjcClass QualifiedCppName.t
| ObjcProtocol Mangled.t | ObjcProtocol QualifiedCppName.t
[@@deriving compare] [@@deriving compare]
and template_spec_info = and template_spec_info =
| NoTemplate | NoTemplate
| Template (string, list (option t)) | Template (QualifiedCppName.t, list (option t))
[@@deriving compare]; [@@deriving compare];
let module Name: { let module Name: {
@ -113,7 +113,14 @@ let module Name: {
/** name of the typename without qualifier */ /** name of the typename without qualifier */
let name: t => string; let name: t => string;
let module C: {let from_string: string => t; let union_from_string: string => t;};
/** qualified name of the type, may return nonsense for Java classes */
let qual_name: t => QualifiedCppName.t;
let module C: {
let from_string: string => t;
let from_qual_name: QualifiedCppName.t => t;
let union_from_qual_name: QualifiedCppName.t => t;
};
let module Java: { let module Java: {
/** Create a typename from a Java classname in the form "package.class" */ /** Create a typename from a Java classname in the form "package.class" */
@ -131,8 +138,7 @@ let module Name: {
let module Cpp: { let module Cpp: {
/** Create a typename from a C++ classname */ /** Create a typename from a C++ classname */
let from_string: string => t; let from_qual_name: template_spec_info => QualifiedCppName.t => t;
let from_template_string: template_spec_info => string => t;
/** [is_class name] holds if [name] names a C++ class */ /** [is_class name] holds if [name] names a C++ class */
let is_class: t => bool; let is_class: t => bool;
@ -141,7 +147,8 @@ let module Name: {
/** Create a typename from a Objc classname */ /** Create a typename from a Objc classname */
let from_string: string => t; let from_string: string => t;
let protocol_from_string: string => t; let from_qual_name: QualifiedCppName.t => t;
let protocol_from_qual_name: QualifiedCppName.t => t;
/** [is_class name] holds if [name] names a Objc class */ /** [is_class name] holds if [name] names a Objc class */
let is_class: t => bool; let is_class: t => bool;
@ -266,7 +273,7 @@ let module Procname: {
let module Set: Caml.Set.S with type elt = t; let module Set: Caml.Set.S with type elt = t;
/** Create a C procedure name from plain and mangled name. */ /** Create a C procedure name from plain and mangled name. */
let c: string => string => template_spec_info => c; let c: QualifiedCppName.t => string => template_spec_info => c;
/** Empty block name. */ /** Empty block name. */
let empty_block: t; let empty_block: t;

@ -32,7 +32,7 @@ let rec get_mangled_method_name function_decl_info method_decl_info =
let get_template_info tenv (fdi : Clang_ast_t.function_decl_info) : Typ.template_spec_info = let get_template_info tenv (fdi : Clang_ast_t.function_decl_info) : Typ.template_spec_info =
match fdi.fdi_template_specialization with match fdi.fdi_template_specialization with
| Some spec_info -> Typ.Template ( | Some spec_info -> Typ.Template (
"", QualifiedCppName.empty,
List.map spec_info.tsi_specialization_args ~f:(function List.map spec_info.tsi_specialization_args ~f:(function
| `Type type_ptr -> Some (CType_decl.type_ptr_to_sil_type tenv type_ptr) | `Type type_ptr -> Some (CType_decl.type_ptr_to_sil_type tenv type_ptr)
| _ -> None)) | _ -> None))
@ -60,7 +60,7 @@ let mk_c_function translation_unit_context ?tenv name function_decl_info_opt =
| _ -> Typ.NoTemplate in | _ -> Typ.NoTemplate in
let mangled = file ^ mangled_name in let mangled = file ^ mangled_name in
if String.is_empty mangled then if String.is_empty mangled then
Typ.Procname.from_string_c_fun name Typ.Procname.from_string_c_fun (QualifiedCppName.to_qual_string name)
else else
Typ.Procname.C (Typ.Procname.c name mangled template_info) Typ.Procname.C (Typ.Procname.c name mangled template_info)
@ -121,7 +121,8 @@ let get_class_typename ?tenv method_decl_info =
module NoAstDecl = struct module NoAstDecl = struct
let c_function_of_string translation_unit_context tenv name = let c_function_of_string translation_unit_context tenv name =
mk_c_function translation_unit_context ~tenv name None let qual_name = QualifiedCppName.of_qual_string name in
mk_c_function translation_unit_context ~tenv qual_name None
let cpp_method_of_string tenv class_name method_name = let cpp_method_of_string tenv class_name method_name =
mk_cpp_method ~tenv class_name method_name None mk_cpp_method ~tenv class_name method_name None

@ -55,10 +55,10 @@ let create_c_record_typename opt_type =
| `Type s -> | `Type s ->
(let buf = Str.split (Str.regexp "[ \t]+") s in (let buf = Str.split (Str.regexp "[ \t]+") s in
match buf with match buf with
| "struct":: _ -> Typ.Name.C.from_string | "struct":: _ -> Typ.Name.C.from_qual_name
| "class":: _ -> Typ.Name.Cpp.from_string | "class":: _ -> Typ.Name.Cpp.from_qual_name Typ.NoTemplate
| "union":: _ -> Typ.Name.C.union_from_string | "union":: _ -> Typ.Name.C.union_from_qual_name
| _ -> Typ.Name.C.from_string) | _ -> Typ.Name.C.from_qual_name)
| _ -> assert false | _ -> assert false
let get_class_template_name = function let get_class_template_name = function
@ -83,7 +83,7 @@ let translate_as_type_ptr_matcher =
let get_translate_as_friend_decl decl_list = let get_translate_as_friend_decl decl_list =
let is_translate_as_friend_name (_, name_info) = let is_translate_as_friend_name (_, name_info) =
let qual_name = QualifiedCppName.of_qual_string (CAst_utils.get_qualified_name name_info) in let qual_name = CAst_utils.get_qualified_name name_info in
QualifiedCppName.Match.match_qualifiers translate_as_type_ptr_matcher qual_name in QualifiedCppName.Match.match_qualifiers translate_as_type_ptr_matcher qual_name in
let get_friend_decl_opt (decl : Clang_ast_t.decl) = match decl with let get_friend_decl_opt (decl : Clang_ast_t.decl) = match decl with
| FriendDecl (_, `Type type_ptr) -> CAst_utils.get_decl_from_typ_ptr type_ptr | FriendDecl (_, `Type type_ptr) -> CAst_utils.get_decl_from_typ_ptr type_ptr
@ -169,17 +169,17 @@ and get_record_typename ?tenv decl =
| ClassTemplateSpecializationDecl (_, name_info, _, _, _, _, _, _, _) -> | ClassTemplateSpecializationDecl (_, name_info, _, _, _, _, _, _, _) ->
(* we use Typ.CppClass for C++ because we expect Typ.CppClass from *) (* we use Typ.CppClass for C++ because we expect Typ.CppClass from *)
(* types that have methods. And in C++ struct/class/union can have methods *) (* types that have methods. And in C++ struct/class/union can have methods *)
let name_str = CAst_utils.get_qualified_name name_info in let qual_name = CAst_utils.get_qualified_name name_info in
let templ_info = match tenv with let templ_info = match tenv with
| Some t -> get_template_specialization t decl | Some t -> get_template_specialization t decl
| None -> Typ.NoTemplate in | None -> Typ.NoTemplate in
Typ.Name.Cpp.from_template_string templ_info name_str Typ.Name.Cpp.from_qual_name templ_info qual_name
| ObjCInterfaceDecl (_, name_info, _, _, _) | ObjCInterfaceDecl (_, name_info, _, _, _)
| ObjCImplementationDecl (_, name_info, _, _, _) | ObjCImplementationDecl (_, name_info, _, _, _)
| ObjCProtocolDecl (_, name_info, _, _, _) | ObjCProtocolDecl (_, name_info, _, _, _)
| ObjCCategoryDecl (_, name_info, _, _, _) | ObjCCategoryDecl (_, name_info, _, _, _)
| ObjCCategoryImplDecl (_, name_info, _, _, _) -> | ObjCCategoryImplDecl (_, name_info, _, _, _) ->
CAst_utils.get_qualified_name name_info |> Typ.Name.Objc.from_string CAst_utils.get_qualified_name name_info |> Typ.Name.Objc.from_qual_name
| _ -> assert false | _ -> assert false
(** fetches list of superclasses for C++ classes *) (** fetches list of superclasses for C++ classes *)

@ -17,26 +17,22 @@ module F = Format
type type_ptr_to_sil_type = Tenv.t -> Clang_ast_t.type_ptr -> Typ.t type type_ptr_to_sil_type = Tenv.t -> Clang_ast_t.type_ptr -> Typ.t
let fold_qual_name qual_name_list = let sanitize_name = Str.global_replace (Str.regexp "[/ ]") "_"
match qual_name_list with let get_qual_name qual_name_list =
| [] -> "" List.rev_map ~f:sanitize_name qual_name_list |> QualifiedCppName.of_list
| name :: quals ->
let s = (List.fold_right ~f:(fun el res -> res ^ el ^ "::") quals ~init:"") ^ name in
let no_slash_space = Str.global_replace (Str.regexp "[/ ]") "_" s in
no_slash_space
let get_qualified_name name_info = let get_qualified_name name_info =
fold_qual_name name_info.Clang_ast_t.ni_qual_name get_qual_name name_info.Clang_ast_t.ni_qual_name
let get_unqualified_name name_info = let get_unqualified_name name_info =
let name = match name_info.Clang_ast_t.ni_qual_name with let name = match name_info.Clang_ast_t.ni_qual_name with
| name :: _ -> name | name :: _ -> name
| [] -> name_info.Clang_ast_t.ni_name in | [] -> name_info.Clang_ast_t.ni_name in
fold_qual_name [name] sanitize_name name
let get_class_name_from_member member_name_info = let get_class_name_from_member member_name_info =
match member_name_info.Clang_ast_t.ni_qual_name with match member_name_info.Clang_ast_t.ni_qual_name with
| _ :: class_qual_list -> fold_qual_name class_qual_list | _ :: class_qual_list -> get_qual_name class_qual_list
| [] -> assert false | [] -> assert false
let make_name_decl name = { let make_name_decl name = {
@ -178,7 +174,7 @@ let name_of_typedef_type_info {Clang_ast_t.tti_decl_ptr} =
match get_decl tti_decl_ptr with match get_decl tti_decl_ptr with
| Some TypedefDecl (_, name_decl_info, _, _, _) -> | Some TypedefDecl (_, name_decl_info, _, _, _) ->
get_qualified_name name_decl_info get_qualified_name name_decl_info
| _ -> "" | _ -> QualifiedCppName.empty
let name_opt_of_typedef_type_ptr type_ptr = let name_opt_of_typedef_type_ptr type_ptr =
match get_type type_ptr with match get_type type_ptr with
@ -255,8 +251,8 @@ let full_name_of_decl_opt decl_opt =
| Some decl -> | Some decl ->
(match Clang_ast_proj.get_named_decl_tuple decl with (match Clang_ast_proj.get_named_decl_tuple decl with
| Some (_, name_info) -> get_qualified_name name_info | Some (_, name_info) -> get_qualified_name name_info
| None -> "") | None -> QualifiedCppName.empty)
| None -> "" | None -> QualifiedCppName.empty
(* Generates a unique number for each variant of a type. *) (* Generates a unique number for each variant of a type. *)
let get_tag ast_item = let get_tag ast_item =
@ -280,7 +276,7 @@ let generate_key_decl decl =
let buffer = Buffer.create 16 in let buffer = Buffer.create 16 in
let name = full_name_of_decl_opt (Some decl) in let name = full_name_of_decl_opt (Some decl) in
Buffer.add_string buffer (string_of_int (get_tag decl)); Buffer.add_string buffer (string_of_int (get_tag decl));
Buffer.add_string buffer name; Buffer.add_string buffer (QualifiedCppName.to_qual_string name);
Buffer.contents buffer Buffer.contents buffer
let rec get_super_if decl = let rec get_super_if decl =
@ -386,7 +382,7 @@ let if_decl_to_di_pointer_opt if_decl =
let is_instance_type type_ptr = let is_instance_type type_ptr =
match name_opt_of_typedef_type_ptr type_ptr with match name_opt_of_typedef_type_ptr type_ptr with
| Some name -> String.equal name "instancetype" | Some name -> String.equal (QualifiedCppName.to_qual_string name) "instancetype"
| None -> false | None -> false
let return_type_matches_class_type rtp type_decl_pointer = let return_type_matches_class_type rtp type_decl_pointer =

@ -41,13 +41,13 @@ val add_enum_constant : Clang_ast_t.pointer -> Clang_ast_t.pointer option -> uni
val get_enum_constant_exp : Clang_ast_t.pointer -> Clang_ast_t.pointer option * Exp.t option val get_enum_constant_exp : Clang_ast_t.pointer -> Clang_ast_t.pointer option * Exp.t option
(** returns sanitized, fully qualified name given name info *) (** returns sanitized, fully qualified name given name info *)
val get_qualified_name : Clang_ast_t.named_decl_info -> string val get_qualified_name : Clang_ast_t.named_decl_info -> QualifiedCppName.t
(** returns sanitized unqualified name given name info *) (** returns sanitized unqualified name given name info *)
val get_unqualified_name : Clang_ast_t.named_decl_info -> string val get_unqualified_name : Clang_ast_t.named_decl_info -> string
(** returns qualified class name given member name info *) (** returns qualified class name given member name info *)
val get_class_name_from_member : Clang_ast_t.named_decl_info -> string val get_class_name_from_member : Clang_ast_t.named_decl_info -> QualifiedCppName.t
(** looks up clang pointer to type and returns c_type. It requires type_ptr to be `TPtr. *) (** looks up clang pointer to type and returns c_type. It requires type_ptr to be `TPtr. *)
val get_type : Clang_ast_t.type_ptr -> Clang_ast_t.c_type option val get_type : Clang_ast_t.type_ptr -> Clang_ast_t.c_type option
@ -64,10 +64,10 @@ val get_decl_from_typ_ptr : Clang_ast_t.type_ptr -> Clang_ast_t.decl option
NOTE: this doesn't expand type, it only converts type_ptr to string *) NOTE: this doesn't expand type, it only converts type_ptr to string *)
val string_of_type_ptr : Clang_ast_t.type_ptr -> string val string_of_type_ptr : Clang_ast_t.type_ptr -> string
val name_of_typedef_type_info : Clang_ast_t.typedef_type_info -> string val name_of_typedef_type_info : Clang_ast_t.typedef_type_info -> QualifiedCppName.t
(** returns name of typedef if type_ptr points to Typedef, None otherwise *) (** returns name of typedef if type_ptr points to Typedef, None otherwise *)
val name_opt_of_typedef_type_ptr : Clang_ast_t.type_ptr -> string option val name_opt_of_typedef_type_ptr : Clang_ast_t.type_ptr -> QualifiedCppName.t option
val string_of_qual_type : Clang_ast_t.qual_type -> string val string_of_qual_type : Clang_ast_t.qual_type -> string
@ -98,7 +98,7 @@ val is_const_expr_var : Clang_ast_t.decl -> bool
val is_ptr_to_objc_class : Clang_ast_t.c_type option -> string -> bool val is_ptr_to_objc_class : Clang_ast_t.c_type option -> string -> bool
val full_name_of_decl_opt : Clang_ast_t.decl option -> string val full_name_of_decl_opt : Clang_ast_t.decl option -> QualifiedCppName.t
(** Generates a key for a statement based on its sub-statements and the statement tag. *) (** Generates a key for a statement based on its sub-statements and the statement tag. *)
val generate_key_stmt : Clang_ast_t.stmt -> string val generate_key_stmt : Clang_ast_t.stmt -> string

@ -94,15 +94,6 @@ let get_curr_class_ptr curr_class =
get_ptr_from_decl_ref ocidi.ocidi_class_interface get_ptr_from_decl_ref ocidi.ocidi_class_interface
| _ -> decl_ptr | _ -> decl_ptr
let get_curr_class_name curr_class =
let class_decl_ptr = get_curr_class_ptr curr_class in
let _, name_info = match Option.bind
(CAst_utils.get_decl class_decl_ptr)
Clang_ast_proj.get_named_decl_tuple with
| Some result -> result
| None -> assert false in
CAst_utils.get_qualified_name name_info
let get_curr_class_typename context = let get_curr_class_typename context =
let tenv = context.tenv in let tenv = context.tenv in
let curr_class = get_curr_class context in let curr_class = get_curr_class context in

@ -45,8 +45,6 @@ val get_cg : t -> Cg.t
val get_curr_class : t -> curr_class val get_curr_class : t -> curr_class
val get_curr_class_name : curr_class -> string
val get_curr_class_typename : t -> Typ.Name.t val get_curr_class_typename : t -> Typ.Name.t
val get_curr_class_decl_ptr : curr_class -> Clang_ast_t.pointer val get_curr_class_decl_ptr : curr_class -> Clang_ast_t.pointer

@ -29,7 +29,7 @@ let fields_superclass tenv interface_decl_info =
| Some dr -> | Some dr ->
(match dr.Clang_ast_t.dr_name with (match dr.Clang_ast_t.dr_name with
| Some sc -> | Some sc ->
let classname = Typ.Name.Objc.from_string (CAst_utils.get_qualified_name sc) in let classname = Typ.Name.Objc.from_qual_name (CAst_utils.get_qualified_name sc) in
get_fields_super_classes tenv classname get_fields_super_classes tenv classname
| _ -> []) | _ -> [])
| _ -> [] | _ -> []
@ -78,12 +78,12 @@ let rec get_fields type_ptr_to_sil_type tenv decl_list =
(* Add potential extra fields defined only in the implementation of the class *) (* Add potential extra fields defined only in the implementation of the class *)
(* to the info given in the interface. Update the tenv accordingly. *) (* to the info given in the interface. Update the tenv accordingly. *)
let add_missing_fields tenv class_name missing_fields = let add_missing_fields tenv class_name missing_fields =
let class_tn_name = Typ.Name.Objc.from_string class_name in let class_tn_name = Typ.Name.Objc.from_qual_name class_name in
match Tenv.lookup tenv class_tn_name with match Tenv.lookup tenv class_tn_name with
| Some ({ fields } as struct_typ) -> | Some ({ fields } as struct_typ) ->
let new_fields = CGeneral_utils.append_no_duplicates_fields fields missing_fields in let new_fields = CGeneral_utils.append_no_duplicates_fields fields missing_fields in
ignore (Tenv.mk_struct tenv ~default:struct_typ ~fields:new_fields ~statics:[] class_tn_name); ignore (Tenv.mk_struct tenv ~default:struct_typ ~fields:new_fields ~statics:[] class_tn_name);
Logging.out_debug " Updating info for class '%s' in tenv\n" class_name Logging.out_debug " Updating info for class '%a' in tenv\n" QualifiedCppName.pp class_name
| _ -> () | _ -> ()
let modelled_fields_in_classes = let modelled_fields_in_classes =

@ -21,6 +21,6 @@ val fields_superclass : Tenv.t -> Clang_ast_t.obj_c_interface_decl_info -> field
val build_sil_field : CAst_utils.type_ptr_to_sil_type -> Tenv.t -> Clang_ast_t.named_decl_info -> val build_sil_field : CAst_utils.type_ptr_to_sil_type -> Tenv.t -> Clang_ast_t.named_decl_info ->
Clang_ast_t.type_ptr -> Clang_ast_t.property_attribute list -> field_type Clang_ast_t.type_ptr -> Clang_ast_t.property_attribute list -> field_type
val add_missing_fields : Tenv.t -> string -> field_type list -> unit val add_missing_fields : Tenv.t -> QualifiedCppName.t -> field_type list -> unit
val modelled_field : Clang_ast_t.named_decl_info -> field_type list val modelled_field : Clang_ast_t.named_decl_info -> field_type list

@ -201,7 +201,8 @@ let log_frontend_issue translation_unit_context method_decl_opt key issue_desc l
let exn = Exceptions.Frontend_warning (name, err_desc, __POS__) in let exn = Exceptions.Frontend_warning (name, err_desc, __POS__) in
let trace = [ Errlog.make_trace_element 0 issue_desc.CIssue.loc "" [] ] in let trace = [ Errlog.make_trace_element 0 issue_desc.CIssue.loc "" [] ] in
let err_kind = issue_desc.CIssue.severity in let err_kind = issue_desc.CIssue.severity in
let method_name = CAst_utils.full_name_of_decl_opt method_decl_opt in let method_name = CAst_utils.full_name_of_decl_opt method_decl_opt
|> QualifiedCppName.to_qual_string in
let key = Hashtbl.hash (key ^ method_name) in let key = Hashtbl.hash (key ^ method_name) in
Reporting.log_issue_from_errlog err_kind errlog exn ~loc ~ltr:trace Reporting.log_issue_from_errlog err_kind errlog exn ~loc ~ltr:trace
~node_id:(0, key) ?linters_def_file ~node_id:(0, key) ?linters_def_file

@ -102,8 +102,7 @@ let replicate n el = List.map ~f:(fun _ -> el) (list_range 0 (n -1))
let mk_class_field_name field_qual_name = let mk_class_field_name field_qual_name =
let field_name = field_qual_name.Clang_ast_t.ni_name in let field_name = field_qual_name.Clang_ast_t.ni_name in
let class_name = CAst_utils.get_class_name_from_member field_qual_name in let class_name = CAst_utils.get_class_name_from_member field_qual_name in
let qual_class_name = QualifiedCppName.of_qual_string class_name in Fieldname.Clang.from_qualified class_name field_name
Fieldname.Clang.from_qualified qual_class_name field_name
let is_cpp_translation translation_unit_context = let is_cpp_translation translation_unit_context =
let lang = translation_unit_context.CFrontend_config.lang in let lang = translation_unit_context.CFrontend_config.lang in
@ -116,7 +115,7 @@ let is_objc_extension translation_unit_context =
CFrontend_config.equal_clang_lang lang CFrontend_config.ObjCPP CFrontend_config.equal_clang_lang lang CFrontend_config.ObjCPP
let get_var_name_mangled name_info var_decl_info = let get_var_name_mangled name_info var_decl_info =
let clang_name = CAst_utils.get_qualified_name name_info in let clang_name = CAst_utils.get_qualified_name name_info |> QualifiedCppName.to_qual_string in
let param_idx_opt = var_decl_info.Clang_ast_t.vdi_parm_index_in_function in let param_idx_opt = var_decl_info.Clang_ast_t.vdi_parm_index_in_function in
let name_string = let name_string =
match clang_name, param_idx_opt with match clang_name, param_idx_opt with
@ -175,5 +174,6 @@ let mk_sil_var trans_unit_ctx named_decl_info decl_info_type_ptr_opt procname ou
let mangled_name = Mangled.mangled name_string mangled in let mangled_name = Mangled.mangled name_string mangled in
Pvar.mk mangled_name procname Pvar.mk mangled_name procname
| None -> | None ->
let name_string = CAst_utils.get_qualified_name named_decl_info in let name_string = CAst_utils.get_qualified_name named_decl_info
|> QualifiedCppName.to_qual_string in
Pvar.mk (Mangled.from_string name_string) procname Pvar.mk (Mangled.from_string name_string) procname

@ -236,7 +236,7 @@ let get_superclass_curr_class_objc context =
super_of_decl_ref_opt ocidi.ocidi_class_interface super_of_decl_ref_opt ocidi.ocidi_class_interface
| _ -> assert false in | _ -> assert false in
match CContext.get_curr_class context with match CContext.get_curr_class context with
| CContext.ContextClsDeclPtr ptr -> Typ.Name.Objc.from_string (retreive_super_name ptr) | CContext.ContextClsDeclPtr ptr -> Typ.Name.Objc.from_qual_name (retreive_super_name ptr)
| CContext.ContextNoCls -> assert false | CContext.ContextNoCls -> assert false
(* Gets the class name from a method signature found by clang, if search is successful *) (* Gets the class name from a method signature found by clang, if search is successful *)

@ -198,7 +198,9 @@ let is_property_pointer_type an =
| Some ObjCObjectPointerType _ | Some ObjCObjectPointerType _
| Some BlockPointerType _ -> true | Some BlockPointerType _ -> true
| Some TypedefType (_, tti) -> | Some TypedefType (_, tti) ->
String.equal (CAst_utils.name_of_typedef_type_info tti) CFrontend_config.id_cl let typedef_str = CAst_utils.name_of_typedef_type_info tti
|> QualifiedCppName.to_qual_string in
String.equal typedef_str CFrontend_config.id_cl
| exception Not_found -> false | exception Not_found -> false
| _ -> false) | _ -> false)
| _ -> false | _ -> false

@ -437,7 +437,7 @@ struct
let root_node' = GotoLabel.find_goto_label trans_state.context label_name sil_loc in let root_node' = GotoLabel.find_goto_label trans_state.context label_name sil_loc in
{ empty_res_trans with root_nodes = [root_node']; leaf_nodes = trans_state.succ_nodes } { empty_res_trans with root_nodes = [root_node']; leaf_nodes = trans_state.succ_nodes }
let get_builtin_pname_opt trans_unit_ctx name decl_opt type_ptr = let get_builtin_pname_opt trans_unit_ctx qual_name decl_opt type_ptr =
let get_deprecated_attr_arg decl = let get_deprecated_attr_arg decl =
let open Clang_ast_t in let open Clang_ast_t in
let decl_info = Clang_ast_proj.get_decl_tuple decl in let decl_info = Clang_ast_proj.get_decl_tuple decl in
@ -451,6 +451,7 @@ struct
coming from ASTExporter.h in facebook-clang-plugins *) coming from ASTExporter.h in facebook-clang-plugins *)
assert false) assert false)
| None -> None in | None -> None in
let name = QualifiedCppName.to_qual_string qual_name in
let function_attr_opt = Option.bind decl_opt get_deprecated_attr_arg in let function_attr_opt = Option.bind decl_opt get_deprecated_attr_arg in
match function_attr_opt with match function_attr_opt with
| Some attr when CTrans_models.is_modeled_attribute attr -> | Some attr when CTrans_models.is_modeled_attribute attr ->
@ -473,12 +474,14 @@ struct
let name_info, decl_ptr, type_ptr = CAst_utils.get_info_from_decl_ref decl_ref in let name_info, decl_ptr, type_ptr = CAst_utils.get_info_from_decl_ref decl_ref in
let decl_opt = CAst_utils.get_function_decl_with_body decl_ptr in let decl_opt = CAst_utils.get_function_decl_with_body decl_ptr in
Option.iter ~f:(call_translation context) decl_opt; Option.iter ~f:(call_translation context) decl_opt;
let name = CAst_utils.get_qualified_name name_info in let qual_name = CAst_utils.get_qualified_name name_info in
let typ = CType_decl.type_ptr_to_sil_type context.tenv type_ptr in let typ = CType_decl.type_ptr_to_sil_type context.tenv type_ptr in
let pname = let pname =
match get_builtin_pname_opt context.translation_unit_context name decl_opt type_ptr with match get_builtin_pname_opt context.translation_unit_context qual_name decl_opt type_ptr with
| Some builtin_pname -> builtin_pname | Some builtin_pname -> builtin_pname
| None -> CMethod_trans.create_procdesc_with_pointer context decl_ptr None name in | None ->
let name = QualifiedCppName.to_qual_string qual_name in
CMethod_trans.create_procdesc_with_pointer context decl_ptr None name in
{ empty_res_trans with exps = [(Exp.Const (Const.Cfun pname), typ)] } { empty_res_trans with exps = [(Exp.Const (Const.Cfun pname), typ)] }
let field_deref_trans trans_state stmt_info pre_trans_result decl_ref ~is_constructor_init = let field_deref_trans trans_state stmt_info pre_trans_result decl_ref ~is_constructor_init =
@ -566,7 +569,7 @@ struct
type_ptr with type_ptr with
| Some builtin_pname -> builtin_pname | Some builtin_pname -> builtin_pname
| None -> | None ->
let class_typename = Typ.Name.Cpp.from_string let class_typename = Typ.Name.Cpp.from_qual_name Typ.NoTemplate
(CAst_utils.get_class_name_from_member name_info) in (CAst_utils.get_class_name_from_member name_info) in
CMethod_trans.create_procdesc_with_pointer context decl_ptr (Some class_typename) CMethod_trans.create_procdesc_with_pointer context decl_ptr (Some class_typename)
method_name in method_name in

@ -74,6 +74,7 @@ let get_first_param_typedef_string_opt type_ptr =
match CAst_utils.get_desugared_type type_ptr with match CAst_utils.get_desugared_type type_ptr with
| Some Clang_ast_t.FunctionProtoType (_, _, {pti_params_type = [param_ptr]}) -> | Some Clang_ast_t.FunctionProtoType (_, _, {pti_params_type = [param_ptr]}) ->
CAst_utils.name_opt_of_typedef_type_ptr param_ptr CAst_utils.name_opt_of_typedef_type_ptr param_ptr
|> Option.map ~f:QualifiedCppName.to_qual_string
| _ -> None | _ -> None
let is_release_builtin funct fun_type = let is_release_builtin funct fun_type =

@ -59,7 +59,7 @@ let get_base_class_name_from_category decl =
| Some decl_ref -> | Some decl_ref ->
(match CAst_utils.get_decl decl_ref.Clang_ast_t.dr_decl_pointer with (match CAst_utils.get_decl decl_ref.Clang_ast_t.dr_decl_pointer with
| Some ObjCInterfaceDecl (_, name_info, _, _, _) -> | Some ObjCInterfaceDecl (_, name_info, _, _, _) ->
Some (Typ.Name.Objc.from_string (CAst_utils.get_qualified_name name_info)) Some (Typ.Name.Objc.from_qual_name (CAst_utils.get_qualified_name name_info))
| _ -> None) | _ -> None)
| None -> None | None -> None
@ -67,7 +67,7 @@ let get_base_class_name_from_category decl =
(* to the corresponding class. Update the tenv accordingly.*) (* to the corresponding class. Update the tenv accordingly.*)
let process_category type_ptr_to_sil_type tenv class_name decl_info decl_list = let process_category type_ptr_to_sil_type tenv class_name decl_info decl_list =
let decl_fields = CField_decl.get_fields type_ptr_to_sil_type tenv decl_list in let decl_fields = CField_decl.get_fields type_ptr_to_sil_type tenv decl_list in
let class_tn_name = Typ.Name.Objc.from_string class_name in let class_tn_name = Typ.Name.Objc.from_qual_name class_name in
let decl_key = `DeclPtr decl_info.Clang_ast_t.di_pointer in let decl_key = `DeclPtr decl_info.Clang_ast_t.di_pointer in
CAst_utils.update_sil_types_map decl_key (Typ.Tstruct class_tn_name); CAst_utils.update_sil_types_map decl_key (Typ.Tstruct class_tn_name);
(match Tenv.lookup tenv class_tn_name with (match Tenv.lookup tenv class_tn_name with
@ -76,7 +76,7 @@ let process_category type_ptr_to_sil_type tenv class_name decl_info decl_list =
ignore( ignore(
Tenv.mk_struct tenv Tenv.mk_struct tenv
~default:struct_typ ~fields:new_fields ~statics:[] ~methods:[] class_tn_name ); ~default:struct_typ ~fields:new_fields ~statics:[] ~methods:[] class_tn_name );
Logging.out_debug " Updating info for class '%s' in tenv\n" class_name Logging.out_debug " Updating info for class '%a' in tenv\n" QualifiedCppName.pp class_name
| _ -> ()); | _ -> ());
Typ.Tstruct class_tn_name Typ.Tstruct class_tn_name
@ -86,7 +86,7 @@ let category_decl type_ptr_to_sil_type tenv decl =
| ObjCCategoryDecl (decl_info, name_info, decl_list, _, cdi) -> | ObjCCategoryDecl (decl_info, name_info, decl_list, _, cdi) ->
let name = CAst_utils.get_qualified_name name_info in let name = CAst_utils.get_qualified_name name_info in
let class_name = get_classname_from_category_decl cdi in let class_name = get_classname_from_category_decl cdi in
Logging.out_debug "ADDING: ObjCCategoryDecl for '%s'\n" name; Logging.out_debug "ADDING: ObjCCategoryDecl for '%a'\n" QualifiedCppName.pp name;
let _ = add_class_decl type_ptr_to_sil_type tenv cdi in let _ = add_class_decl type_ptr_to_sil_type tenv cdi in
let typ = process_category type_ptr_to_sil_type tenv class_name decl_info decl_list in let typ = process_category type_ptr_to_sil_type tenv class_name decl_info decl_list in
let _ = add_category_implementation type_ptr_to_sil_type tenv cdi in let _ = add_category_implementation type_ptr_to_sil_type tenv cdi in
@ -99,7 +99,7 @@ let category_impl_decl type_ptr_to_sil_type tenv decl =
| ObjCCategoryImplDecl (decl_info, name_info, decl_list, _, cii) -> | ObjCCategoryImplDecl (decl_info, name_info, decl_list, _, cii) ->
let name = CAst_utils.get_qualified_name name_info in let name = CAst_utils.get_qualified_name name_info in
let class_name = get_classname_from_category_impl cii in let class_name = get_classname_from_category_impl cii in
Logging.out_debug "ADDING: ObjCCategoryImplDecl for '%s'\n" name; Logging.out_debug "ADDING: ObjCCategoryImplDecl for '%a'\n" QualifiedCppName.pp name;
let _ = add_category_decl type_ptr_to_sil_type tenv cii in let _ = add_category_decl type_ptr_to_sil_type tenv cii in
let typ = process_category type_ptr_to_sil_type tenv class_name decl_info decl_list in let typ = process_category type_ptr_to_sil_type tenv class_name decl_info decl_list in
typ typ

@ -60,8 +60,8 @@ let get_interface_supers super_opt protocols =
let super_class = let super_class =
match super_opt with match super_opt with
| None -> [] | None -> []
| Some super -> [Typ.Name.Objc.from_string super] in | Some super -> [Typ.Name.Objc.from_qual_name super] in
let protocol_names = List.map ~f:Typ.Name.Objc.protocol_from_string protocols in let protocol_names = List.map ~f:Typ.Name.Objc.protocol_from_qual_name protocols in
let super_classes = super_class@protocol_names in let super_classes = super_class@protocol_names in
super_classes super_classes
@ -76,8 +76,8 @@ let create_supers_fields type_ptr_to_sil_type tenv decl_list
(* Adds pairs (interface name, interface_type_info) to the global environment. *) (* Adds pairs (interface name, interface_type_info) to the global environment. *)
let add_class_to_tenv type_ptr_to_sil_type tenv decl_info name_info decl_list ocidi = let add_class_to_tenv type_ptr_to_sil_type tenv decl_info name_info decl_list ocidi =
let class_name = CAst_utils.get_qualified_name name_info in let class_name = CAst_utils.get_qualified_name name_info in
Logging.out_debug "ADDING: ObjCInterfaceDecl for '%s'\n" class_name; Logging.out_debug "ADDING: ObjCInterfaceDecl for '%a'\n" QualifiedCppName.pp class_name;
let interface_name = Typ.Name.Objc.from_string class_name in let interface_name = Typ.Name.Objc.from_qual_name class_name in
let decl_key = `DeclPtr decl_info.Clang_ast_t.di_pointer in let decl_key = `DeclPtr decl_info.Clang_ast_t.di_pointer in
CAst_utils.update_sil_types_map decl_key (Typ.Tstruct interface_name); CAst_utils.update_sil_types_map decl_key (Typ.Tstruct interface_name);
let decl_supers, decl_fields = let decl_supers, decl_fields =
@ -100,7 +100,7 @@ let add_class_to_tenv type_ptr_to_sil_type tenv decl_info name_info decl_list oc
(* We add the special hidden counter_field for implementing reference counting *) (* We add the special hidden counter_field for implementing reference counting *)
let modelled_fields = Typ.Struct.objc_ref_counter_field :: CField_decl.modelled_field name_info in let modelled_fields = Typ.Struct.objc_ref_counter_field :: CField_decl.modelled_field name_info in
let all_fields = CGeneral_utils.append_no_duplicates_fields modelled_fields fields in let all_fields = CGeneral_utils.append_no_duplicates_fields modelled_fields fields in
Logging.out_debug "Class %s field:\n" class_name; Logging.out_debug "Class %a field:\n" QualifiedCppName.pp class_name;
List.iter ~f:(fun (fn, _, _) -> List.iter ~f:(fun (fn, _, _) ->
Logging.out_debug "-----> field: '%s'\n" (Fieldname.to_string fn)) all_fields; Logging.out_debug "-----> field: '%s'\n" (Fieldname.to_string fn)) all_fields;
ignore( ignore(
@ -136,11 +136,12 @@ let interface_impl_declaration type_ptr_to_sil_type tenv decl =
match decl with match decl with
| ObjCImplementationDecl (decl_info, name_info, decl_list, _, idi) -> | ObjCImplementationDecl (decl_info, name_info, decl_list, _, idi) ->
let class_name = CAst_utils.get_qualified_name name_info in let class_name = CAst_utils.get_qualified_name name_info in
Logging.out_debug "ADDING: ObjCImplementationDecl for class '%s'\n" class_name; Logging.out_debug
"ADDING: ObjCImplementationDecl for class '%a'\n" QualifiedCppName.pp class_name;
let _ = add_class_decl type_ptr_to_sil_type tenv idi in let _ = add_class_decl type_ptr_to_sil_type tenv idi in
let fields = CField_decl.get_fields type_ptr_to_sil_type tenv decl_list in let fields = CField_decl.get_fields type_ptr_to_sil_type tenv decl_list in
CField_decl.add_missing_fields tenv class_name fields; CField_decl.add_missing_fields tenv class_name fields;
let class_tn_name = Typ.Name.Objc.from_string class_name in let class_tn_name = Typ.Name.Objc.from_qual_name class_name in
let decl_key = `DeclPtr decl_info.Clang_ast_t.di_pointer in let decl_key = `DeclPtr decl_info.Clang_ast_t.di_pointer in
let class_typ = Typ.Tstruct class_tn_name in let class_typ = Typ.Tstruct class_tn_name in
CAst_utils.update_sil_types_map decl_key class_typ; CAst_utils.update_sil_types_map decl_key class_typ;

@ -24,8 +24,8 @@ let protocol_decl type_ptr_to_sil_type tenv decl =
(* Protocol_type_info contains the methods composing the protocol. *) (* Protocol_type_info contains the methods composing the protocol. *)
(* Here we are giving a similar treatment as interfaces (see above)*) (* Here we are giving a similar treatment as interfaces (see above)*)
(* It may turn out that we need a more specific treatment for protocols*) (* It may turn out that we need a more specific treatment for protocols*)
Logging.out_debug "ADDING: ObjCProtocolDecl for '%s'\n" name; Logging.out_debug "ADDING: ObjCProtocolDecl for '%a'\n" QualifiedCppName.pp name;
let protocol_name = Typ.Name.Objc.protocol_from_string name in let protocol_name = Typ.Name.Objc.protocol_from_qual_name name in
let decl_key = `DeclPtr decl_info.Clang_ast_t.di_pointer in let decl_key = `DeclPtr decl_info.Clang_ast_t.di_pointer in
CAst_utils.update_sil_types_map decl_key (Typ.Tstruct protocol_name); CAst_utils.update_sil_types_map decl_key (Typ.Tstruct protocol_name);
ignore( Tenv.mk_struct tenv ~methods:[] protocol_name ); ignore( Tenv.mk_struct tenv ~methods:[] protocol_name );

Loading…
Cancel
Save