Skip to content
Snippets Groups Projects
Forked from pub / Frama Clang
802 commits behind the upstream repository.
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))