Skip to content
Snippets Groups Projects
visit.ml 29.68 KiB
(**************************************************************************)
(*                                                                        *)
(*  This file is part of the Frama-C's E-ACSL plug-in.                    *)
(*                                                                        *)
(*  Copyright (C) 2012-2016                                               *)
(*    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/LGPLv2.1).             *)
(*                                                                        *)
(**************************************************************************)

module E_acsl_label = Label
open Cil_types
open Cil_datatype

let dkey = Options.dkey_translation

let rename_alloc_function ~create bhv vi =
  let is_alloc_name s =
    s = "malloc" || s = "free" || s = "realloc" || s = "calloc"
  in
  if is_alloc_name vi.vname then
    let new_name =  "__" ^ vi.vname in
    let kf =
      try Globals.Functions.find_by_name new_name
      with Not_found -> assert false
    in
    if create then Cil.makeGlobalVar new_name vi.vtype
    else Cil.get_varinfo bhv (Globals.Functions.get_vi kf)
  else
    vi

let allocate_function env kf =
  List.fold_left
    (fun env vi -> 
      if Mmodel_analysis.must_model_vi ~kf vi then
        let vi = Cil.get_varinfo (Env.get_behavior env) vi in
        Env.add_stmt env (Misc.mk_store_stmt vi)
      else
        env)
    env
    (Kernel_function.get_formals kf)

let deallocate_function env kf  = 
  List.fold_left
    (fun env vi -> 
      if Mmodel_analysis.must_model_vi ~kf vi then 
        let vi = Cil.get_varinfo (Env.get_behavior env) vi in
        Env.add_stmt env (Misc.mk_delete_stmt vi)
      else
        env)
    env
    (Kernel_function.get_formals kf)

(* ************************************************************************** *)
(* Visitor *)
(* ************************************************************************** *)

(* local references to the below visitor and to [do_visit] *)
let function_env = ref Env.dummy
let dft_funspec = Cil.empty_funspec ()
let funspec = ref dft_funspec

