Forked from
pub / Frama Clang
802 commits behind the upstream repository.
-
Virgile Prevosto authoredVirgile Prevosto authored
cxx_utils.ml 14.60 KiB
(**************************************************************************)
(* *)
(* This file is part of Frama-Clang *)
(* *)
(* Copyright (C) 2012-2018 *)
(* CEA (Commissariat à l'énergie atomique et aux énergies *)
(* alternatives) *)
(* *)
(* you can redistribute it and/or modify it under the terms of the GNU *)
(* Lesser General Public License as published by the Free Software *)
(* Foundation, version 2.1. *)
(* *)
(* It is distributed in the hope that it will be useful, *)
(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)
(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)
(* GNU Lesser General Public License for more details. *)
(* *)
(* See the GNU Lesser General Public License version 2.1 *)
(* for more details (enclosed in the file LICENSE). *)
(* *)
(**************************************************************************)
open Intermediate_format
open Genlex
let dkey = Frama_Clang_option.register_category "parse_ident"
let builtin_types = [ "__builtin_va_list" ]
let is_builtin_type t = List.mem t builtin_types
let is_builtin_qual_type n = List.mem n.decl_name builtin_types
let is_const_type t = List.mem Const t.qualifier
let add_qualifier typ qual =
if List.mem qual typ.qualifier then typ else
{ typ with qualifier = qual :: typ.qualifier }
let add_qualifiers quals typ = List.fold_left add_qualifier typ quals
let lexer = Genlex.make_lexer
[ "("; ")"; "::"; "<"; ">"; ","; "*"; "&"; "&&"; "~";
"bool"; "char"; "const"; "float"; "double"; "int"; "long"; "short";
"static"; "unsigned"; "void"; "volatile"; "wchar_t";
]
let rec wait_for l s =
match Stream.peek s with
| Some l' -> Stream.junk s; if l <> l' then wait_for l s
| None -> ()
let identify_u_long b s =
match Stream.peek s with
| Some (Kwd "int") ->
Stream.junk s;
if b then raise (Stream.Error "illegal type specification")
else Intermediate_format.Int IULong
| _ -> Intermediate_format.Int IULong
let identify_u_short b s =
match Stream.peek s with
| Some (Kwd "int") ->
Stream.junk s;
if b then raise (Stream.Error "illegal type specification")
else Intermediate_format.Int IUShort
| _ -> Intermediate_format.Int IUShort
let identify_s_long b s =
match Stream.peek s with
| Some (Kwd "int") ->
Stream.junk s;
if b then raise (Stream.Error "illegal type specification")
else Intermediate_format.Int ILong
| _ -> Intermediate_format.Int ILong
let identify_s_short b s =
match Stream.peek s with
| Some (Kwd "int") ->
Stream.junk s;
if b then raise (Stream.Error "illegal type specification")
else Intermediate_format.Int IShort
| _ -> Intermediate_format.Int IShort
let rec identify_unsigned b s =
match Stream.peek s with
| Some (Kwd "long") -> Stream.junk s; identify_u_long b s
| Some (Kwd "short") -> Stream.junk s; identify_u_short b s
| Some (Kwd "int") ->
Stream.junk s;
if b then raise (Stream.Error "illegal type specification")
else identify_unsigned true s
| Some (Kwd "char") ->
Stream.junk s;
if b then raise (Stream.Error "illegal type specification")
else Intermediate_format.Int IUChar
| _ -> Intermediate_format.Int IUInt
let rec identify_signed b s =
match Stream.peek s with
| Some (Kwd "long") -> Stream.junk s; identify_s_long b s
| Some (Kwd "short") -> Stream.junk s; identify_s_short b s
| Some (Kwd "int") ->
Stream.junk s;
if b then raise (Stream.Error "illegal type specification")
else identify_signed true s
| Some (Kwd "char") ->
Stream.junk s;
if b then raise (Stream.Error "illegal type specification")
else Intermediate_format.Int ISChar
| _ -> Intermediate_format.Int IInt
let identify_long_double _ = Intermediate_format.Float FLongDouble
let identify_float _ = Intermediate_format.Float FFloat
let rec identify_long b s =
match Stream.peek s with
| Some (Kwd "int") ->
Stream.junk s;
if b then raise (Stream.Error "illegal type specification")
else identify_long true s
| Some (Kwd "signed") -> Stream.junk s; identify_s_long b s
| Some (Kwd "unsigned") -> Stream.junk s; identify_u_long b s
| Some (Kwd "double") ->
Stream.junk s;
if b then raise (Stream.Error "illegal type specification")
else identify_long_double s
| _ -> Intermediate_format.Int ILong
let rec identify_short b s =
match Stream.peek s with
| Some (Kwd "int") ->
Stream.junk s;
if b then raise (Stream.Error "illegal type specification")
else identify_short true s
| Some (Kwd "signed") -> Stream.junk s; identify_s_short b s
| Some (Kwd "unsigned") -> Stream.junk s; identify_u_short b s
| _ -> Intermediate_format.Int IShort
let identify_double s =
match Stream.peek s with
| Some (Kwd "long") -> Stream.junk s; identify_long_double s
| _ -> Intermediate_format.Float FDouble
let identify_char s =
match Stream.peek s with
| Some (Kwd "signed") -> Stream.junk s; Intermediate_format.Int ISChar
| Some (Kwd "unsigned") -> Stream.junk s; Intermediate_format.Int IUChar
| _ -> Intermediate_format.Int IChar
let identify_int s =
match Stream.peek s with
| Some (Kwd "unsigned") -> Stream.junk s; identify_unsigned true s
| Some (Kwd "long") -> Stream.junk s; identify_long true s
| Some (Kwd "short") -> Stream.junk s; identify_short true s
| Some (Kwd "signed") -> Stream.junk s; identify_signed true s
| _ -> Intermediate_format.Int IInt
let decl_flag s =
match Stream.peek s with
| Some (Kwd "const") -> Stream.junk s; Some Const
| Some (Kwd "volatile") -> Stream.junk s; Some Volatile
| Some (Kwd "restrict") -> Stream.junk s; Some Restrict
| Some (Kwd "static") -> Stream.junk s; Some Static
| _ -> None
let opt_dimension s =
match Stream.peek s with
| Some (Int i) ->
Stream.junk s;
Some { eloc=(Lexing.dummy_pos, Lexing.dummy_pos);
econtent=Constant (IntCst (IInt, ICLiteral, Int64.of_int i)) }
| _ -> wait_for (Kwd "]") s; None
let rec parse_simple_type s =
match ident s with
| Some s -> Some (Struct (s,TStandard))
| None ->
begin match Stream.peek s with
| Some (Kwd "bool") -> Stream.junk s; Some (Intermediate_format.Int IBool)
| Some (Kwd "void") -> Stream.junk s; Some Void
| Some (Kwd "int") -> Stream.junk s; Some (identify_int s)
| Some (Kwd "char") -> Stream.junk s; Some (identify_char s)
| Some (Kwd "unsigned") -> Stream.junk s; Some (identify_unsigned false s)
| Some (Kwd "long") -> Stream.junk s; Some (identify_long false s)
| Some (Kwd "short") -> Stream.junk s; Some (identify_short false s)
| Some (Kwd "signed") -> Stream.junk s; Some (identify_signed false s)
| Some (Kwd "float") -> Stream.junk s; Some (identify_float s)
| Some (Kwd "double") -> Stream.junk s; Some (identify_double s)
| _ -> None
end
(* Not really accurate (should take priorities and parentheses into account). *)
and modifier typ s =
match Stream.peek s with
| Some (Kwd "*") ->
Stream.junk s;
modifier (Pointer (PDataPointer { qualifier = []; plain_type = typ})) s
| Some (Kwd "&") ->
Stream.junk s;
modifier (LVReference (PDataPointer {qualifier=[];plain_type=typ})) s
| Some (Kwd "&&") ->
Stream.junk s;
modifier (RVReference (PDataPointer {qualifier=[];plain_type=typ})) s
| Some (Kwd "[") ->
begin
Stream.junk s;
let dim = opt_dimension s in
modifier
(Array { subtype = {qualifier=[];plain_type=typ}; dimension = dim }) s
end
| _ -> typ
and template_args s =
let t = template_arg s in
let l = try_next_template_arg s in
t :: l
and try_next_template_arg s =
match Stream.peek s with
| Some (Kwd ",") -> Stream.junk s; template_args s
| _ -> []
(* Very incomplete *)
and template_arg s =
match Stream.peek s with
| Some (Int i) ->
Stream.junk s; TPConstant (IntCst(IInt, ICLiteral, Int64.of_int i))
| _ ->
begin match ident s with
| Some id -> TPStructOrClass id
| None -> TPTypename (parse_type s)
(* NB: we don't distinguish anymore between STA_TYPE and STA_ATOMIC*)
end
and template t s =
match Stream.peek s with
| Some (Kwd "<") ->
Stream.junk s;
let l = template_args s in
Stream.junk s; (* TODO: check that it is Kwd ">" *)
maybe_qual (QTemplateInstance (t,l)) s
| _ -> maybe_qual (QNamespace t) s
and ident s =
match Stream.npeek 2 s with
| Ident id :: _ ->
Frama_Clang_option.debug ~dkey "IDENT(%s)" id;
Stream.junk s;
Some (template id s)
| Kwd "~" :: Ident id :: _ ->
Frama_Clang_option.debug ~dkey "DESTR(%s)" id;
Some { prequalification = []; decl_name = "~" ^ id }
| _ -> None
and maybe_qual prefix s =
match Stream.peek s with
| Some Kwd "::" ->
Frama_Clang_option.debug ~dkey "QUAL";
Stream.junk s;
(match ident s with
| Some id ->
{ id with prequalification = prefix :: id.prequalification }
| None -> raise (Stream.Error "unsupported qualifier sequence"))
| _ ->
match prefix with
| QNamespace s | QStructOrClass s ->
{ prequalification = []; decl_name = s }
| QTemplateInstance (s,_) ->
Frama_Clang_option.warning
"Dropping template arguments during parsing of identifier";
{ prequalification = []; decl_name = s }
and parse_type_aux t s =
match decl_flag s with
| Some q -> parse_type_aux (add_qualifier t q) s
| None ->
(match parse_simple_type s with
| Some st ->
let typ = modifier st s in
{ t with plain_type = typ }
| None -> t)
and parse_type s =
parse_type_aux { qualifier = []; plain_type = Void } s
let rec parse_args s =
let a = parse_type s in
let l = maybe_args s in
a :: l
and maybe_args s =
match Stream.peek s with
| Some (Ident _) -> Stream.junk s; next_args s
| _ -> next_args s
and next_args s =
match Stream.peek s with
| Some (Kwd ",") -> Stream.junk s; parse_args s
| Some (Kwd ")") -> Stream.junk s; []
| _ -> raise (Stream.Error "Unfinished argument list")
let parse_formals s =
match Stream.peek s with
| Some (Kwd "(") ->
Stream.junk s; Frama_Clang_option.debug ~dkey "IS_SIG"; Some (parse_args s)
| _ -> None
let plain_ident_or_func id s =
match ident s with
| Some f ->
(match parse_formals s with
| Some args ->
f,
Some
(FKFunction,
{ result =
{ qualifier = [];
plain_type = Struct (id, TStandard);
};
parameter = args;
variadic = false;
})
| None -> id, None)
| None ->
(match parse_formals s with
| Some args ->
let kind =
if String.length id.decl_name > 0 && id.decl_name.[0] = '~' then
FKDestructor true
else begin
match id.prequalification with
| [] -> FKFunction
| l ->
match Extlib.last l with
| QNamespace s | QStructOrClass s | QTemplateInstance (s,_)
when s = id.decl_name ->
FKConstructor true
| QNamespace _ | QStructOrClass _ | QTemplateInstance _ ->
FKFunction
end
in
(id,
Some (kind,
{ result = { qualifier = []; plain_type = Struct (id, TStandard)};
parameter = args;
variadic = false;
}))
| None -> id, None)
let parse_signature s =
match Stream.peek s with
| Some (Kwd "::") ->
Stream.junk s;
(match ident s with
| Some i -> plain_ident_or_func i s
| None -> raise (Stream.Error "ill-formed C++ full qualification"))
| _ ->
(match ident s with
| Some i -> plain_ident_or_func i s
| None ->
let rt = parse_type s in
(match ident s with
| Some f ->
(match parse_formals s with
| Some args ->
f,
Some (FKFunction,
{ result = rt; parameter = args; variadic = false })
| None -> raise (Stream.Error "unexpected format for C++ signature"))
| None -> raise (Stream.Error "unexpected format for C++ signature")))
exception NoCxxName
let analyse_entrypoint s =
try
let input = Stream.of_string s in
let input = lexer input in
parse_signature input
with
| Stream.Error e ->
Frama_Clang_option.debug ~dkey
"Unable to parse symbol %s as C++ function (%s). \
Considering it as standard C identifier" s e;
raise NoCxxName
let empty_qual s = { prequalification = []; decl_name = s }
let meth_name class_name tkind decl_name =
let last_elt =
match tkind with
| TStandard -> QStructOrClass class_name.decl_name
| TTemplateInstance i -> QTemplateInstance (class_name.decl_name, i)
in
let prequalification= List.append class_name.prequalification [last_elt] in
{ prequalification; decl_name }
let unqual_type t = { qualifier = []; plain_type = t }
let const_type t = { qualifier = [ Const ]; plain_type = t }
let const_qual_type t =
if List.mem Const t.qualifier then t
else { t with qualifier = Const :: t.qualifier }
let force_ptr_to_const p =
match p with
| { plain_type = Pointer (PDataPointer t) } ->
let t = const_qual_type t in
{ p with plain_type = Pointer (PDataPointer t) }
| _ -> p
let make_lambda_type result args closure =
let parameter = List.map (fun x -> x.arg_type) args in
Lambda ({ result; parameter; variadic = false }, closure)
let plain_obj_ptr t = Pointer (PDataPointer t)
let plain_obj_lvref t = LVReference (PDataPointer t)
let plain_obj_rvref t = RVReference (PDataPointer t)
let obj_ptr t = unqual_type (plain_obj_ptr t)
let obj_lvref t = unqual_type (plain_obj_lvref t)
let obj_rvref t = unqual_type (plain_obj_rvref t)
let plain_class_ptr (n,t) = plain_obj_ptr (unqual_type (Struct (n,t)))
let plain_class_lvref (n,t) = plain_obj_lvref (unqual_type (Struct (n,t)))
let plain_class_rvref (n,t) = plain_obj_rvref (unqual_type (Struct (n,t)))
let class_ptr (n,t) = unqual_type (plain_class_ptr (n,t))
let class_lvref (n,t) = unqual_type (plain_class_lvref (n,t))
let class_rvref (n,t) = unqual_type (plain_class_rvref (n,t))