Skip to content
Snippets Groups Projects
Forked from pub / Frama Clang
557 commits behind the upstream repository.
convert_link.ml 6.85 KiB
(**************************************************************************)
(*                                                                        *)
(*  This file is part of Frama-Clang                                      *)
(*                                                                        *)
(*  Copyright (C) 2012-2020                                               *)
(*    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 Cil
open Cil_types

let transform_category =
  File.register_code_transformation_category "C++ cleanup"

let exn_dkey = Frama_Clang_option.register_category "exn:inherit"

let find_comp name =
  try 
    (match Globals.Types.find_type Logic_typing.Struct name with
      | TComp(c,_,_) -> c
      | _ ->
        Frama_Clang_option.fatal "unexpected type returned for struct %s" name)
  with Not_found ->
    Frama_Clang_option.fatal
      "Did not find struct for mangled class name %s" name

let rec coerce lv comp path =
  match path with
    | [] -> lv
    | (_,_,direct_base) :: path ->
      let cname = Extlib.uncurry Mangling.mangle direct_base None in
      let fname =
        Extlib.uncurry 
          (Convert.create_base_field_name Convert_env.empty_env) direct_base
      in
      let field = Cil.getCompField comp fname in
      let new_lv = Cil.addOffsetLval (Field (field, NoOffset)) lv in
      let new_comp = find_comp cname in
      coerce new_lv new_comp path

(* all the names in catch should avoid typedefs except builtin ones *)
let add_subtypes kf = function
  | Catch_all -> Catch_all
  | Catch_exn(vi,l) as bind ->
    let loc = vi.vdecl in
    (match (Cil.unrollType vi.vtype) with
      | TComp({ corig_name = base },_,_) as base_struct ->
        let qualified_base = Class.class_of_mangled base in
        (match qualified_base with
          | Some qualified_base ->
            let derived = Class.subtypes qualified_base in
            (* if there is no derived class, we can keep a plain clause. *)
            if Fclang_datatype.Qualified_name.Set.is_empty derived then bind
            else begin
                (* add all inherited classes as auxiliary catch binders.
                   Do not forget to also add the base class itself. *)
              let f = Kernel_function.get_definition kf in
              File.must_recompute_cfg f;
              Ast.mark_as_grown ();
              let base_block = Cil.mkBlock [] in
              let base_vi =
                Cil.makeTempVar
                  f ~insert:false ~name:(vi.vname ^ "_0") base_struct
              in
              f.slocals <- base_vi :: f.slocals;
              (* TODO: use the appropriate copy constructor instead *)
              let copy =
                Cil.mkStmtOneInstr
                  ~valid_sid:true
                  (Set ((Var vi, NoOffset),Cil.evar ~loc base_vi, loc))
              in
              base_block.bstmts <- [copy];
              let binders = (base_vi, base_block) :: l in
              let treat_one_derived c (i, binders as acc) =
                if Class.has_unambiguous_path c qualified_base
                  && Class.has_public_path c qualified_base
                then begin
                  Frama_Clang_option.debug ~dkey:exn_dkey
                    "Adding derived catch clause for %a from %a"
                    Fclang_datatype.Qualified_name.pretty c
                    Fclang_datatype.Qualified_name.pretty qualified_base;
                  let path = Class.inheritance_path c qualified_base in
                  let block = Cil.mkBlock [] in
                  let mangled = Extlib.uncurry Mangling.mangle c None in
                  let struct_info = find_comp mangled in
                  let my_vi =
                    Cil.makeTempVar f
                      ~insert:false
                      ~name:(vi.vname ^ "_" ^ (string_of_int i))
                      (TComp (struct_info, { scache = Not_Computed }, []))
                  in
                  f.slocals <- my_vi :: f.slocals;
                  let lv = coerce (Cil.var my_vi) struct_info path in
                  let e = Cil.new_exp ~loc (Lval lv) in
                  let copy =
                    Cil.mkStmtOneInstr (Set ((Var vi,NoOffset),e,loc)) in
                  block.bstmts <- [copy];
                  (i+1,(my_vi, block) :: binders)
                end else acc
              in
              let _,binders =
                Fclang_datatype.Qualified_name.Set.fold 
                  treat_one_derived derived (1,binders)
              in
              Catch_exn(vi,binders)
            end
          | None -> bind)
      | _ -> bind)

class clean =
  object(self)
    inherit Visitor.frama_c_inplace

    method! vstmt_aux s =
      match s.skind with
        | TryCatch(t,c,l) ->
          let c =
            List.map
              (fun (bind, body) ->
                add_subtypes (Option.get self#current_kf) bind, body)
              c
          in
          s.skind <- TryCatch(t,c,l);
          DoChildren
        | Instr (
            Call (
              None,
              { enode = Lval (Var f, NoOffset) },
              { enode =
                  (AddrOf (Var o, NoOffset)
                  | CastE (_, { enode = AddrOf (Var o, NoOffset)}))}
              :: args,
              loc))
          when Mangling.is_constructor_name f.vname
            && not o.vglob && not o.vformal ->
          let init = ConsInit (f,args,Constructor) in
          o.vdefined <- true;
          s.skind <- Instr (Local_init(o,init,loc));
          DoChildren
        | _ -> DoChildren
  end

let clean_whole_program file =
  Visitor.visitFramacFileSameGlobals (new clean) file

let register_transformation () =
  File.add_code_transformation_after_cleanup
    ~deps:[]
    ~before:[Exn_flow.transform_category]
    transform_category
    clean_whole_program