(* the main visitor performing e-acsl checking and C code generator *)
class e_acsl_visitor prj generate = object (self)

  inherit Visitor.generic_frama_c_visitor 
    (if generate then Cil.copy_visit prj else Cil.inplace_visit ())

  val mutable main_fct = None
  (* fundec of the main entry point, in the new project [prj].
     [None] while the global corresponding to this fundec has not been
     visited *)

  val mutable is_initializer = false
  (* Global flag set to [true] if a currently visited node
    belongs to a global initializer and set to [false] otherwise *)

  val global_vars: init option Varinfo.Hashtbl.t = Varinfo.Hashtbl.create 7
  (* Hashtable mapping global variables (as Cil_type.varinfo) to their
   initializers aiming to capture memory allocated by global variable
   declarations and initilisation. At runtime the memory blocks corresponding
   to space occupied by global are recorded via a call to
   [__e_acsl_memory_init] instrumented before (almost) anything else in the
   [main] function. Each variable stored by [global_vars] will be handled in
   the body of [__e_acsl_memory_init] as follows:
      __store_block(...); // Record a memory block used by the variable
      __full_init(...);   // ... and mark it as initialized memory

    NOTE: In [global_vars] keys belong to the original project while values
    belong to the new one *)

  method private reset_env () =
    function_env := Env.empty (self :> Visitor.frama_c_visitor)

  method !vfile _f =
    (* copy the options used during the visit in the new project: it is the
       right place to do this: it is still before visiting, but after
       that the visitor internals reset all of them :-(. *)
    let cur = Project.current () in
    let selection = 
      State_selection.of_list 
        [ Options.Gmp_only.self; Options.Check.self; Options.Full_mmodel.self;
          Kernel.SignedOverflow.self; Kernel.UnsignedOverflow.self;
          Kernel.SignedDowncast.self; Kernel.UnsignedDowncast.self;
          Kernel.Machdep.self ] 
    in
    if generate then Project.copy ~selection ~src:cur prj;
    Cil.DoChildrenPost
      (fun f ->
        (* extend the main with forward initialization and put it at end *)
        if generate then begin
          let must_init =
            not (Literal_strings.is_empty ())
            ||
              try
                Varinfo.Hashtbl.iter
                  (fun old_vi i -> match i with None | Some _ ->
                    if Mmodel_analysis.must_model_vi old_vi then raise Exit)
                  global_vars;
                false
              with Exit ->
                true
          in
          if must_init then begin
            let build_initializer () =
              Options.feedback ~dkey ~level:2 "building global initializer.";
              let return = 
                Cil.mkStmt ~valid_sid:true (Return(None, Location.unknown))
              in
              let env = Env.push !function_env in
              let stmts, env = 
                Varinfo.Hashtbl.fold_sorted
                  (fun old_vi i (stmts, env) -> 
                    let new_vi = Cil.get_varinfo self#behavior old_vi in
                    (* [model] creates an initialization statement
                    of the form [__full_init(...)]) for every global
                    variable which needs to be tracked and is not a Frama-C
                    builtin. Further the statement is appended to the provided
                    list of statements ([blk]) *)
                    let model blk =
                      if Mmodel_analysis.must_model_vi old_vi then
                        let blk =
                          if Kernel.LibEntry.get () then blk
                          else
                            Misc.mk_initialize ~loc:Location.unknown
                              (Cil.var new_vi)
                            :: blk
                        in
                        Misc.mk_store_stmt new_vi :: blk
                      else
                        stmts
                    in
                    match i with
                    | None -> model stmts, env
                    | Some (CompoundInit _) -> assert false
                    | Some (SingleInit e) -> 
                      let _, env = self#literal_string env e in stmts, env)
                  global_vars
                  ([ return ], env)
              in
              let stmts =
                (* literal strings initialization *)
                Literal_strings.fold
                  (fun s vi stmts ->
                    let loc = Location.unknown in
                    let e = Cil.new_exp ~loc:loc (Const (CStr s)) in
                    let str_size = Cil.new_exp loc (SizeOfStr s) in
                    Cil.mkStmtOneInstr ~valid_sid:true (Set(Cil.var vi, e, loc))
                    :: Misc.mk_store_stmt ~str_size vi
                    :: Misc.mk_full_init_stmt ~addr:false vi
                    :: Misc.mk_literal_string vi
                    :: stmts)
                  stmts
              in
              (* Create a new code block with generated statements *)
              let (b, env), stmts = match stmts with
                | [] -> assert false
                | stmt :: stmts ->
                  Env.pop_and_get env stmt ~global_clear:true Env.Before, stmts
              in
              function_env := env;
              let stmts = Cil.mkStmt ~valid_sid:true (Block b) :: stmts in
              let blk = Cil.mkBlock stmts in
              (* Create [__e_acsl_memory_init] function with definition
               for initialization of global variables *)
              let fname = "__e_acsl_memory_init" in
              let vi =
                Cil.makeGlobalVar ~source:true
                  fname
                  (TFun(Cil.voidType, Some [], false, []))
              in
              vi.vdefined <- true;
              (* There is no contract associated with the function *)
              let spec = Cil.empty_funspec () in
              (* Create function definition which has the list of the
               * generated initialization statements *)
              let fundec =
                { svar = vi;
                  sformals = [];
                  slocals = [];
                  smaxid = 0;
                  sbody = blk;
                  smaxstmtid = None;
                  sallstmts = [];
                  sspec = spec }
              in
              self#add_generated_variables_in_function fundec;
              let fct = Definition(fundec, Location.unknown) in
             (* Create and register [__e_acsl_memory_init] as kernel function *)
              let kf =
                { fundec = fct; return_stmt = Some return; spec = spec } 
              in
              Globals.Functions.register kf;
              Globals.Functions.replace_by_definition 
                spec fundec Location.unknown;
              let cil_fct = GFun(fundec, Location.unknown) in
              if Mmodel_analysis.use_model () then
                match main_fct with
                | Some main ->
                  let exp = Cil.evar ~loc:Location.unknown vi in
                  (* Create [__e_acsl_memory_init();] call *)
                  let stmt = 
                    Cil.mkStmtOneInstr ~valid_sid:true 
                      (Call(None, exp, [], Location.unknown))
                  in
                  vi.vreferenced <- true;
                  (* Add [__e_acsl_memory_init();] call as the first statement
                   to the [main] function *)
                  main.sbody.bstmts <- stmt :: main.sbody.bstmts;
                  let new_globals =
                    List.fold_right
                      (fun g acc -> match g with
                      | GFun({ svar = vi }, _)
                          when Varinfo.equal vi main.svar -> 
                        acc
                      | _ -> g :: acc)
                      f.globals
                      [ cil_fct; GFun(main, Location.unknown) ]
                  in
                  (* add the literal string varinfos as the very first
                     globals *)
                  let new_globals =
                    Literal_strings.fold
                      (fun _ vi l ->
                        GVar(vi, { init = None }, Location.unknown) :: l)
                      new_globals
                  in
                  f.globals <- new_globals
                | None -> 
                  Kernel.warning "@[no entry point specified:@ \
you must call function `%s' and `__e_acsl_memory_clean by yourself.@]" 
                    fname;
                  f.globals <- f.globals @ [ cil_fct ]
            in
            Project.on prj build_initializer ()
          end; (* must_init *)
	  let must_init_args = match main_fct with
	    | Some main ->
	       let charPtrPtrType = TPtr(Cil.charPtrType,[]) in
	       (* this might not be valid in an embedded environment,
		  where int/char** arguments is not necessarily
		  valid *)
	       (match main.sformals with
               | vi1 :: vi2 :: _ when
                    vi1.vtype = Cil.intType
                    && vi2.vtype = charPtrPtrType
                      && Mmodel_analysis.must_model_vi vi2 -> true
               | _ -> false)
	    | None -> false
	  in
	  if must_init_args then begin
	    (* init memory store, and then add program arguments if
	       there are any. must be called before global variables
	       are initialized. *)
	    let build_args_initializer () =
	      let main = Extlib.the main_fct in
	      let loc = Location.unknown in
              let args = List.map Cil.evar main.sformals in
	      let call = Misc.mk_call loc "__init_args" args in
	      main.sbody.bstmts <- call :: main.sbody.bstmts;
            in
            Project.on prj build_args_initializer ()
	  end;
	  (* reset copied states at the end to be observationally
	     equivalent to a standard visitor. *)
	  Project.clear ~selection ~project:prj ();
	 end; (* generate *)
	 f)

  method !vglob_aux = function
  | GVarDecl(vi, _) | GVar(vi, _, _)
  | GFunDecl(_, vi, _) | GFun({ svar = vi }, _)
      when Misc.is_library_loc vi.vdecl ->
    if generate then
      Cil.JustCopyPost
        (fun l -> 
          Misc.register_library_function (Cil.get_varinfo self#behavior vi);
          l)
    else begin
      Misc.register_library_function vi; 
      Cil.SkipChildren
    end
  | GVarDecl(vi, _) | GVar(vi, _, _) | GFun({ svar = vi }, _)
      when Cil.is_builtin vi ->
    if generate then Cil.JustCopy else Cil.SkipChildren
  | g when Misc.is_library_loc (Global.loc g) ->
    if generate then Cil.JustCopy else Cil.SkipChildren
  | g ->
    let do_it = function
      | GVar(vi, _, _) ->
        vi.vghost <- false; ()
      | GFun({ svar = vi } as fundec, _) ->
        vi.vghost <- false;
        (* remember that we have to remove the main later (see method
           [vfile]) *)
        if vi.vorig_name = Kernel.MainFunction.get () then
          main_fct <- Some fundec
      | GVarDecl(vi, _) | GFunDecl(_, vi, _) ->
        (* do not convert extern ghost variables, because they can't be linked,
           see bts #1392 *)
        if vi.vstorage <> Extern then
          vi.vghost <- false
      | _ -> 
        ()
    in
    (match g with
    | GVar(vi, _, _) | GVarDecl(vi, _) ->
      (* Make a unique mapping for each global variable omitting initializers.
       Initializers (used to capture literal strings) are added to
       [global_vars] via the [vinit] visitor method (see comments below). *)
      Varinfo.Hashtbl.replace global_vars vi None
    | _ -> ());
    if generate then Cil.DoChildrenPost(fun g -> List.iter do_it g; g)
    else Cil.DoChildren

  (* Add mappings from global variables to their initializers to [global_vars].
     Note that the below function captures only [SingleInit]s. All compound
     initializers (which contain single ones) are unrapped and thrown away. *)
  method !vinit vi _off _i =
    if generate then
      if Mmodel_analysis.must_model_vi vi then begin
        is_initializer <- true;
        Cil.DoChildrenPost
          (fun i ->
            (match is_initializer with
            (* Note the use of [add] instead [replace]. This is because a
             single variable can be associated with multiple initializers
             and all of them need to be captured. *)
            | true -> Varinfo.Hashtbl.add global_vars vi (Some i)
            | false-> ());
            is_initializer <- false;
          i)
      end else
        Cil.JustCopy
    else
      Cil.SkipChildren

  method !vvdec vi =
    (try
       let old_vi = Cil.get_original_varinfo self#behavior vi in
       let old_kf = Globals.Functions.get old_vi in
       funspec :=
         Cil.visitCilFunspec
         (self :> Cil.cilVisitor)
         (Annotations.funspec old_kf)
     with Not_found ->
       ());
    Cil.DoChildrenPost(rename_alloc_function ~create:true self#behavior)

  method !vvrbl _vi =
    Cil.DoChildrenPost(rename_alloc_function ~create:false self#behavior)

  method private add_generated_variables_in_function f =
    assert generate;
    let vars = Env.get_generated_variables !function_env in
    self#reset_env ();
    let locals, blocks =
      List.fold_left
        (fun (local_vars, block_vars as acc) (v, scope) -> match scope with
        | Env.Global -> acc
        | Env.Function -> v :: local_vars, v :: block_vars
        | Env.Local_block -> v :: local_vars, block_vars)
        (f.slocals, f.sbody.blocals)
        vars
    in
    f.slocals <- locals;
    f.sbody.blocals <- blocks

  method !vfunc f =
    if generate then
      let kf = Extlib.the self#current_kf in
      Options.feedback ~dkey ~level:2 "entering in function %a." 
        Kernel_function.pretty kf;
      List.iter (fun vi -> vi.vghost <- false) f.slocals;
      Cil.DoChildrenPost 
        (fun f -> 
          self#add_generated_variables_in_function f; 
          Options.feedback ~dkey ~level:2 "function %a done."
            Kernel_function.pretty kf;
          f)
    else
      Cil.DoChildren

  method private is_return old_kf stmt = 
    let old_ret = 
      try Kernel_function.find_return old_kf
      with Kernel_function.No_Statement -> assert false
    in
    Stmt.equal stmt (Cil.get_stmt self#behavior old_ret)

  method private is_first_stmt old_kf stmt =
    try 
      Stmt.equal
        (Cil.get_original_stmt self#behavior stmt) 
        (Kernel_function.find_first_stmt old_kf)
    with Kernel_function.No_Statement -> 
      assert false

  method private is_main old_kf =
    try
      let main, _ = Globals.entry_point () in
      Kernel_function.equal old_kf main
    with Globals.No_such_entry_point _s ->
      (* [JS 2013/05/21] already a warning in pre-analysis *)
      (*      Options.warning ~once:true "%s@ \
              @[The generated program may be incomplete.@]" 
              s;*)
      false

  method private literal_string env e =
    assert generate;
    let env_ref = ref env in
    let o = object
      inherit Cil.genericCilVisitor (Cil.copy_visit (Project.current ()))
      method !vexpr e = match e.enode with
      (* the guard below could be optimized: if no annotation **depends on this
         string**, then it is not required to monitor it.
         (currently, the guard says: "no annotation uses the memory model" *)
      | Const(CStr s) when Mmodel_analysis.use_model () ->
        (try
           let vi = Literal_strings.find s in
           (* if the literal string was already created, just get it. *)
           let exp = Cil.evar ~loc:e.eloc vi in
           Cil.ChangeTo exp
         with Not_found ->
           (* never seen this string before: replace it by a new global var *)
           let loc = e.eloc in
           let vi, exp, env =
             Env.new_var
               ~loc
               ~scope:Env.Global
               ~name:"literal_string"
               env
               None
               Cil.charPtrType
               (fun _ _ -> [] (* done in the initializer, see {!vglob_aux} *))
           in
           Literal_strings.add s vi;
           env_ref := env;
           Cil.ChangeTo exp)
      | _ -> 
        Cil.DoChildren
    end in
    let e = Cil.visitCilExpr o e in
    e, !env_ref

  method !vstmt_aux stmt =
    Options.debug ~level:4 "proceeding stmt (sid %d) %a@." 
      stmt.sid Stmt.pretty stmt;
    let kf = Extlib.the self#current_kf in
    let is_main = self#is_main kf in
    let env = Env.push !function_env in
    let env = match stmt.skind with
      | Loop _ -> Env.push_loop env
      | _ -> env
    in
    let env = 
      if self#is_first_stmt kf stmt then
        (* JS: should be done in the new project? *)
        let env = if generate then allocate_function env kf else env in
        (* translate the precondition of the function *)
        if Dup_functions.is_generated (Extlib.the self#current_kf) then
          Project.on prj (Translate.translate_pre_spec kf Kglobal env) !funspec
        else
          env
      else
        env
    in
    let env, new_annots =
      Annotations.fold_code_annot
        (fun _ old_a (env, new_annots) -> 
          let a =
            (* [VP] Don't use Visitor here, as it will fill the queue in the
               middle of the computation... *)
            Cil.visitCilCodeAnnotation (self :> Cil.cilVisitor) old_a
          in
          let env = 
            Project.on
              prj
              (Translate.translate_pre_code_annotation kf stmt env)
              a 
          in
          env, a :: new_annots)
        (Cil.get_original_stmt self#behavior stmt)
        (env, [])
    in
    function_env := env;
    let mk_block stmt =
      (* be careful: since this function is called in a post action, [env] has
         been modified from the time where pre actions have been executed.
         Use [function_env] to get it back. *)
      let env = !function_env in
      let env =
        if stmt.ghost && generate then begin
          stmt.ghost <- false;
          (* translate potential RTEs of ghost code *)
          let rtes = Rte.stmt ~warn:false kf stmt in
          Translate.translate_rte_annots Printer.pp_stmt stmt kf env rtes
        end else
          env
      in
      let mk_post_env env =
        (* [fold_right] to preserve order of generation of pre_conditions *) 
        Project.on
          prj
          (List.fold_right
             (fun a env -> 
               Translate.translate_post_code_annotation kf stmt env a)
             new_annots)
          env
      in
      (* handle loop invariants *)
      let new_stmt, env, must_mv = Loops.preserve_invariant prj env kf stmt in
      let new_stmt, env = 
        if self#is_return kf stmt then 
          (* must generate the post_block before including [stmt] (the 'return')
             since no code is executed after it. However, since this statement
             is pure (Cil invariant), that is semantically correct. *)
          let env = mk_post_env env in
          (* also handle the postcondition of the function and clear the env *)
          let env = 
            if Dup_functions.is_generated (Extlib.the self#current_kf) then
              Project.on
                prj
                (Translate.translate_post_spec kf Kglobal env) 
                !funspec 
            else
              env
          in
          (* de-allocating memory previously allocating by the kf *)
          (* JS: should be done in the new project? *)
          if generate then
            let env = deallocate_function env kf in
            let b, env = 
              Env.pop_and_get env new_stmt ~global_clear:true Env.After 
            in
            if is_main && Mmodel_analysis.use_model () then begin
              let stmts = b.bstmts in
              let l = List.rev stmts in
              match l with
              | [] -> assert false (* at least the 'return' stmt *)
              | ret :: l ->
                let loc = Stmt.loc stmt in
                let delete_stmts =
                  Varinfo.Hashtbl.fold_sorted
                    (fun old_vi i acc ->
                      if Mmodel_analysis.must_model_vi old_vi then
                        let new_vi = Cil.get_varinfo self#behavior old_vi in
                        (* Since there are multiple entries for same variables
                         do delete only variables without initializers, this
                         ensures that each variable is released once only *)
                         (match i with
                         | None -> Misc.mk_delete_stmt new_vi :: acc
                         | Some _ -> acc)
                      else
                        acc)
                    global_vars
                    [ Misc.mk_call ~loc "__e_acsl_memory_clean" []; ret ]
                in
                b.bstmts <- List.rev l @ delete_stmts
            end;
            let new_stmt = Misc.mk_block prj stmt b in
            (* move the labels of the return to the new block in order to
               evaluate the postcondition when jumping to them. *)
            E_acsl_label.move
              (self :> Visitor.generic_frama_c_visitor) stmt new_stmt;
            new_stmt, env
          else
            stmt, env
        else (* i.e. not (is_return stmt) *)
          if generate then
            (* must generate [pre_block] which includes [stmt] before generating
               [post_block] *)
            let pre_block, env =
              Env.pop_and_get env new_stmt ~global_clear:false Env.After
            in
            let env = mk_post_env (Env.push env) in
            let post_block, env =
              Env.pop_and_get
                env
                (Misc.mk_block prj new_stmt pre_block)
                ~global_clear:false
                Env.Before
            in
            (* TODO: must clear the local block anytime (?) *)
            Misc.mk_block prj new_stmt post_block, env
          else
            stmt, env
      in
      if must_mv then Loops.mv_invariants env ~old:new_stmt stmt;
      function_env := env;
      Options.debug ~level:4
      "@[new stmt (from sid %d):@ %a@]" stmt.sid Printer.pp_stmt new_stmt;
      if generate then new_stmt else stmt
    in
    Cil.ChangeDoChildrenPost(stmt, mk_block)

  method private add_initializer loc checked_lv assigned_lv =
    assert generate;
    let kf = Extlib.the self#current_kf in
    let stmt = Extlib.the self#current_stmt in
    let may_safely_ignore = function
      | Var vi, NoOffset -> vi.vglob || vi.vformal
      | _ -> false
    in
(*    Options.feedback "%a? %a (%b && %b)" 
      Printer.pp_lval assigned_lv 
      Printer.pp_lval checked_lv
      (not (may_safely_ignore assigned_lv))
      (Pre_analysis.must_model_lval ~kf ~stmt checked_lv);*)
    if not (may_safely_ignore assigned_lv) && 
      Mmodel_analysis.must_model_lval ~kf ~stmt checked_lv
    then
      let new_stmt =
        Misc.mk_debug_mmodel_stmt (Misc.mk_initialize loc assigned_lv) 
      in
      let before = Cil.memo_stmt self#behavior stmt in
      function_env := Env.add_stmt ~before !function_env new_stmt

  method !vinst = function
  | Set(old_lv, _, _) ->
    if generate then
      Cil.DoChildrenPost 
        (function 
        | [ Set(new_lv, _, loc) ] as l -> 
          self#add_initializer loc old_lv new_lv; 
          l
        | _ -> assert false)
    else
      Cil.DoChildren
  | Call(Some old_ret, _, _, _) ->
    if not generate || Misc.is_generated_kf (Extlib.the self#current_kf) then
      Cil.DoChildren
    else
      Cil.DoChildrenPost 
        (function 
        | [ Call(Some new_ret, _, _, loc) ] as l -> 
          self#add_initializer loc old_ret new_ret;
          l
        | _ -> assert false)
  | _ ->
    Cil.DoChildren

  method !vblock blk =
    let handle_memory new_blk = 
      match new_blk.blocals with
      | [] -> new_blk
      | _ :: _ ->
        let kf = Extlib.the self#current_kf in
        let add_locals stmts =
          List.fold_left
            (fun acc vi ->
              if Mmodel_analysis.old_must_model_vi self#behavior ~kf vi then
                Misc.mk_delete_stmt vi :: acc
              else
                acc)
            stmts
            new_blk.blocals
        in
        let rec insert_in_innermost_last_block blk = function
          | { skind = Return _ } as ret :: ((potential_clean :: tl) as l) ->
            (* keep the return (enclosed in a generated block) at the end;
               preceded by clean if any *)
            let init, tl = 
              if self#is_main kf && Mmodel_analysis.use_model () then
                [ potential_clean; ret ], tl
              else
                [ ret ], l
            in
            blk.bstmts <-
              List.fold_left (fun acc v -> v :: acc) (add_locals init) tl
          | { skind = Block b } :: _ -> 
            insert_in_innermost_last_block b (List.rev b.bstmts)
          | l -> blk.bstmts <- 
            List.fold_left (fun acc v -> v :: acc) (add_locals []) l
        in
        insert_in_innermost_last_block new_blk (List.rev new_blk.bstmts);
        new_blk.bstmts <-
          List.fold_left
          (fun acc vi -> 
            if Mmodel_analysis.must_model_vi vi then
              let vi = Cil.get_varinfo self#behavior vi in
              Misc.mk_store_stmt vi :: acc
            else acc)
          new_blk.bstmts
          blk.blocals;
        new_blk
    in
    if generate then Cil.DoChildrenPost handle_memory else Cil.DoChildren

  (* Processing expressions for the purpose of replacing literal strings found
   in the code with variables generated by E-ACSL. *)
  method !vexpr _ =
    if generate then begin
      match is_initializer with
      (* Do not touch global initializers because they accept only constants *)
      | true -> Cil.DoChildren
      (* Replace literal strings elsewhere *)
      | false -> Cil.DoChildrenPost
        (fun e ->
          let e, env = self#literal_string !function_env e in
          function_env := env;
          e)
    end else
      Cil.SkipChildren

  method !vterm _ =
    Cil.DoChildrenPost
      (fun t ->
        (match t.term_node with
        | Tat(_, StmtLabel s_ref) ->
          (* the label may have been moved,
             so move the corresponding reference *)
          s_ref := E_acsl_label.new_labeled_stmt !s_ref
        | _ -> ());
        t)

  method !vpredicate _ =
    Cil.DoChildrenPost
      (function
      | Pat(_, StmtLabel s_ref) as p ->
          (* the label may have been moved,
             so move the corresponding reference *)
        s_ref := E_acsl_label.new_labeled_stmt !s_ref;
        p
      | p -> p);

  initializer
    Misc.reset ();
    Literal_strings.reset ();
    self#reset_env ();
    Translate.set_original_project (Project.current ())
end

let do_visit ?(prj=Project.current ()) generate =
  Options.feedback ~level:2 "%s annotations in %a." 
    (if generate then "translating" else "checking")
    Project.pretty prj;
  let vis =
    Extlib.try_finally ~finally:Typing.clear (new e_acsl_visitor prj) generate
  in
  (* explicit type annotation in order to check that no new method is
     introduced by error *)
  (vis : Visitor.frama_c_visitor)

(*
Local Variables:
compile-command: "make"
End:
*)