Newer
Older
1
2
3
4
5
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
(**************************************************************************)
(* *)
(* This file is part of Frama-C. *)
(* *)
(* Copyright (C) 2007-2021 *)
(* 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_types
module Orig_project =
State_builder.Option_ref(Project.Datatype)(
struct
let name = "Ast_diff.OrigProject"
let dependencies = []
end)
type 'a correspondance =
[ `Same of 'a (** symbol with identical definition has been found. *)
| `Not_present (** no correspondance *)
]
module Correspondance =
Datatype.Polymorphic(
struct
type 'a t = 'a correspondance
let name a = Type.name a ^ " correspondance"
let module_name = "Correspondance"
let structural_descr _ = Structural_descr.t_abstract
let reprs x = [ `Not_present; `Same x]
let mk_equal eq x y =
match x,y with
| `Same x, `Same y -> eq x y
| `Not_present, `Not_present -> true
| `Same _, `Not_present
| `Not_present, `Same _ -> false
let mk_compare cmp x y =
match x,y with
| `Not_present, `Not_present -> 0
| `Not_present, `Same _ -> -1
| `Same x, `Same y -> cmp x y
| `Same _, `Not_present -> 1
let mk_hash h = function
| `Same x -> 117 * h x
| `Not_present -> 43
let map f = function
| `Same x -> `Same (f x)
| `Not_present -> `Not_present
let mk_internal_pretty_code pp prec fmt = function
| `Not_present -> Format.pp_print_string fmt "`Not_present"
| `Same x ->
let pp fmt = Format.fprintf fmt "`Same %a" (pp Type.Call) x in
Type.par prec Call fmt pp
let mk_pretty pp fmt = function
| `Not_present -> Format.pp_print_string fmt "N/A"
| `Same x -> Format.fprintf fmt " => %a" pp x
let mk_varname v = function
| `Not_present -> "x"
| `Same x -> v x ^ "_c"
let mk_mem_project mem query = function
| `Not_present -> false
| `Same x -> mem query x
end
)
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
(* for kernel function, we are a bit more precise than a yes/no answer.
More precisely, we check whether a function has the same spec,
the same body, and whether its callees have changed (provided
the body itself is identical, otherwise, there's no point in checking
the callees.
*)
type partial_correspondance =
[ `Spec_changed (* body and callees haven't changed *)
| `Body_changed (* spec hasn't changed *)
| `Callees_changed (* spec and body haven't changed *)
| `Callees_spec_changed (* body hasn't changed *)
]
type body_correspondance =
[ `Body_changed
| `Callees_changed
| `Same_body
]
let (<=>) res (cmp,x,y) = if res <> 0 then res else cmp x y
let compare_pc pc1 pc2 =
match pc1, pc2 with
| `Spec_changed, `Spec_changed -> 0
| `Spec_changed, _ -> -1
| _, `Spec_changed -> 1
| `Body_changed, `Body_changed -> 0
| `Body_changed, _ -> -1
| _, `Body_changed -> 1
| `Callees_changed, `Callees_changed -> -1
| `Callees_changed, _ -> -1
| _, `Callees_changed -> 1
| `Callees_spec_changed, `Callees_spec_changed -> 0
let string_of_pc = function
| `Spec_changed -> "Spec_changed"
| `Body_changed -> "Body_changed"
| `Callees_changed -> "Callees_changed"
| `Callees_spec_changed -> "Callees_spec_changed"
let pretty_pc fmt =
let open Format in
function
| `Spec_changed -> pp_print_string fmt "(spec changed)"
| `Body_changed -> pp_print_string fmt "(body_changed)"
| `Callees_changed -> pp_print_string fmt "(callees changed)"
| `Callees_spec_changed -> pp_print_string fmt "(callees and spec changed)"
type kf_correspondance =
[ kernel_function correspondance
| `Partial of kernel_function * partial_correspondance
]
module Kf_correspondance =
Datatype.Make(
struct
let name = "Kf_correspondance"
module C = Correspondance.Make(Kernel_function)
include Datatype.Serializable_undefined
type t = kf_correspondance
let reprs =
let kf = List.hd Kernel_function.reprs in
`Partial (kf,`Spec_changed)
:: (C.reprs :> t list)
let compare x y =
match x,y with
| `Partial (kf1,flags1), `Partial(kf2,flags2) ->
Kernel_function.compare kf1 kf2 <=> (compare_pc,flags1,flags2)
| `Partial _, _ -> 1
| _, `Partial _ -> -1
| (#correspondance as x), (#correspondance as y) -> C.compare x y
let equal = Datatype.from_compare
let hash = function
| `Partial(kf,_) -> 57 * (Kernel_function.hash kf)
| #correspondance as x -> C.hash x
let internal_pretty_code p fmt = function
| `Partial (kf,flags) ->
Format.fprintf fmt
"`Partial (%a,%s)"
(Kernel_function.internal_pretty_code Type.Call) kf
(string_of_pc flags)
in
Type.par p Call fmt pp
| #correspondance as x -> C.internal_pretty_code p fmt x
let pretty fmt = function
| `Partial(kf,flags) ->
Format.fprintf fmt "-> %a %a"
Kernel_function.pretty kf
pretty_pc flags
| #correspondance as x -> C.pretty fmt x
end)
module Info(I: sig val name: string end) =
(struct
let name = "Ast_diff." ^ I.name
let dependencies = [Ast.self; Orig_project.self ]
let size = 43
end)
Virgile Prevosto
committed
(* Map of symbols under analysis, in case of recursion.
Note that this can only happen with aggregate types, logic
types, and function (both C and ACSL). Other symbols must be
correctly ordered in a well-formed AST
*)
type is_same_env =
{
compinfo: compinfo Cil_datatype.Compinfo.Map.t;
kernel_function: kernel_function Kernel_function.Map.t;
logic_info: logic_info Cil_datatype.Logic_info.Map.t;
logic_type_info: logic_type_info Cil_datatype.Logic_type_info.Map.t;
}
module Build(H:Datatype.S_with_collections)(D:Datatype.S) =
State_builder.Hashtbl(H.Hashtbl)(D)
(Info(struct let name = "Ast_diff." ^ D.name end))
Virgile Prevosto
committed
module Build_correspondance(H:Datatype.S_with_collections) =
Build(H)(Correspondance.Make(H))
Virgile Prevosto
committed
module Varinfo = Build_correspondance(Cil_datatype.Varinfo)
Virgile Prevosto
committed
module Compinfo = Build_correspondance(Cil_datatype.Compinfo)
Virgile Prevosto
committed
module Enuminfo = Build_correspondance(Cil_datatype.Enuminfo)
Virgile Prevosto
committed
module Enumitem = Build_correspondance(Cil_datatype.Enumitem)
Virgile Prevosto
committed
module Typeinfo = Build_correspondance(Cil_datatype.Typeinfo)
Virgile Prevosto
committed
module Stmt = Build_correspondance(Cil_datatype.Stmt)
Virgile Prevosto
committed
module Logic_info = Build_correspondance(Cil_datatype.Logic_info)
Virgile Prevosto
committed
module Logic_type_info = Build_correspondance(Cil_datatype.Logic_type_info)
Virgile Prevosto
committed
module Fieldinfo = Build_correspondance(Cil_datatype.Fieldinfo)
Virgile Prevosto
committed
module Model_info = Build_correspondance(Cil_datatype.Model_info)
module Logic_var = Build_correspondance(Cil_datatype.Logic_var)
module Kf = Kernel_function
module Kernel_function = Build(Kernel_function)(Kf_correspondance)
module Fundec = Build_correspondance(Cil_datatype.Fundec)
let empty_env =
{ compinfo = Cil_datatype.Compinfo.Map.empty;
kernel_function = Kf.Map.empty;
logic_info = Cil_datatype.Logic_info.Map.empty;
logic_type_info = Cil_datatype.Logic_type_info.Map.empty;
}
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
(** TODO: use location info to detect potential renaming.
Requires some information about syntactic diff. *)
let find_candidate_type ?loc:_loc ti =
if Globals.Types.mem_type Logic_typing.Typedef ti.tname then begin
match Globals.Types.global Logic_typing.Typedef ti.tname with
| GType(ti,_) -> Some ti
| g ->
Kernel.fatal
"Expected typeinfo instead of %a" Cil_datatype.Global.pretty g
end else None
let find_candidate_compinfo ?loc:_loc ci =
let su = if ci.cstruct then Logic_typing.Struct else Logic_typing.Union in
if Globals.Types.mem_type su ci.cname then begin
match Globals.Types.global su ci.cname with
| GCompTag (ci,_) | GCompTagDecl(ci,_) -> Some ci
| g ->
Kernel.fatal
"Expected aggregate definition instead of %a"
Cil_datatype.Global.pretty g
end else None
let find_candidate_enuminfo ?loc:_loc ei =
if Globals.Types.mem_type Logic_typing.Enum ei.ename then begin
match Globals.Types.global Logic_typing.Enum ei.ename with
| GEnumTag(ei,_) | GEnumTagDecl(ei,_) -> Some ei
| g ->
Kernel.fatal
"Expected enumeration definition instead of %a"
Cil_datatype.Global.pretty g
end else None
let find_candidate_varinfo ?loc:_loc vi where =
try
Some (Globals.Vars.find_from_astinfo vi.vname where)
with Not_found -> None
let find_candidate_func ?loc:_loc kf =
try
Some (Globals.Functions.find_by_name (Kf.get_name kf))
with Not_found -> None
let find_candidate_logic_type ?loc:_loc ti =
try
Some (Logic_env.find_logic_type ti.lt_name)
with Not_found -> None
Virgile Prevosto
committed
let is_same_opt f o o' env =
match o, o' with
| None, None -> true
Virgile Prevosto
committed
| Some v, Some v' -> f v v' env
| _ -> false
Virgile Prevosto
committed
let rec is_same_list f l l' env =
match l, l' with
| [], [] -> true
Virgile Prevosto
committed
| h::t, h'::t' ->
f h h' env && is_same_list f t t' env
| _ -> false
Virgile Prevosto
committed
let rec is_same_type t t' env =
match t, t' with
| (TVoid _ | TInt _ | TFloat _ | TBuiltin_va_list _), _ ->
Cil_datatype.TypByName.equal t t'
| TPtr(t,a), TPtr(t',a') ->
is_same_type t t' env && Cil_datatype.Attributes.equal a a'
| TArray(t,s,a), TArray(t',s',a') ->
is_same_type t t' env &&
is_same_opt is_same_exp s s' env &&
Virgile Prevosto
committed
Cil_datatype.Attributes.equal a a'
| TFun(rt,l,var,a), TFun(rt', l', var', a') ->
is_same_type rt rt' env &&
is_same_opt (is_same_list is_same_formal) l l' env &&
(var = var') &&
Cil_datatype.Attributes.equal a a'
| TNamed(t,a), TNamed(t',a') ->
let correspondance = typeinfo_correspondance t env in
(match correspondance with
| `Not_present -> false
| `Same t'' -> Cil_datatype.Typeinfo.equal t' t'') &&
Cil_datatype.Attributes.equal a a'
| TComp(c,a), TComp(c', a') ->
let correspondance = compinfo_correspondance c env in
(match correspondance with
| `Not_present -> false
| `Same c'' -> Cil_datatype.Compinfo.equal c' c'') &&
Cil_datatype.Attributes.equal a a'
| TEnum(e,a), TEnum(e',a') ->
let correspondance = enuminfo_correspondance e env in
(match correspondance with
| `Not_present -> false
| `Same e'' -> Cil_datatype.Enuminfo.equal e' e'') &&
Cil_datatype.Attributes.equal a a'
| (TPtr _ | TArray _ | TFun _ | TNamed _ | TComp _ | TEnum _), _ -> false
and is_same_compinfo _ci _ci' _env = false
and is_same_enuminfo _ei _ei' _env = false
Virgile Prevosto
committed
and is_same_formal (_,t,a) (_,t',a') env =
is_same_type t t' env && Cil_datatype.Attributes.equal a a'
Virgile Prevosto
committed
and is_same_compound_init (o,i) (o',i') env =
is_same_offset o o' env && is_same_init i i' env
Virgile Prevosto
committed
and is_same_init i i' env =
match i, i' with
Virgile Prevosto
committed
| SingleInit e, SingleInit e' -> is_same_exp e e' env
| CompoundInit (t,l), CompoundInit (t', l') ->
is_same_type t t' env &&
(is_same_list is_same_compound_init) l l' env
| (SingleInit _ | CompoundInit _), _ -> false
and is_same_initinfo i i' env = is_same_opt is_same_init i.init i'.init env
and is_same_exp _e _e' _env = false
and is_same_offset _o _o' _env = false
and is_same_funspec _s _s' _env = false
and is_same_fundec _f _f' _env: body_correspondance = `Body_changed
and is_same_logic_type _t _t' _env = false
and is_same_logic_param _lv _lv' _env = false
and is_same_logic_info _li _li' _env = false
and is_same_logic_type_info _ti _ti' _env = false
and is_same_model_info _mi _mi' _env = false
(* because of overloading, we have to check for a corresponding profile,
leading to potentially recursive calls to is_same_* functions. *)
and find_candidate_logic_info ?loc:_loc li env =
let candidates = Logic_env.find_all_logic_functions li.l_var_info.lv_name in
let find_one li' =
is_same_list is_same_logic_param li.l_profile li'.l_profile env
in
let rec extract l =
match l with
| [] -> None
| li' :: tl -> if find_one li' then Some li' else extract tl
in
match extract candidates with
| None -> None
| Some li' ->
let res = is_same_opt is_same_logic_type li.l_type li'.l_type env in
if res then Some li' else None
and find_candidate_model_info ?loc:_loc mi env =
let candidates = Logic_env.find_all_model_fields mi.mi_name in
let find_one mi' =
is_same_type mi.mi_base_type mi'.mi_base_type env
in
let rec aux = function
| [] -> None
| mi' :: tl -> if find_one mi' then Some mi' else aux tl
in
aux candidates
Virgile Prevosto
committed
and typeinfo_correspondance ?loc ti env =
let add ti =
match find_candidate_type ?loc ti with
| None -> `Not_present
| Some ti' ->
let res = is_same_type ti.ttype ti'.ttype env in
if res then `Same ti' else `Not_present
Typeinfo.memo add ti
Virgile Prevosto
committed
and compinfo_correspondance ?loc ci env =
let add ci =
match find_candidate_compinfo ?loc ci with
| None -> `Not_present
| Some ci' ->
let env' =
{env with compinfo = Cil_datatype.Compinfo.Map.add ci ci' env.compinfo}
in
let res = is_same_compinfo ci ci' env' in
if res then `Same ci' else `Not_present
Virgile Prevosto
committed
match Cil_datatype.Compinfo.Map.find_opt ci env.compinfo with
| Some ci' -> `Same ci'
| None -> Compinfo.memo add ci
Virgile Prevosto
committed
and enuminfo_correspondance ?loc ei env =
let add ei =
match find_candidate_enuminfo ?loc ei with
| None -> `Not_present
| Some ei' ->
if is_same_enuminfo ei ei' env then `Same ei' else `Not_present
Enuminfo.memo add ei
and gfun_correspondance ?loc vi env =
let selection =
State_selection.(
union
(with_dependencies Kernel_function.self)
(union
(with_dependencies Annotations.funspec_state)
(with_dependencies Annotations.code_annot_state)))
in
let kf =
Project.on ~selection (Orig_project.get()) Globals.Functions.get vi
in
let add kf =
match find_candidate_func ?loc kf with
| None -> `Not_present
let env' =
{ env with kernel_function = Kf.Map.add kf kf' env.kernel_function }
in
let same_spec = is_same_funspec kf.spec kf'.spec env' in
let same_body =
match (Kf.has_definition kf, Kf.has_definition kf') with
| false, false -> `Same_body
| false, true | true, false -> `Body_changed
is_same_fundec (Kf.get_definition kf) (Kf.get_definition kf') env'
in
match same_spec, same_body with
| false, `Body_changed -> `Not_present
| false, `Callees_changed -> `Partial(kf',`Callees_spec_changed)
| false, `Same_body -> `Partial(kf', `Spec_changed)
| true, `Same_body -> `Same kf'
| true, ((`Body_changed|`Callees_changed) as c) -> `Partial(kf', c)
in
match Kf.Map.find_opt kf env.kernel_function with
| Some kf' -> `Same kf'
| None -> Kernel_function.memo add kf
and logic_info_correspondance ?loc li env =
let add li =
match find_candidate_logic_info ?loc li env with
| None -> `Not_present
| Some li' ->
let env =
{ env with
logic_info = Cil_datatype.Logic_info.Map.add li li' env.logic_info }
in
let res = is_same_logic_info li li' env in
if res then `Same li' else `Not_present
in
match Cil_datatype.Logic_info.Map.find_opt li env.logic_info with
| Some li' -> `Same li'
| None -> Logic_info.memo add li
and logic_type_correspondance ?loc ti env =
let add ti =
match find_candidate_logic_type ?loc ti with
| None -> `Not_present
| Some ti' ->
let env =
{ env with
logic_type_info =
Cil_datatype.Logic_type_info.Map.add ti ti' env.logic_type_info }
in
let res = is_same_logic_type_info ti ti' env in
if res then `Same ti' else `Not_present
in
match Cil_datatype.Logic_type_info.Map.find_opt ti env.logic_type_info with
| Some ti' -> `Same ti'
| None -> Logic_type_info.memo add ti
let gvar_correspondance ?loc vi =
let add vi =
match find_candidate_varinfo ?loc vi Cil_types.VGlobal with
| None -> `Not_present
| Some vi' ->
let selection = State_selection.singleton Globals.Vars.self in
let init =
Project.on ~selection (Orig_project.get()) Globals.Vars.find vi
in
let init' = Globals.Vars.find vi' in
let res = is_same_initinfo init init' empty_env in
Virgile Prevosto
committed
if res then `Same vi' else `Not_present
in
Varinfo.memo add vi
let model_info_correspondance ?loc mi =
let add mi =
match find_candidate_model_info ?loc mi empty_env with
| None -> `Not_present
| Some mi' ->
let res = is_same_model_info mi mi' empty_env in
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
if res then `Same mi' else `Not_present
in
Model_info.memo add mi
let rec gannot_correspondance =
function
| Dfun_or_pred (li,loc) ->
ignore (logic_info_correspondance ~loc li empty_env)
| Dvolatile _ -> ()
(* reading and writing function themselves will be checked elsewhere. *)
| Daxiomatic(_,l,_,_) ->
List.iter gannot_correspondance l
| Dtype (ti,loc) -> ignore (logic_type_correspondance ~loc ti empty_env)
| Dlemma _ -> ()
(* TODO: we currently do not have any appropriate structure for
storing information about lemmas. *)
| Dinvariant(li, loc) ->
ignore (logic_info_correspondance ~loc li empty_env)
| Dtype_annot(li,loc) ->
ignore (logic_info_correspondance ~loc li empty_env)
| Dmodel_annot (mi,loc) ->
ignore (model_info_correspondance ~loc mi)
| Dextended _ -> ()
(* TODO: provide mechanism for extension themselves
to give relevant information. *)
let global_correspondance g =
match g with
Virgile Prevosto
committed
| GType (ti,loc) -> ignore (typeinfo_correspondance ~loc ti empty_env)
| GCompTag(ci,loc) | GCompTagDecl(ci,loc) ->
Virgile Prevosto
committed
ignore (compinfo_correspondance ~loc ci empty_env)
| GEnumTag(ei,loc) | GEnumTagDecl(ei,loc) ->
Virgile Prevosto
committed
ignore (enuminfo_correspondance ~loc ei empty_env)
| GVar(vi,_,loc) | GVarDecl(vi,loc) ->
ignore (gvar_correspondance ~loc vi)
| GFunDecl(_,vi,loc) -> ignore (gfun_correspondance ~loc vi empty_env)
| GFun(f,loc) -> ignore (gfun_correspondance ~loc f.svar empty_env)
| GAnnot (g,_) -> gannot_correspondance g
| GAsm _ | GPragma _ | GText _ -> ()
let compare_ast prj =
Orig_project.set prj;
let ast = Project.on prj Ast.get () in
Cil.iterGlobals ast global_correspondance