Newer
Older
(**************************************************************************)
(* *)
(* This file is part of Frama-C. *)
(* *)
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
(* 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 licenses/LGPLv2.1). *)
(* *)
(**************************************************************************)
open Cil
open Cil_types
(* all exceptions that can be raised somewhere in the AST.
Used to handle function pointers without exn specification
*)
module All_exn =
State_builder.Option_ref(Cil_datatype.Typ.Set)
(struct let name = "Exn_flow.All_exn" let dependencies = [Ast.self] end)
module Exns =
State_builder.Hashtbl(Kernel_function.Hashtbl)(Cil_datatype.Typ.Set)
(struct
let name = "Exn_flow.Exns"
let dependencies = [Ast.self; All_exn.self]
let size = 47
end)
module ExnsStmt =
State_builder.Hashtbl(Cil_datatype.Stmt.Hashtbl)(Cil_datatype.Typ.Set)
(struct
let name = "Exn_flow.ExnsStmt"
let dependencies = [Ast.self; All_exn.self]
let size = 53
end)
let self_fun = Exns.self
let self_stmt = ExnsStmt.self
let purify t =
let t = Cil.unrollTypeDeep t in
Cil.type_remove_qualifier_attributes_deep t
class all_exn =
object
inherit Visitor.frama_c_inplace
val mutable all_exn = Cil_datatype.Typ.Set.empty
method get_exn = all_exn
method! vstmt_aux s =
match s.skind with
| Throw (Some (_,t),_) ->
all_exn <- Cil_datatype.Typ.Set.add (purify t) all_exn;
SkipChildren
| _ -> DoChildren
end
let compute_all_exn () =
let vis = new all_exn in
Visitor.visitFramacFileSameGlobals (vis:>Visitor.frama_c_visitor) (Ast.get());
vis#get_exn
let all_exn () = All_exn.memo compute_all_exn
let add_exn_var exns v =
let t = Cil.unrollTypeDeep v.vtype in
let t = Cil.type_remove_qualifier_attributes t in
Cil_datatype.Typ.Set.add t exns
let add_exn_clause exns (v,_) = add_exn_var exns v
(* We're not really interested by intra-procedural Dataflow here: all the
interesting stuff happens at inter-procedural level (except for Throw
encapsulated directly in a TryCatch, but even then it is easily captured
at syntactical level). Therefore, we can as well use a syntactic pass
at intra-procedural level
*)
class exn_visit =
object (self)
inherit Visitor.frama_c_inplace
val stack = Stack.create ()
val possible_exn = Stack.create ()
(* current set of exn included in a catch-all clause. Used to
handle Throw None;
*)
val current_exn = Stack.create ()
method private recursive_call kf =
try
Stack.iter
(fun (kf',_) -> if Kernel_function.equal kf kf' then raise Exit) stack;
false
with Exit -> true
method private add_exn t =
let current_uncaught = Stack.top possible_exn in
current_uncaught:= Cil_datatype.Typ.Set.add t !current_uncaught
method private union_exn s =
let current_uncaught = Stack.top possible_exn in
current_uncaught := Cil_datatype.Typ.Set.union s !current_uncaught
method! vstmt_aux s =
match s.skind with
| Throw (None,_) ->
let my_exn = Stack.top current_exn in
self#union_exn my_exn; ExnsStmt.replace s my_exn; SkipChildren
| Throw(Some (_,t),_) ->
let t = Cil.unrollTypeDeep t in
let t = Cil.type_remove_qualifier_attributes t in
self#add_exn t;
ExnsStmt.replace s (Cil_datatype.Typ.Set.singleton t);
SkipChildren
| TryCatch (t,c,_) ->
let catch, catch_all =
List.fold_left
(fun (catch, catch_all) ->
function
| (Catch_all,_) -> catch, true
| (Catch_exn(v,[]),_) ->
let catch = add_exn_var catch v in
catch, catch_all
| (Catch_exn(_,aux), _) ->
let catch = List.fold_left add_exn_clause catch aux in
catch, catch_all)
(Cil_datatype.Typ.Set.empty,false) c
in
Stack.push (ref Cil_datatype.Typ.Set.empty) possible_exn;
ignore (Visitor.visitFramacBlock (self:>Visitor.frama_c_inplace) t);
let my_exn = Stack.pop possible_exn in
let uncaught = Cil_datatype.Typ.Set.diff !my_exn catch in
(* uncaught exceptions are lift to previous set of exn, but
only if there's no catch-all clause. *)
Stack.push (ref Cil_datatype.Typ.Set.empty) possible_exn;
if not catch_all then self#union_exn uncaught;
List.iter
(fun (v,b) ->
(match v with
| Catch_all ->
(* catch_all will catch all exceptions that are not
already handled by a previous clause. It must be the
last handler (C++11, 15.3§5), thus there's no handler
ordering issue
*)
Stack.push uncaught current_exn
| Catch_exn (v,[]) ->
let catch = add_exn_var Cil_datatype.Typ.Set.empty v in
Stack.push catch current_exn
| Catch_exn (_,aux) ->
let catch =
List.fold_left
add_exn_clause Cil_datatype.Typ.Set.empty aux
in
Stack.push catch current_exn);
ignore
(Visitor.visitFramacBlock (self:>Visitor.frama_c_inplace) b);
ignore (Stack.pop current_exn))
c;
let my_exn = !(Stack.pop possible_exn) in
ExnsStmt.replace s my_exn;
self#union_exn my_exn;
SkipChildren
| If _ | Switch _ | Loop _ | Block _ | UnspecifiedSequence _
| TryFinally _ | TryExcept _
| Instr _ -> (* must take into account exceptions thrown by a fun call*)
Stack.push (ref Cil_datatype.Typ.Set.empty) possible_exn;
DoChildrenPost
(fun s ->
let my_exn = !(Stack.pop possible_exn) in
ExnsStmt.replace s my_exn;
self#union_exn my_exn;
s)
(* No exception can be thrown here. *)
| Return _ | Goto _ | Break _ | Continue _ ->
ExnsStmt.replace s Cil_datatype.Typ.Set.empty;
SkipChildren
method! vinst =
function
| Call(_,{ enode = Lval(Var f,NoOffset) },_,_)
| Local_init(_, ConsInit(f, _, _), _) ->
let kf = Globals.Functions.get f in
if self#recursive_call kf then begin
let module Found =
struct
exception F of Cil_datatype.Typ.Set.t
end
in
let computed_exn =
try
Stack.iter
(fun (kf', exns) ->
if Kernel_function.equal kf kf' then raise (Found.F !exns))
stack;
Kernel.fatal "No cycle found!"
with Found.F exns -> exns
in
let known_exn =
try Exns.find kf with Not_found -> Cil_datatype.Typ.Set.empty
in
if Cil_datatype.Typ.Set.subset computed_exn known_exn then begin
(* Fixpoint found, no need to recurse. *)
self#union_exn known_exn
end else begin
(* add known exns in table and recurse. Termination is ensured
by the fact that only a finite number of exceptions
can be thrown. *)
let kf_exn = Cil_datatype.Typ.Set.union computed_exn known_exn in
Exns.replace kf kf_exn;
ignore
(Visitor.visitFramacFunction
(self:>Visitor.frama_c_visitor)
(Kernel_function.get_definition kf));
let callee_exn = Exns.find kf in
self#union_exn callee_exn
end
end else if Exns.mem kf then begin
self#union_exn (Exns.find kf)
end else if Kernel_function.is_definition kf then begin
let def = Kernel_function.get_definition kf in
ignore
(Visitor.visitFramacFunction (self:>Visitor.frama_c_visitor) def);
let callee_exn = Exns.find kf in
self#union_exn callee_exn
end else begin (* TODO: introduce extension to declare
exceptions that can be thrown by prototypes. *)
Kernel.warning
"Assuming declared function %a can't throw any exception"
Kernel_function.pretty kf
end;
SkipChildren
| Call _ ->
(* Function pointer: we consider that it can throw any possible
exception. *)
self#union_exn (all_exn()); SkipChildren
| _ -> SkipChildren
method! vfunc f =
let my_exns = ref Cil_datatype.Typ.Set.empty in
let kf = Globals.Functions.get f.svar in
Stack.push (kf,my_exns) stack;
Stack.push my_exns possible_exn;
let after_visit f =
let callee_exn = Stack.pop possible_exn in
Exns.add kf !callee_exn;
ignore (Stack.pop stack); f
in
DoChildrenPost after_visit
end
let compute_kf kf =
if Kernel_function.is_definition kf then
ignore
(Visitor.visitFramacFunction (new exn_visit)
(Kernel_function.get_definition kf))
(* just ignore prototypes. *)
let compute () = Globals.Functions.iter compute_kf
let get_type_tag t =
let rec aux t =
match t with
| TVoid _ -> "v"
| TInt (IBool,_) -> "B"
| TInt (IChar,_) -> "c"
| TInt (ISChar,_) -> "sc"
| TInt (IUChar,_) -> "uc"
| TInt (IInt,_) -> "i"
| TInt (IUInt,_) -> "ui"
| TInt (IShort,_) -> "s"
| TInt (IUShort,_) -> "us"
| TInt (ILong,_) -> "l"
| TInt (IULong,_) -> "ul"
| TInt (ILongLong,_) -> "ll"
| TInt (IULongLong,_) -> "ull"
| TFloat(FFloat,_) -> "f"
| TFloat(FDouble,_) -> "d"
| TFloat (FLongDouble,_) -> "ld"
| TPtr(t,_) -> "p" ^ aux t
| TArray(t,_,_,_) -> "a" ^ aux t
| TFun(rt,l,_,_) ->
let base = "fun" ^ aux rt in
(match l with
| None -> base
| Some l ->
List.fold_left (fun acc (_,t,_) -> acc ^ aux t) base l)
| TNamed _ -> Kernel.fatal "named type not correctly unrolled"
| TComp (s,_,_) -> (if s.cstruct then "S" else "U") ^ s.cname
| TEnum (e,_) -> "E" ^ e.ename
| TBuiltin_va_list _ -> "va"
in "__fc_" ^ aux t
let get_type_enum t = "__fc_exn_kind_" ^ (get_type_tag t)
let get_kf_exn kf =
if not (Exns.is_computed()) then compute();
Exns.find kf
let exn_uncaught_name = "exn_uncaught"
let exn_kind_name = "exn_kind"
let exn_obj_name = "exn_obj"
(* enumeration for all possible exceptions *)
let generate_exn_enum exns =
let loc = Cil_datatype.Location.unknown in
let v = ref 0 in
let info =
{ eorig_name = "__fc_exn_enum";
ename = "__fc_exn_enum";
eitems = [];
eattr = [];
ereferenced = true; (* not generated if no exn can be thrown *)
ekind = IInt; (* Take into account -enum option? *)
}
in
let create_enum_item t acc =
let ve = Cil.kinteger ~loc IInt !v in
let name = get_type_enum t in
incr v;
{ eiorig_name = name;
einame = name;
eival = ve;
eihost = info;
eiloc = loc;
} :: acc
in
let enums = Cil_datatype.Typ.Set.fold create_enum_item exns [] in
info.eitems <- enums;
info
(* discriminated union (i.e. struct + union) for all possible exceptions. *)
let generate_exn_union e exns =
let loc = Cil_datatype.Location.unknown in
let create_union_fields _ =
let add_one_field t acc = (get_type_tag t, t, None, [], loc) :: acc in
Cil_datatype.Typ.Set.fold add_one_field exns []
in
let union_name = "__fc_exn_union" in
let exn_kind_union =

Allan Blanchard
committed
Cil_const.mkCompInfo
false union_name ~norig:union_name create_union_fields []
in
let create_struct_fields _ =
let uncaught = (exn_uncaught_name, Cil.intType, None, [], loc) in
let kind = (exn_kind_name, TEnum (e,[]), None, [], loc) in
let obj =
(exn_obj_name,
TComp(exn_kind_union, { scache = Not_Computed } , []), None, [], loc)
in
[uncaught; kind; obj]
in
let struct_name = "__fc_exn_struct" in
let exn_struct =

Allan Blanchard
committed
Cil_const.mkCompInfo
true struct_name ~norig:struct_name create_struct_fields []
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
in
exn_kind_union, exn_struct
let add_types_and_globals typs globs f =
let iter_globs (acc,added) g =
match g with
| GVarDecl _ | GVar _ | GFun _ as g when not added ->
(g :: List.rev_append globs (List.rev_append typs acc), true)
| _ -> g :: acc, added
in
let globs, added = List.fold_left iter_globs ([],false) f.globals in
let globs =
if added then List.rev globs
else List.rev_append globs (List.rev_append typs globs)
in
f.globals <- globs;
f
let make_init_assign loc v init =
let rec aux lv acc = function
| SingleInit e -> Cil.mkStmtOneInstr (Set(lv,e,loc)) :: acc
| CompoundInit(_,l) ->
let treat_one_offset acc (o,i) = aux (Cil.addOffsetLval o lv) acc i in
List.fold_left treat_one_offset acc l
in
List.rev (aux (Var v, NoOffset) [] init)
let find_exns_func v =
try Exns.find (Globals.Functions.get v)
with Not_found -> Cil_datatype.Typ.Set.empty
let find_exns e =
match e.enode with
| Lval(Var v, NoOffset) -> find_exns_func v
| _ -> all_exn ()
class erase_exn =
object(self)
inherit Visitor.frama_c_inplace
(* reverse before filling. *)
val mutable new_types = []
val exn_enum = Cil_datatype.Typ.Hashtbl.create 7
val exn_union = Cil_datatype.Typ.Hashtbl.create 7
val mutable modified_funcs = Cil_datatype.Fundec.Set.empty
val mutable exn_struct = None
val mutable exn_var = None
val mutable can_throw = false
val mutable fct_can_throw = false
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
val mutable catched_var = None
val mutable label_counter = 0
val exn_labels = Cil_datatype.Typ.Hashtbl.create 7
val catch_all_label = Stack.create ()
method modified_funcs = modified_funcs
method private update_enum_bindings enum exns =
let update_one_binding t =
let s = get_type_enum t in
let ei = List.find (fun ei -> ei.einame = s) enum.eitems in
Cil_datatype.Typ.Hashtbl.add exn_enum t ei
in
Cil_datatype.Typ.Set.iter update_one_binding exns
method private update_union_bindings union exns =
let update_one_binding t =
let s = get_type_tag t in
Kernel.debug ~dkey:Kernel.dkey_exn_flow
"Registering %a as possible exn type" Cil_datatype.Typ.pretty t;
let fi = List.find (fun fi -> fi.fname = s) union.cfields in
Cil_datatype.Typ.Hashtbl.add exn_union t fi
in
Cil_datatype.Typ.Set.iter update_one_binding exns
method private exn_kind t = Cil_datatype.Typ.Hashtbl.find exn_enum t
method private is_thrown t = Cil_datatype.Typ.Hashtbl.mem exn_enum t
method private exn_field_off name =
List.find (fun fi -> fi.fname = name) (Extlib.the exn_struct).cfields
method private exn_field name =
Var (Extlib.the exn_var), Field(self#exn_field_off name, NoOffset)
method private exn_field_term name =
TVar(Cil.cvar_to_lvar (Extlib.the exn_var)),
TField(self#exn_field_off name, TNoOffset)
method private exn_obj_field = self#exn_field exn_obj_name
method private exn_obj_field_term = self#exn_field_term exn_obj_name
method private exn_kind_field = self#exn_field exn_kind_name
method private exn_kind_field_term = self#exn_field_term exn_kind_name
method private uncaught_flag_field = self#exn_field exn_uncaught_name
method private uncaught_flag_field_term =
self#exn_field_term exn_uncaught_name
method private exn_obj_kind_field t =
Kernel.debug ~dkey:Kernel.dkey_exn_flow
"Searching for %a as possible exn type" Cil_datatype.Typ.pretty t;
Cil_datatype.Typ.Hashtbl.find exn_union t
method private test_uncaught_flag loc b =
let e1 = Cil.new_exp ~loc (Lval self#uncaught_flag_field) in
let e2 = if b then Cil.one ~loc else Cil.zero ~loc in
Cil.new_exp ~loc (BinOp(Eq,e1,e2,Cil.intType))
method private pred_uncaught_flag loc b =
let e1 =
Logic_const.term
~loc (TLval self#uncaught_flag_field_term) Linteger
in
let e2 =
if b then Logic_const.tinteger ~loc 1
else Logic_const.tinteger ~loc 0
in
Logic_const.prel ~loc (Req,e1,e2)
method private set_uncaught_flag loc b =
let e = if b then Cil.one ~loc else Cil.zero ~loc in
Cil.mkStmtOneInstr (Set(self#uncaught_flag_field,e,loc))
method private set_exn_kind loc t =
let e = self#exn_kind (purify t) in
let e = Cil.new_exp ~loc (Const (CEnum e)) in
Cil.mkStmtOneInstr(Set(self#exn_kind_field,e,loc))
method private set_exn_value loc t e =
let lv = self#exn_obj_field in
let union_field = self#exn_obj_kind_field (purify t) in
let lv = Cil.addOffsetLval (Field (union_field, NoOffset)) lv in
Cil.mkStmtOneInstr (Set(lv,e,loc))
method private jumps_to_default_handler loc =
if Stack.is_empty catch_all_label then begin
(* no catch-all clause in the function: just go up in the stack. *)
fct_can_throw <- true;
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
let kf = Extlib.the self#current_kf in
let ret = Kernel_function.find_return kf in
let rtyp = Kernel_function.get_return_type kf in
if ret.labels = [] then
ret.labels <- [Label("__ret_label",Cil_datatype.Stmt.loc ret,false)];
let goto = mkStmt (Goto (ref ret,loc)) in
match ret.skind with
| Return (None,_) -> [goto]
(* rt is void: do not need to create a dummy return value *)
| Return (Some { enode = Lval(Var rv, NoOffset) },_) ->
let init = Cil.makeZeroInit ~loc rtyp in
make_init_assign loc rv init @ [goto]
| Return _ ->
Kernel.fatal "exception removal should be used after oneRet"
| _ ->
Kernel.fatal "find_return did not give a Return statement"
end else begin
let stmt = Stack.top catch_all_label in
[mkStmt (Goto (ref stmt, loc))]
end
method private jumps_to_handler loc t =
let t = purify t in
try
let stmt = Cil_datatype.Typ.Hashtbl.find exn_labels t in
[mkStmt (Goto (ref stmt, loc))]
with
| Not_found -> self#jumps_to_default_handler loc
method! vfile f =
let exns = all_exn () in
if not (Cil_datatype.Typ.Set.is_empty exns) then begin
let loc = Cil_datatype.Location.unknown in
let e = generate_exn_enum exns in
let u,s = generate_exn_union e exns in
let exn =
Cil.makeGlobalVar "__fc_exn" (TComp (s,{scache = Not_Computed},[]))
in
self#update_enum_bindings e exns;
self#update_union_bindings u exns;
exn_struct <- Some s;
can_throw <- true;
new_types <-
GCompTag (s,loc) ::
GCompTag (u,loc) ::
GEnumTag (e,loc) :: new_types;
exn_var <- Some exn;
let exn_init = Cil.makeZeroInit ~loc (TComp(s,{scache=Not_Computed},[]))
in
let gexn_var = GVar(exn, { init = Some exn_init }, loc) in
ChangeDoChildrenPost(
f,add_types_and_globals (List.rev new_types) [gexn_var])
end else (* nothing can be thrown in the first place, but we still have
to get rid of (useless) try/catch blocks if any. *)
DoChildren
method private visit_catch_clause loc (v,b) =
let loc =
match b.bstmts with
| [] -> loc
| [x] -> Cil_datatype.Stmt.loc x
| x::tl ->
fst (Cil_datatype.Stmt.loc x),
snd (Cil_datatype.Stmt.loc (Extlib.last tl))
in
let add_unreachable_block b =
Cil.mkStmt (If(Cil.zero ~loc, b, Cil.mkBlock [], loc))
in
let assign_catched_obj v b =
let exn_obj = self#exn_obj_field in
let kind_field = self#exn_obj_kind_field (purify v.vtype) in
let lv = Cil.addOffsetLval (Field (kind_field,NoOffset)) exn_obj in
let s =
Cil.mkStmtOneInstr
(Set ((Var v, NoOffset), Cil.new_exp ~loc (Lval lv), loc))
in
b.bstmts <- s :: b.bstmts
in
let f = Extlib.the self#current_func in
let update_locals v b =
if not (List.memq v b.blocals) then b.blocals <- v::b.blocals;
if not (List.memq v f.slocals) then f.slocals <- v::f.slocals
in
let b =
(match v with
| Catch_all -> b
| Catch_exn (v,[]) ->
Cil.update_var_type v (purify v.vtype);
update_locals v b;
assign_catched_obj v b; b
| Catch_exn(v,aux) ->
let add_one_aux stmts (v,b) =
Cil.update_var_type v (purify v.vtype);
update_locals v b;
assign_catched_obj v b;
add_unreachable_block b :: stmts
in
b.blocals <- List.filter (fun v' -> v!=v') b.blocals;
let aux_blocks =
List.fold_left add_one_aux [Cil.mkStmt (Block b)] aux
in
let main_block = Cil.mkBlock aux_blocks in
Cil.update_var_type v (purify v.vtype);
update_locals v main_block;
main_block)
in
ignore (Visitor.visitFramacBlock (self :> Visitor.frama_c_visitor) b);
add_unreachable_block b
method! vfunc _ =
label_counter <- 0;
fct_can_throw <- false;
let update_body f =
if fct_can_throw then begin
Virgile Prevosto
committed
Oneret.encapsulate_local_vars f
end;
f
in
DoChildrenPost update_body
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
method private modify_current () =
modified_funcs <-
Cil_datatype.Fundec.Set.add (Extlib.the self#current_func) modified_funcs;
method private aux_handler_goto target (v,b) =
let loc = v.vdecl in
let goto_main_handler = Cil.mkStmt (Goto (ref target,loc)) in
let suf =
if label_counter = 0 then "" else "_" ^ (string_of_int label_counter)
in
let lab = (get_type_tag (purify v.vtype)) ^ suf in
label_counter <- label_counter + 1;
b.bstmts <- b.bstmts @ [goto_main_handler];
(* we have at least the goto statement in the block *)
let s = List.hd b.bstmts in
s.labels <- (Label(lab,loc,false)::s.labels);
Cil_datatype.Typ.Hashtbl.add exn_labels (purify v.vtype) s
method private guard_post_cond (kind,pred as orig) =
match kind with
(* If we exit explicitly with exit,
we haven't seen an uncaught exception anyway. *)
| Exits | Breaks | Continues -> orig
| Returns | Normal ->
let loc = pred.ip_content.pred_loc in
let p = self#pred_uncaught_flag loc false in
let pred' = Logic_const.pred_of_id_pred pred in
(kind,
(Logic_const.new_predicate
(Logic_const.pimplies ~loc (p,pred'))))
method! vbehavior b =
match self#current_kf, self#current_stmt with
| None, None -> SkipChildren
(* Prototype is assumed to not throw any exception. *)
| None, Some _ ->
Kernel.fatal
"Inconsistent visitor state: visiting a statement \
outside of any function."
| Some f, None when not (Kernel_function.is_definition f) ->
(* By hypothesis, prototypes do not throw anything. *)
SkipChildren
| Some f, None -> (* function contract *)
let exns = Exns.find f in
if Cil_datatype.Typ.Set.is_empty exns then SkipChildren
else begin
b.b_post_cond <- List.map self#guard_post_cond b.b_post_cond;
ChangeTo b (* need to register the new clauses. *)
end
| Some _, Some s -> (* statement contract *)
let exns = ExnsStmt.find s in
if Cil_datatype.Typ.Set.is_empty exns then SkipChildren
else begin
b.b_post_cond <- List.map self#guard_post_cond b.b_post_cond;
ChangeTo b
end
method private clean_catch_clause (bind,b as handler) acc =
let remove_local v =

Allan Blanchard
committed
let f =
Visitor_behavior.Get.fundec self#behavior (Extlib.the self#current_func)

Allan Blanchard
committed
in
let v = Visitor_behavior.Get.varinfo self#behavior v in
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
f.slocals <- List.filter (fun v' -> v!=v') f.slocals;
in
match bind with
| Catch_all -> handler :: acc
| Catch_exn (v, []) ->
if self#is_thrown (purify v.vtype) then handler :: acc
else begin remove_local v; acc end
| Catch_exn (v, l) ->
let caught, remove =
List.partition (fun (v,_) -> self#is_thrown (purify v.vtype)) l
in
List.iter (fun (v,_) -> remove_local v) remove;
if caught = [] then begin remove_local v; acc end
else (Catch_exn (v,caught), b) :: acc
method! vstmt_aux s =
let generate_jumps instr exns loc =
if Cil_datatype.Typ.Set.is_empty exns then SkipChildren
else begin
self#modify_current ();
let make_jump t (stmts, uncaught) =
let t = purify t in
if Cil_datatype.Typ.Hashtbl.mem exn_labels t then begin
let e = self#exn_kind t in
let e = Cil.new_exp ~loc (Const (CEnum e)) in
let b = self#jumps_to_handler loc t in
let s = Cil.mkStmt (Block (Cil.mkBlock b)) in
s.labels <- [Case (e,loc)];
s::stmts, uncaught
end else stmts, true
in
let stmts, uncaught =
Cil_datatype.Typ.Set.fold make_jump exns ([],false)
in
let stmts =
if uncaught then begin
let default =
Cil.mkStmt (
Block (Cil.mkBlock (self#jumps_to_default_handler loc)))
in
default.labels <- [Default loc];
List.rev_append stmts [default]
end else List.rev stmts
in
let test = self#test_uncaught_flag loc true in
let cases = Cil.new_exp ~loc (Lval self#exn_kind_field) in
let switch = Cil.mkStmt (Switch(cases,Cil.mkBlock stmts,stmts,loc)) in
let handler =
Cil.mkStmt (If(test,Cil.mkBlock [switch],Cil.mkBlock [],loc))
in
let instr =
Visitor.visitFramacInstr (self:>Visitor.frama_c_visitor) instr
in
let call = Cil.mkStmtOneInstr (List.hd instr) in
s.skind <- Block (Cil.mkBlockNonScoping [call;handler]);
SkipChildren
end
in
match s.skind with
| Instr (Call (_,f,_,loc) as instr) ->
generate_jumps instr (find_exns f) loc
| Instr (Local_init(_, ConsInit(f,_,_), loc) as instr) ->
generate_jumps instr (find_exns_func f) loc
| Throw _ when not can_throw ->
Kernel.fatal "Unexpected Throw statement"
| Throw(Some(e,t),loc) ->
self#modify_current();
let s1 = self#set_uncaught_flag loc true in
let s2 = self#set_exn_kind loc t in
let s3 = self#set_exn_value loc t e in
let rv = self#jumps_to_handler loc t in
let b = mkBlock (s1 :: s2 :: s3 :: rv) in
s.skind <- Block b;
SkipChildren
| Throw (None,loc) ->
self#modify_current ();
let s1 = self#set_uncaught_flag loc true in
let t = purify (Extlib.the exn_var).vtype in
let rv = self#jumps_to_handler loc t in
let b = mkBlock (s1 :: rv) in
s.skind <- Block b;
SkipChildren
| TryCatch (t,_,_) when not can_throw ->
self#modify_current();
(* no exception can be thrown:
we can simply remove the catch clauses. *)
s.skind <- (Block t);
DoChildren (* visit the block for nested try catch. *)
| TryCatch (t,c,loc) ->
self#modify_current();
(* First, remove handlers of exceptions that cannot be thrown. *)
let c = List.fold_right self#clean_catch_clause c [] in
(* Visit the catch clauses first, as they are in the same catch scope
than the current block. As we are adding statements in the
auxiliary blocks, we need to do that before adding labels to the
entry points of these blocks.
*)
let stmts = List.map (self#visit_catch_clause loc) c in
let suf =
if label_counter = 0 then "" else "_" ^ (string_of_int label_counter)
in
label_counter <- label_counter + 1;
(* now generate the labels for jumping to the appropriate block when
catching an exception. *)
List.iter
(function
| (Catch_exn (v,aux),b) ->
(* first thing that we do is to flag the exn as caught *)
let stmt = self#set_uncaught_flag v.vdecl false in
let label = (get_type_tag (purify v.vtype)) ^ suf in
stmt.labels <- [Label (label,v.vdecl,false)];
b.bstmts <- stmt :: b.bstmts;
(match aux with
| [] ->
Cil_datatype.Typ.Hashtbl.add exn_labels (purify v.vtype) stmt
| _ :: _ ->
List.iter (self#aux_handler_goto stmt) aux)
| (Catch_all, b) ->
let loc =
match b.bstmts with [] -> loc | s::_ -> Cil_datatype.Stmt.loc s
in
let stmt = self#set_uncaught_flag loc false in
stmt.labels <- [Label ("catch_all" ^ suf,loc,false)];
b.bstmts <- stmt :: b.bstmts;
Stack.push stmt catch_all_label)
(* We generate the bindings in reverse order, as if two clauses
match the same type, the first one (which is the one that has
to be taken), will be visited last, hiding the binding of the
second in the Hashtbl. *)
(List.rev c);
ignore (Visitor.visitFramacBlock (self:>Visitor.frama_c_visitor) t);
List.iter
(function
| (Catch_exn (v,[]), _) ->
Cil_datatype.Typ.Hashtbl.remove exn_labels (purify v.vtype)
| Catch_exn(_,l), _ ->
List.iter
(fun (v,_) ->
Cil_datatype.Typ.Hashtbl.remove exn_labels (purify v.vtype))
l
| Catch_all,_ -> ignore (Stack.pop catch_all_label))
c; (* we remove bindings in the reverse order as we added them,
though order does not really matter here. *)
t.bstmts <- t.bstmts @ stmts;
s.skind <- Block t;
SkipChildren
| _ -> DoChildren
end
let prepare_file f =
if Kernel.SimplifyCfg.get () then begin
Cfg.prepareCFG ~keepSwitch:false f;
end;
File.must_recompute_cfg f
let remove_exn f =
if Kernel.RemoveExn.get() then begin
Visitor.visitFramacFileSameGlobals (new exn_visit) f;
let vis = new erase_exn in
Visitor.visitFramacFile (vis :> Visitor.frama_c_visitor) f;
let funs = vis#modified_funcs in
if not (Cil_datatype.Fundec.Set.is_empty funs) then Ast.mark_as_changed ();
Cil_datatype.Fundec.Set.iter prepare_file funs
end
let transform_category = File.register_code_transformation_category "remove_exn"
let () =
let deps = [ (module Kernel.RemoveExn: Parameter_sig.S) ] in
File.add_code_transformation_after_cleanup ~deps transform_category remove_exn