2 * Copyright 2010, INRIA, University of Copenhagen
3 * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
4 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
5 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
6 * This file is part of Coccinelle.
8 * Coccinelle is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, according to version 2 of the License.
12 * Coccinelle is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
20 * The authors reserve the right to distribute this or future versions of
21 * Coccinelle under other licenses.
26 * Copyright 2010, INRIA, University of Copenhagen
27 * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
28 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
29 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
30 * This file is part of Coccinelle.
32 * Coccinelle is free software: you can redistribute it and/or modify
33 * it under the terms of the GNU General Public License as published by
34 * the Free Software Foundation, according to version 2 of the License.
36 * Coccinelle is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 * GNU General Public License for more details.
41 * You should have received a copy of the GNU General Public License
42 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
44 * The authors reserve the right to distribute this or future versions of
45 * Coccinelle under other licenses.
49 (* For each rule return the list of variables that are used after it.
50 Also augment various parts of each rule with unitary, inherited, and freshness
53 (* metavar decls should be better integrated into computations of free
54 variables in plus code *)
56 module Ast
= Ast_cocci
57 module V
= Visitor_ast
58 module TC
= Type_cocci
60 let rec nub = function
62 | (x
::xs
) when (List.mem x xs
) -> nub xs
63 | (x
::xs
) -> x
::(nub xs
)
65 (* Collect all variable references in a minirule. For a disj, we collect
66 the maximum number (2 is enough) of references in any branch. *)
68 let collect_unitary_nonunitary free_usage
=
69 let free_usage = List.sort compare
free_usage in
70 let rec loop1 todrop
= function (* skips multiple occurrences *)
72 | (x
::xs
) as all
-> if x
= todrop
then loop1 todrop xs
else all
in
73 let rec loop2 = function
77 if x
= y
(* occurs more than once in free_usage *)
79 let (unitary
,non_unitary
) = loop2(loop1 x xs
) in
80 (unitary
,x
::non_unitary
)
81 else (* occurs only once in free_usage *)
82 let (unitary
,non_unitary
) = loop2 (y
::xs
) in
83 (x
::unitary
,non_unitary
) in
86 let collect_refs include_constraints
=
87 let bind x y
= x
@ y
in
88 let option_default = [] in
90 let donothing recursor k e
= k e
in (* just combine in the normal way *)
92 let donothing_a recursor k e
= (* anything is not wrapped *)
93 k e
in (* just combine in the normal way *)
95 (* the following considers that anything that occurs non-unitarily in one
96 branch occurs nonunitarily in all branches. This is not optimal, but
97 doing better seems to require a breadth-first traversal, which is
98 perhaps better to avoid. Also, unitarily is represented as occuring once,
99 while nonunitarily is represented as twice - more is irrelevant *)
100 (* cases for disjs and metavars *)
101 let bind_disj refs_branches
=
102 let (unitary
,nonunitary
) =
103 List.split
(List.map
collect_unitary_nonunitary refs_branches
) in
104 let unitary = nub (List.concat
unitary) in
105 let nonunitary = nub (List.concat
nonunitary) in
107 List.filter
(function x
-> not
(List.mem x
nonunitary)) unitary in
108 unitary@nonunitary@nonunitary in
110 let metaid (x
,_
,_
,_
) = x
in
112 let astfvident recursor k i
=
114 (match Ast.unwrap i
with
115 Ast.MetaId
(name
,idconstraint
,_
,_
) | Ast.MetaFunc
(name
,idconstraint
,_
,_
)
116 | Ast.MetaLocalFunc
(name
,idconstraint
,_
,_
) ->
118 if include_constraints
120 match idconstraint
with
121 Ast.IdNegIdSet
(_
,metas) -> metas
124 bind (List.rev
metas) [metaid name
]
125 | _
-> option_default) in
127 let rec type_collect res
= function
128 TC.ConstVol
(_
,ty
) | TC.Pointer
(ty
) | TC.FunctionPointer
(ty
)
129 | TC.Array
(ty
) -> type_collect res ty
130 | TC.EnumName
(TC.MV
(tyname
,_
,_
)) ->
132 | TC.StructUnionName
(_
,TC.MV
(tyname
,_
,_
)) ->
134 | TC.MetaType
(tyname
,_
,_
) ->
136 | TC.SignedT
(_
,Some ty
) -> type_collect res ty
139 let astfvexpr recursor k e
=
141 (match Ast.unwrap e
with
142 Ast.MetaExpr
(name
,constraints
,_
,Some type_list
,_
,_
) ->
143 let types = List.fold_left
type_collect option_default type_list
in
145 if include_constraints
147 match constraints
with
148 Ast.SubExpCstrt l
-> l
151 bind extra (bind [metaid name
] types)
152 | Ast.MetaErr
(name
,constraints
,_
,_
)
153 | Ast.MetaExpr
(name
,constraints
,_
,_
,_
,_
) ->
155 if include_constraints
157 match constraints
with
158 Ast.SubExpCstrt l
-> l
161 bind extra [metaid name
]
162 | Ast.MetaExprList
(name
,Ast.MetaListLen
(lenname
,_
,_
),_
,_
) ->
163 [metaid name
;metaid lenname
]
164 | Ast.MetaExprList
(name
,_
,_
,_
) -> [metaid name
]
165 | Ast.DisjExpr
(exps
) -> bind_disj (List.map k exps
)
166 | _
-> option_default) in
168 let astfvdecls recursor k d
=
170 (match Ast.unwrap d
with
171 Ast.MetaDecl
(name
,_
,_
) | Ast.MetaField
(name
,_
,_
) -> [metaid name
]
172 | Ast.DisjDecl
(decls
) -> bind_disj (List.map k decls
)
173 | _
-> option_default) in
175 let astfvfullType recursor k ty
=
177 (match Ast.unwrap ty
with
178 Ast.DisjType
(types) -> bind_disj (List.map k
types)
179 | _
-> option_default) in
181 let astfvtypeC recursor k ty
=
183 (match Ast.unwrap ty
with
184 Ast.MetaType
(name
,_
,_
) -> [metaid name
]
185 | _
-> option_default) in
187 let astfvinit recursor k ty
=
189 (match Ast.unwrap ty
with
190 Ast.MetaInit
(name
,_
,_
) -> [metaid name
]
191 | _
-> option_default) in
193 let astfvparam recursor k p
=
195 (match Ast.unwrap p
with
196 Ast.MetaParam
(name
,_
,_
) -> [metaid name
]
197 | Ast.MetaParamList
(name
,Ast.MetaListLen
(lenname
,_
,_
),_
,_
) ->
198 [metaid name
;metaid lenname
]
199 | Ast.MetaParamList
(name
,_
,_
,_
) -> [metaid name
]
200 | _
-> option_default) in
202 let astfvrule_elem recursor k re
=
203 (*within a rule_elem, pattern3 manages the coherence of the bindings*)
206 (match Ast.unwrap re
with
207 Ast.MetaRuleElem
(name
,_
,_
) | Ast.MetaStmt
(name
,_
,_
,_
)
208 | Ast.MetaStmtList
(name
,_
,_
) -> [metaid name
]
209 | _
-> option_default)) in
211 let astfvstatement recursor k s
=
213 (match Ast.unwrap s
with
215 bind_disj (List.map recursor
.V.combiner_statement_dots stms
)
216 | _
-> option_default) in
219 if include_constraints
221 match Ast.get_pos_var mc
with
222 Ast.MetaPos
(name
,constraints
,_
,_
,_
) -> (metaid name
)::constraints
223 | _
-> option_default
224 else option_default in
226 V.combiner
bind option_default
227 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
228 donothing donothing donothing donothing donothing
229 astfvident astfvexpr astfvfullType astfvtypeC astfvinit astfvparam
230 astfvdecls astfvrule_elem astfvstatement donothing donothing donothing_a
232 let collect_all_refs = collect_refs true
233 let collect_non_constraint_refs = collect_refs false
235 let collect_all_rule_refs minirules
=
236 List.fold_left
(@) []
237 (List.map
collect_all_refs.V.combiner_top_level minirules
)
239 let collect_all_minirule_refs = collect_all_refs.V.combiner_top_level
241 (* ---------------------------------------------------------------- *)
244 let bind = Common.union_set
in
245 let option_default = [] in
247 let donothing recursor k e
= k e
in (* just combine in the normal way *)
249 let metaid (x
,_
,_
,_
) = x
in
251 (* cases for metavariables *)
252 let astfvident recursor k i
=
254 (match Ast.unwrap i
with
255 Ast.MetaId
(name
,_
,TC.Saved
,_
)
256 | Ast.MetaFunc
(name
,_
,TC.Saved
,_
)
257 | Ast.MetaLocalFunc
(name
,_
,TC.Saved
,_
) ->
259 | _
-> option_default) in
261 let rec type_collect res
= function
262 TC.ConstVol
(_
,ty
) | TC.Pointer
(ty
) | TC.FunctionPointer
(ty
)
263 | TC.Array
(ty
) -> type_collect res ty
264 | TC.EnumName
(TC.MV
(tyname
,TC.Saved
,_
)) ->
266 | TC.StructUnionName
(_
,TC.MV
(tyname
,TC.Saved
,_
)) ->
268 | TC.MetaType
(tyname
,TC.Saved
,_
) ->
270 | TC.SignedT
(_
,Some ty
) -> type_collect res ty
273 let astfvexpr recursor k e
=
275 match Ast.unwrap e
with
276 Ast.MetaExpr
(name
,_
,_
,Some type_list
,_
,_
) ->
277 List.fold_left
type_collect option_default type_list
281 (match Ast.unwrap e
with
282 Ast.MetaErr
(name
,_
,TC.Saved
,_
) | Ast.MetaExpr
(name
,_
,TC.Saved
,_
,_
,_
)
284 | Ast.MetaExprList
(name
,Ast.MetaListLen
(lenname
,ls
,_
),ns
,_
) ->
286 match ns
with TC.Saved
-> [metaid name
] | _
-> [] in
288 match ls
with TC.Saved
-> [metaid lenname
] | _
-> [] in
290 | Ast.MetaExprList
(name
,_
,TC.Saved
,_
) -> [metaid name
]
291 | _
-> option_default) in
294 let astfvtypeC recursor k ty
=
296 (match Ast.unwrap ty
with
297 Ast.MetaType
(name
,TC.Saved
,_
) -> [metaid name
]
298 | _
-> option_default) in
300 let astfvinit recursor k ty
=
302 (match Ast.unwrap ty
with
303 Ast.MetaInit
(name
,TC.Saved
,_
) -> [metaid name
]
304 | _
-> option_default) in
306 let astfvparam recursor k p
=
308 (match Ast.unwrap p
with
309 Ast.MetaParam
(name
,TC.Saved
,_
) -> [metaid name
]
310 | Ast.MetaParamList
(name
,Ast.MetaListLen
(lenname
,ls
,_
),ns
,_
) ->
312 match ns
with TC.Saved
-> [metaid name
] | _
-> [] in
314 match ls
with TC.Saved
-> [metaid lenname
] | _
-> [] in
316 | Ast.MetaParamList
(name
,_
,_
,_
) -> [metaid name
]
317 | _
-> option_default) in
319 let astfvdecls recursor k d
=
321 (match Ast.unwrap d
with
322 Ast.MetaDecl
(name
,TC.Saved
,_
) | Ast.MetaField
(name
,TC.Saved
,_
) ->
324 | _
-> option_default) in
326 let astfvrule_elem recursor k re
=
327 (*within a rule_elem, pattern3 manages the coherence of the bindings*)
330 (match Ast.unwrap re
with
331 Ast.MetaRuleElem
(name
,TC.Saved
,_
) | Ast.MetaStmt
(name
,TC.Saved
,_
,_
)
332 | Ast.MetaStmtList
(name
,TC.Saved
,_
) -> [metaid name
]
333 | _
-> option_default)) in
336 match Ast.get_pos_var e
with
337 Ast.MetaPos
(name
,_
,_
,TC.Saved
,_
) -> [metaid name
]
338 | _
-> option_default in
340 V.combiner
bind option_default
341 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
342 donothing donothing donothing donothing donothing
343 astfvident astfvexpr donothing astfvtypeC astfvinit astfvparam
344 astfvdecls astfvrule_elem donothing donothing donothing donothing
346 (* ---------------------------------------------------------------- *)
348 (* For the rules under a given metavariable declaration, collect all of the
349 variables that occur in the plus code *)
351 let cip_mcodekind r mck
=
352 let process_anything_list_list anythings
=
353 let astfvs = collect_all_refs.V.combiner_anything
in
354 List.fold_left
(@) []
355 (List.map
(function l
-> List.fold_left
(@) [] (List.map
astfvs l
))
358 Ast.MINUS
(_
,_
,_
,anythings
) -> process_anything_list_list anythings
359 | Ast.CONTEXT
(_
,befaft
) ->
361 Ast.BEFORE
(ll
,_
) -> process_anything_list_list ll
362 | Ast.AFTER
(ll
,_
) -> process_anything_list_list ll
363 | Ast.BEFOREAFTER
(llb
,lla
,_
) ->
364 (process_anything_list_list lla
) @
365 (process_anything_list_list llb
)
370 let collect_fresh_seed_env metavars l
=
375 Ast.MetaFreshIdDecl
(_
,seed
) as x
->
376 ((Ast.get_meta_name x
),seed
)::prev
379 let (seed_env
,seeds
) =
381 (function (seed_env
,seeds
) as prev
->
384 (let v = List.assoc x
fresh in
391 Ast.SeedId
(id
) -> id
::prev
394 ((x
,ids)::seed_env
,Common.union_set
ids seeds
)
395 | _
-> ((x
,[])::seed_env
,seeds
))
396 with Not_found
-> prev
)
398 (List.rev seed_env
,List.rev seeds
)
400 let collect_fresh_seed metavars l
=
401 let (_
,seeds
) = collect_fresh_seed_env metavars l
in seeds
403 let collect_in_plus_term =
405 let bind x y
= x
@ y
in
406 let option_default = [] in
407 let donothing r k e
= k e
in
409 (* no positions in the + code *)
410 let mcode r
(_
,_
,mck
,_
) = cip_mcodekind r mck
in
412 (* case for things with bef/aft mcode *)
414 let astfvrule_elem recursor k re
=
415 match Ast.unwrap re
with
416 Ast.FunHeader
(bef
,_
,fi
,nm
,_
,params
,_
) ->
421 Ast.FType
(ty
) -> collect_all_refs.V.combiner_fullType ty
424 let nm_metas = collect_all_refs.V.combiner_ident nm
in
426 match Ast.unwrap params
with
427 Ast.DOTS
(params
) | Ast.CIRCLES
(params
) ->
431 match Ast.unwrap p
with
432 Ast.VoidParam
(t
) | Ast.Param
(t
,_
) ->
433 collect_all_refs.V.combiner_fullType t
436 | _
-> failwith
"not allowed for params" in
440 (bind (cip_mcodekind recursor bef
) (k re
))))
441 | Ast.Decl
(bef
,_
,_
) ->
442 bind (cip_mcodekind recursor bef
) (k re
)
445 let astfvstatement recursor k s
=
446 match Ast.unwrap s
with
447 Ast.IfThen
(_
,_
,(_
,_
,_
,aft
)) | Ast.IfThenElse
(_
,_
,_
,_
,(_
,_
,_
,aft
))
448 | Ast.While
(_
,_
,(_
,_
,_
,aft
)) | Ast.For
(_
,_
,(_
,_
,_
,aft
))
449 | Ast.Iterator
(_
,_
,(_
,_
,_
,aft
)) ->
450 bind (k s
) (cip_mcodekind recursor aft
)
453 V.combiner
bind option_default
454 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
455 donothing donothing donothing donothing donothing
456 donothing donothing donothing donothing donothing donothing
457 donothing astfvrule_elem astfvstatement donothing donothing donothing
459 let collect_in_plus metavars minirules
=
461 (collect_fresh_seed metavars
463 (List.map
collect_in_plus_term.V.combiner_top_level minirules
)))
465 (* ---------------------------------------------------------------- *)
467 (* For the rules under a given metavariable declaration, collect all of the
468 variables that occur only once and more than once in the minus code *)
470 let collect_all_multirefs minirules
=
471 let refs = List.map
collect_all_refs.V.combiner_top_level minirules
in
472 collect_unitary_nonunitary (List.concat
refs)
474 (* ---------------------------------------------------------------- *)
476 (* classify as unitary (no binding) or nonunitary (env binding) or saved
479 let classify_variables metavar_decls minirules used_after
=
480 let metavars = List.map
Ast.get_meta_name metavar_decls
in
481 let (unitary,nonunitary) = collect_all_multirefs minirules
in
482 let inplus = collect_in_plus metavar_decls minirules
in
484 let donothing r k e
= k e
in
485 let check_unitary name inherited
=
486 if List.mem name
inplus or List.mem name used_after
488 else if not inherited
&& List.mem name
unitary
490 else TC.Nonunitary
in
492 let get_option f
= function Some x
-> Some
(f x
) | None
-> None
in
494 let classify (name
,_
,_
,_
) =
495 let inherited = not
(List.mem name
metavars) in
496 (check_unitary name
inherited,inherited) in
499 match Ast.get_pos_var mc
with
500 Ast.MetaPos
(name
,constraints
,per
,unitary,inherited) ->
501 let (unitary,inherited) = classify name
in
502 Ast.set_pos_var
(Ast.MetaPos
(name
,constraints
,per
,unitary,inherited))
508 match Ast.unwrap
e with
509 Ast.MetaId
(name
,constraints
,_
,_
) ->
510 let (unitary,inherited) = classify name
in
512 (Ast.MetaId
(name
,constraints
,unitary,inherited))
513 | Ast.MetaFunc
(name
,constraints
,_
,_
) ->
514 let (unitary,inherited) = classify name
in
515 Ast.rewrap
e (Ast.MetaFunc
(name
,constraints
,unitary,inherited))
516 | Ast.MetaLocalFunc
(name
,constraints
,_
,_
) ->
517 let (unitary,inherited) = classify name
in
518 Ast.rewrap
e (Ast.MetaLocalFunc
(name
,constraints
,unitary,inherited))
521 let rec type_infos = function
522 TC.ConstVol
(cv
,ty
) -> TC.ConstVol
(cv
,type_infos ty
)
523 | TC.Pointer
(ty
) -> TC.Pointer
(type_infos ty
)
524 | TC.FunctionPointer
(ty
) -> TC.FunctionPointer
(type_infos ty
)
525 | TC.Array
(ty
) -> TC.Array
(type_infos ty
)
526 | TC.EnumName
(TC.MV
(name
,_
,_
)) ->
527 let (unitary,inherited) = classify (name
,(),(),Ast.NoMetaPos
) in
528 TC.EnumName
(TC.MV
(name
,unitary,inherited))
529 | TC.StructUnionName
(su
,TC.MV
(name
,_
,_
)) ->
530 let (unitary,inherited) = classify (name
,(),(),Ast.NoMetaPos
) in
531 TC.StructUnionName
(su
,TC.MV
(name
,unitary,inherited))
532 | TC.MetaType
(name
,_
,_
) ->
533 let (unitary,inherited) = classify (name
,(),(),Ast.NoMetaPos
) in
534 Type_cocci.MetaType
(name
,unitary,inherited)
535 | TC.SignedT
(sgn
,Some ty
) -> TC.SignedT
(sgn
,Some
(type_infos ty
))
538 let expression r k
e =
540 match Ast.unwrap
e with
541 Ast.MetaErr
(name
,constraints
,_
,_
) ->
542 let (unitary,inherited) = classify name
in
543 Ast.rewrap
e (Ast.MetaErr
(name
,constraints
,unitary,inherited))
544 | Ast.MetaExpr
(name
,constraints
,_
,ty
,form
,_
) ->
545 let (unitary,inherited) = classify name
in
546 let ty = get_option (List.map
type_infos) ty in
547 Ast.rewrap
e (Ast.MetaExpr
(name
,constraints
,unitary,ty,form
,inherited))
548 | Ast.MetaExprList
(name
,Ast.MetaListLen
(lenname
,_
,_
),_
,_
) ->
549 (* lenname should have the same properties of being unitary or
551 let (unitary,inherited) = classify name
in
552 let (lenunitary
,leninherited
) = classify lenname
in
556 Ast.MetaListLen
(lenname
,lenunitary
,leninherited
),
558 | Ast.MetaExprList
(name
,lenname
,_
,_
) ->
559 (* lenname should have the same properties of being unitary or
561 let (unitary,inherited) = classify name
in
562 Ast.rewrap
e (Ast.MetaExprList
(name
,lenname
,unitary,inherited))
567 match Ast.unwrap
e with
568 Ast.MetaType
(name
,_
,_
) ->
569 let (unitary,inherited) = classify name
in
570 Ast.rewrap
e (Ast.MetaType
(name
,unitary,inherited))
575 match Ast.unwrap
e with
576 Ast.MetaInit
(name
,_
,_
) ->
577 let (unitary,inherited) = classify name
in
578 Ast.rewrap
e (Ast.MetaInit
(name
,unitary,inherited))
583 match Ast.unwrap
e with
584 Ast.MetaParam
(name
,_
,_
) ->
585 let (unitary,inherited) = classify name
in
586 Ast.rewrap
e (Ast.MetaParam
(name
,unitary,inherited))
587 | Ast.MetaParamList
(name
,Ast.MetaListLen
(lenname
,_
,_
),_
,_
) ->
588 let (unitary,inherited) = classify name
in
589 let (lenunitary
,leninherited
) = classify lenname
in
592 (name
,Ast.MetaListLen
(lenname
,lenunitary
,leninherited
),
594 | Ast.MetaParamList
(name
,lenname
,_
,_
) ->
595 let (unitary,inherited) = classify name
in
596 Ast.rewrap
e (Ast.MetaParamList
(name
,lenname
,unitary,inherited))
601 match Ast.unwrap
e with
602 Ast.MetaDecl
(name
,_
,_
) ->
603 let (unitary,inherited) = classify name
in
604 Ast.rewrap
e (Ast.MetaDecl
(name
,unitary,inherited))
605 | Ast.MetaField
(name
,_
,_
) ->
606 let (unitary,inherited) = classify name
in
607 Ast.rewrap
e (Ast.MetaField
(name
,unitary,inherited))
610 let rule_elem r k
e =
612 match Ast.unwrap
e with
613 Ast.MetaStmt
(name
,_
,msi
,_
) ->
614 let (unitary,inherited) = classify name
in
615 Ast.rewrap
e (Ast.MetaStmt
(name
,unitary,msi
,inherited))
616 | Ast.MetaStmtList
(name
,_
,_
) ->
617 let (unitary,inherited) = classify name
in
618 Ast.rewrap
e (Ast.MetaStmtList
(name
,unitary,inherited))
622 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
623 donothing donothing donothing donothing donothing
624 ident expression donothing typeC init param decl rule_elem
625 donothing donothing donothing donothing in
627 List.map
fn.V.rebuilder_top_level minirules
629 (* ---------------------------------------------------------------- *)
631 (* For a minirule, collect the set of non-local (not in "bound") variables that
632 are referenced. Store them in a hash table. *)
634 (* bound means the metavariable was declared previously, not locally *)
636 (* Highly inefficient, because we call collect_all_refs on nested code
637 multiple times. But we get the advantage of not having too many variants
638 of the same functions. *)
640 (* Inherited doesn't include position constraints. If they are not bound
641 then there is no constraint. *)
643 let astfvs metavars bound
=
648 Ast.MetaFreshIdDecl
(_
,seed
) as x
->
649 ((Ast.get_meta_name x
),seed
)::prev
653 let collect_fresh l
=
654 let (matched
,freshvars
) =
656 (function (matched
,freshvars
) ->
658 try let v = List.assoc x
fresh in (matched
,(x
,v)::freshvars
)
659 with Not_found
-> (x
::matched
,freshvars
))
661 (List.rev matched
, List.rev freshvars
) in
663 (* cases for the elements of anything *)
664 let simple_setup getter k re
=
665 let minus_free = nub (getter
collect_all_refs re
) in
667 nub (getter
collect_non_constraint_refs re
) in
669 collect_fresh_seed metavars (getter
collect_in_plus_term re
) in
670 let free = Common.union_set
minus_free plus_free in
671 let nc_free = Common.union_set
minus_nc_free plus_free in
673 List.filter
(function x
-> not
(List.mem x bound
)) free in
675 List.filter
(function x
-> List.mem x bound
) nc_free in
677 List.filter
(function x
-> not
(List.mem x bound
)) minus_free in
678 let (matched
,fresh) = collect_fresh unbound in
680 Ast.free_vars
= matched
;
681 Ast.minus_free_vars
= munbound;
682 Ast.fresh_vars
= fresh;
683 Ast.inherited = inherited;
684 Ast.saved_witness
= []} in
686 let astfvrule_elem recursor k re
=
687 simple_setup (function x
-> x
.V.combiner_rule_elem
) k re
in
689 let astfvstatement recursor k s
=
690 let minus_free = nub (collect_all_refs.V.combiner_statement s
) in
692 nub (collect_non_constraint_refs.V.combiner_statement s
) in
694 collect_fresh_seed metavars
695 (collect_in_plus_term.V.combiner_statement s
) in
696 let free = Common.union_set
minus_free plus_free in
697 let nc_free = Common.union_set
minus_nc_free plus_free in
698 let classify free minus_free =
699 let (unbound,inherited) =
700 List.partition
(function x
-> not
(List.mem x bound
)) free in
702 List.filter
(function x
-> not
(List.mem x bound
)) minus_free in
703 let (matched
,fresh) = collect_fresh unbound in
704 (matched
,munbound,fresh,inherited) in
708 collect_fresh_seed metavars
709 (cip_mcodekind collect_in_plus_term aft
) in
710 match Ast.unwrap
res with
711 Ast.IfThen
(header
,branch
,(_
,_
,_
,aft
)) ->
712 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
713 Ast.IfThen
(header
,branch
,(unbound,fresh,inherited,aft
))
714 | Ast.IfThenElse
(header
,branch1
,els
,branch2
,(_
,_
,_
,aft
)) ->
715 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
716 Ast.IfThenElse
(header
,branch1
,els
,branch2
,
717 (unbound,fresh,inherited,aft
))
718 | Ast.While
(header
,body
,(_
,_
,_
,aft
)) ->
719 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
720 Ast.While
(header
,body
,(unbound,fresh,inherited,aft
))
721 | Ast.For
(header
,body
,(_
,_
,_
,aft
)) ->
722 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
723 Ast.For
(header
,body
,(unbound,fresh,inherited,aft
))
724 | Ast.Iterator
(header
,body
,(_
,_
,_
,aft
)) ->
725 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
726 Ast.Iterator
(header
,body
,(unbound,fresh,inherited,aft
))
729 let (matched
,munbound,fresh,_
) = classify free minus_free in
731 List.filter
(function x
-> List.mem x bound
) nc_free in
734 Ast.free_vars
= matched
;
735 Ast.minus_free_vars
= munbound;
736 Ast.fresh_vars
= fresh;
737 Ast.inherited = inherited;
738 Ast.saved_witness
= []} in
740 let astfvstatement_dots recursor k sd
=
741 simple_setup (function x
-> x
.V.combiner_statement_dots
) k sd
in
743 let astfvcase_line recursor k cl
=
744 simple_setup (function x
-> x
.V.combiner_case_line
) k cl
in
746 let astfvtoplevel recursor k tl
=
747 let saved = collect_saved.V.combiner_top_level tl
in
748 {(k tl
) with Ast.saved_witness
= saved} in
751 let donothing r k
e = k
e in
754 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
755 donothing donothing astfvstatement_dots donothing donothing
756 donothing donothing donothing donothing donothing donothing donothing
757 astfvrule_elem astfvstatement astfvcase_line astfvtoplevel donothing
760 let collect_astfvs rules =
761 let rec loop bound = function
763 | (metavars,(nm,rule_info,minirules))::rules ->
765 Common.minus_set bound (List.map Ast.get_meta_name metavars) in
767 (List.map (astfvs metavars bound).V.rebuilder_top_level minirules))::
768 (loop ((List.map Ast.get_meta_name metavars)@bound) rules) in
772 let collect_astfvs rules
=
773 let rec loop bound = function
775 | (metavars, rule
)::rules
->
777 Ast.ScriptRule
(_
,_
,_
,_
,script_vars
,_
) ->
778 (* why are metavars in rule, but outside for cocci rule??? *)
779 let bound = script_vars
@ bound in
780 rule
::(loop bound rules
)
781 | Ast.InitialScriptRule
(_
,_
,_
,_
) | Ast.FinalScriptRule
(_
,_
,_
,_
) ->
782 (* bound stays as is because script rules have no names, so no
783 inheritance is possible *)
784 rule
::(loop bound rules
)
785 | Ast.CocciRule
(nm
, rule_info
, minirules
, isexp
, ruletype
) ->
787 Common.minus_set
bound (List.map
Ast.get_meta_name
metavars) in
790 (List.map
(astfvs metavars bound).V.rebuilder_top_level
793 (loop ((List.map
Ast.get_meta_name
metavars)@bound) rules
) in
796 (* ---------------------------------------------------------------- *)
797 (* position variables that appear as a constraint on another position variable.
798 a position variable also cannot appear both positively and negatively in a
801 let get_neg_pos_list (_
,rule
) used_after_list
=
802 let donothing r k
e = k
e in
803 let bind (p1
,np1
) (p2
,np2
) =
804 (Common.union_set p1 p2
, Common.union_set np1 np2
) in
805 let option_default = ([],[]) in
806 let metaid (x
,_
,_
,_
) = x
in
808 match Ast.get_pos_var mc
with
809 Ast.MetaPos
(name
,constraints
,Ast.PER
,_
,_
) ->
810 ([metaid name
],constraints
)
811 | Ast.MetaPos
(name
,constraints
,Ast.ALL
,_
,_
) ->
812 ([],(metaid name
)::constraints
)
813 | _
-> option_default in
815 V.combiner
bind option_default
816 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
817 donothing donothing donothing donothing donothing
818 donothing donothing donothing donothing donothing donothing
819 donothing donothing donothing donothing donothing donothing in
821 Ast.CocciRule
(_
,_
,minirules
,_
,_
) ->
823 (function toplevel
->
824 let (positions
,neg_positions
) = v.V.combiner_top_level toplevel
in
825 (if List.exists
(function p
-> List.mem p neg_positions
) positions
828 "a variable cannot be used both as a position and a constraint");
831 | Ast.ScriptRule _
| Ast.InitialScriptRule _
| Ast.FinalScriptRule _
->
832 (*no negated positions*) []
834 (* ---------------------------------------------------------------- *)
836 (* collect used after lists, per minirule *)
838 (* defined is a list of variables that were declared in a previous metavar
841 (* Top-level used after: For each rule collect the set of variables that
842 are inherited, ie used but not defined. These are accumulated back to
843 their point of definition. *)
846 let collect_top_level_used_after metavar_rule_list
=
847 let drop_virt = List.filter
(function ("virtual",_
) -> false | _
-> true) in
848 let (used_after
,used_after_lists
) =
850 (function (metavar_list
,r
) ->
851 function (used_after
,used_after_lists
) ->
852 let locally_defined =
854 Ast.ScriptRule
(_
,_
,_
,_
,free_vars
,_
) -> free_vars
855 | _
-> List.map
Ast.get_meta_name metavar_list
in
856 let continue_propagation =
857 List.filter
(function x
-> not
(List.mem x
locally_defined))
861 Ast.ScriptRule
(_
,_
,_
,mv
,_
,_
) ->
862 drop_virt(List.map
(function (_
,(r
,v),_
) -> (r
,v)) mv
)
863 | Ast.InitialScriptRule
(_
,_
,_
,_
)
864 | Ast.FinalScriptRule
(_
,_
,_
,_
) -> []
865 | Ast.CocciRule
(_
,_
,rule
,_
,_
) ->
867 (Common.union_set
(nub (collect_all_rule_refs rule
))
868 (collect_in_plus metavar_list rule
)) in
870 List.filter
(function x
-> not
(List.mem x
locally_defined))
872 (Common.union_set
inherited continue_propagation,
873 used_after
::used_after_lists
))
874 metavar_rule_list
([],[]) in
875 match used_after
with
876 [] -> used_after_lists
879 (Printf.sprintf
"collect_top_level_used_after: unbound variables %s"
880 (String.concat
" " (List.map
(function (_
,x
) -> x
) used_after
)))
882 let collect_local_used_after metavars minirules used_after
=
883 let locally_defined = List.map
Ast.get_meta_name
metavars in
884 let rec loop = function
885 [] -> (used_after
,[],[],[],[])
887 (* In a rule there are three kinds of local variables:
888 1. Variables referenced in the minus or context code.
889 These get a value by matching. This value can be used in
891 2. Fresh variables referenced in the plus code.
892 3. Variables referenced in the seeds of the fresh variables.
893 There are also non-local variables. These may either be variables
894 referenced in the minus, context, or plus code, or they may be
895 variables referenced in the seeds of the fresh variables. *)
896 (* Step 1: collect all references in minus/context, plus, seed
898 let variables_referenced_in_minus_context_code =
899 nub (collect_all_minirule_refs minirule
) in
900 let variables_referenced_in_plus_code =
901 collect_in_plus_term.V.combiner_top_level minirule
in
902 let (env_of_fresh_seeds
,seeds_and_plus
) =
903 collect_fresh_seed_env
904 metavars variables_referenced_in_plus_code in
906 Common.union_set
variables_referenced_in_minus_context_code
908 (* Step 2: identify locally defined ones *)
909 let local_fresh = List.map fst env_of_fresh_seeds
in
911 List.partition
(function x
-> List.mem x
locally_defined) in
912 let local_env_of_fresh_seeds =
913 (* these have to be restricted to only one value if the associated
914 fresh variable is used after *)
915 List.map
(function (f
,ss
) -> (f
,is_local ss
)) env_of_fresh_seeds
in
916 let (local_all_free_vars
,nonlocal_all_free_vars
) =
917 is_local all_free_vars in
918 (* Step 3, recurse on the rest of the rules, making available whatever
919 has been defined in this one *)
920 let (mini_used_after
,fvs_lists
,mini_used_after_lists
,
921 mini_fresh_used_after_lists
,mini_fresh_used_after_seeds
) =
923 (* Step 4: collect the results. These are:
924 1. All of the variables used non-locally in the rules starting
926 2. All of the free variables to the end of the semantic patch
927 3. The variables that are used afterwards and defined here by
928 matching (minus or context code)
929 4. The variables that are used afterwards and are defined here as
931 5. The variables that are used as seeds in computing the bindings
932 of the variables collected in part 4. *)
933 let (local_used_after
, nonlocal_used_after
) =
934 is_local mini_used_after
in
935 let (fresh_local_used_after
(*4*),matched_local_used_after
) =
936 List.partition
(function x
-> List.mem x
local_fresh)
938 let matched_local_used_after(*3*) =
939 Common.union_set
matched_local_used_after nonlocal_used_after
in
940 let new_used_after = (*1*)
941 Common.union_set nonlocal_all_free_vars nonlocal_used_after
in
942 let fresh_local_used_after_seeds =
944 (* no point to keep variables that already are gtd to have only
946 (function x
-> not
(List.mem x
matched_local_used_after))
947 (List.fold_left
(function p
-> function c
-> Common.union_set c p
)
951 fst
(List.assoc fua
local_env_of_fresh_seeds))
952 fresh_local_used_after
)) in
953 (new_used_after,all_free_vars::fvs_lists
(*2*),
954 matched_local_used_after::mini_used_after_lists
,
955 fresh_local_used_after
::mini_fresh_used_after_lists
,
956 fresh_local_used_after_seeds::mini_fresh_used_after_seeds
) in
957 let (_
,fvs_lists
,used_after_lists
(*ua*),
958 fresh_used_after_lists
(*fua*),fresh_used_after_lists_seeds
(*fuas*)) =
960 (fvs_lists
,used_after_lists
,
961 fresh_used_after_lists
,fresh_used_after_lists_seeds
)
965 let collect_used_after metavar_rule_list
=
966 let used_after_lists = collect_top_level_used_after metavar_rule_list
in
968 (function (metavars,r
) ->
969 function used_after
->
971 Ast.ScriptRule
(_
,_
,_
,_
,_
,_
) (* no minirules, so nothing to do? *)
972 | Ast.InitialScriptRule
(_
,_
,_
,_
) | Ast.FinalScriptRule
(_
,_
,_
,_
) ->
973 ([], [used_after
], [[]], [])
974 | Ast.CocciRule
(name
, rule_info
, minirules
, _
,_
) ->
975 collect_local_used_after metavars minirules used_after
977 metavar_rule_list
used_after_lists
979 let rec split4 = function
981 | (a
,b
,c
,d
)::l
-> let (a1
,b1
,c1
,d1
) = split4 l
in (a
::a1
,b
::b1
,c
::c1
,d
::d1
)
983 (* ---------------------------------------------------------------- *)
986 let free_vars rules
=
987 let metavars = List.map
(function (mv
,rule
) -> mv
) rules
in
988 let (fvs_lists
,used_after_matched_lists
,
989 fresh_used_after_lists
,fresh_used_after_lists_seeds
) =
990 split4 (collect_used_after rules
) in
992 List.map2
get_neg_pos_list rules used_after_matched_lists
in
993 let positions_list = (* for all rules, assume all positions are used after *)
997 Ast.ScriptRule _
(* doesn't declare position variables *)
998 | Ast.InitialScriptRule _
| Ast.FinalScriptRule _
-> []
999 | Ast.CocciRule
(_
,_
,rule
,_
,_
) ->
1003 function Ast.MetaPosDecl
(_
,nm
) -> nm
::prev
| _
-> prev
)
1005 List.map
(function _
-> positions) rule
)
1010 function (ua
,fua
) ->
1013 | Ast.InitialScriptRule _
| Ast.FinalScriptRule _
-> r
1014 | Ast.CocciRule
(nm
, rule_info
, r
, is_exp
,ruletype
) ->
1017 classify_variables mv r
1018 ((List.concat ua
) @ (List.concat fua
)),
1020 rules
(List.combine used_after_matched_lists fresh_used_after_lists
) in
1021 let new_rules = collect_astfvs (List.combine
metavars new_rules) in
1022 (metavars,new_rules,
1023 fvs_lists
,neg_pos_lists,
1024 (used_after_matched_lists
,
1025 fresh_used_after_lists
,fresh_used_after_lists_seeds
),