2 * Copyright 2012, INRIA
3 * Julia Lawall, Gilles Muller
4 * Copyright 2010-2011, INRIA, University of Copenhagen
5 * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
6 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
7 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
8 * This file is part of Coccinelle.
10 * Coccinelle is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, according to version 2 of the License.
14 * Coccinelle is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
22 * The authors reserve the right to distribute this or future versions of
23 * Coccinelle under other licenses.
27 (* For each rule return the list of variables that are used after it.
28 Also augment various parts of each rule with unitary, inherited, and freshness
31 (* metavar decls should be better integrated into computations of free
32 variables in plus code *)
34 module Ast
= Ast_cocci
35 module V
= Visitor_ast
36 module TC
= Type_cocci
38 let rec nub = function
40 | (x
::xs
) when (List.mem x xs
) -> nub xs
41 | (x
::xs
) -> x
::(nub xs
)
43 (* Collect all variable references in a minirule. For a disj, we collect
44 the maximum number (2 is enough) of references in any branch. *)
46 let collect_unitary_nonunitary free_usage
=
47 let free_usage = List.sort compare
free_usage in
48 let rec loop1 todrop
= function (* skips multiple occurrences *)
50 | (x
::xs
) as all
-> if x
= todrop
then loop1 todrop xs
else all
in
51 let rec loop2 = function
55 if x
= y
(* occurs more than once in free_usage *)
57 let (unitary
,non_unitary
) = loop2(loop1 x xs
) in
58 (unitary
,x
::non_unitary
)
59 else (* occurs only once in free_usage *)
60 let (unitary
,non_unitary
) = loop2 (y
::xs
) in
61 (x
::unitary
,non_unitary
) in
64 let collect_refs include_constraints
=
65 let bind x y
= x
@ y
in
66 let option_default = [] in
68 let donothing recursor k e
= k e
in (* just combine in the normal way *)
70 let donothing_a recursor k e
= (* anything is not wrapped *)
71 k e
in (* just combine in the normal way *)
73 (* the following considers that anything that occurs non-unitarily in one
74 branch occurs nonunitarily in all branches. This is not optimal, but
75 doing better seems to require a breadth-first traversal, which is
76 perhaps better to avoid. Also, unitarily is represented as occuring once,
77 while nonunitarily is represented as twice - more is irrelevant *)
78 (* cases for disjs and metavars *)
79 let bind_disj refs_branches
=
80 let (unitary
,nonunitary
) =
81 List.split
(List.map
collect_unitary_nonunitary refs_branches
) in
82 let unitary = nub (List.concat
unitary) in
83 let nonunitary = nub (List.concat
nonunitary) in
85 List.filter
(function x
-> not
(List.mem x
nonunitary)) unitary in
86 unitary@nonunitary@nonunitary in
88 let metaid (x
,_
,_
,_
) = x
in
90 let astfvident recursor k i
=
92 (match Ast.unwrap i
with
93 Ast.MetaId
(name
,idconstraint
,_
,_
) | Ast.MetaFunc
(name
,idconstraint
,_
,_
)
94 | Ast.MetaLocalFunc
(name
,idconstraint
,_
,_
) ->
96 if include_constraints
98 match idconstraint
with
99 Ast.IdNegIdSet
(_
,metas) -> metas
102 bind (List.rev
metas) [metaid name
]
103 | Ast.DisjId
(ids
) -> bind_disj (List.map k ids
)
104 | _
-> option_default) in
106 let rec type_collect res
= function
107 TC.ConstVol
(_
,ty
) | TC.Pointer
(ty
) | TC.FunctionPointer
(ty
)
108 | TC.Array
(ty
) -> type_collect res ty
109 | TC.EnumName
(TC.MV
(tyname
,_
,_
)) ->
111 | TC.StructUnionName
(_
,TC.MV
(tyname
,_
,_
)) ->
113 | TC.MetaType
(tyname
,_
,_
) ->
115 | TC.SignedT
(_
,Some ty
) -> type_collect res ty
118 let astfvexpr recursor k e
=
120 (match Ast.unwrap e
with
121 Ast.MetaExpr
(name
,constraints
,_
,Some type_list
,_
,_
) ->
122 let types = List.fold_left
type_collect option_default type_list
in
124 if include_constraints
126 match constraints
with
127 Ast.SubExpCstrt l
-> l
130 bind extra (bind [metaid name
] types)
131 | Ast.MetaErr
(name
,constraints
,_
,_
)
132 | Ast.MetaExpr
(name
,constraints
,_
,_
,_
,_
) ->
134 if include_constraints
136 match constraints
with
137 Ast.SubExpCstrt l
-> l
140 bind extra [metaid name
]
141 | Ast.MetaExprList
(name
,Ast.MetaListLen
(lenname
,_
,_
),_
,_
) ->
142 [metaid name
;metaid lenname
]
143 | Ast.MetaExprList
(name
,_
,_
,_
) -> [metaid name
]
144 | Ast.DisjExpr
(exps
) -> bind_disj (List.map k exps
)
145 | _
-> option_default) in
147 let astfvdecls recursor k d
=
149 (match Ast.unwrap d
with
150 Ast.MetaDecl
(name
,_
,_
) | Ast.MetaField
(name
,_
,_
) -> [metaid name
]
151 | Ast.MetaFieldList
(name
,Ast.MetaListLen
(lenname
,_
,_
),_
,_
) ->
152 [metaid name
;metaid lenname
]
153 | Ast.MetaFieldList
(name
,_
,_
,_
) ->
155 | Ast.DisjDecl
(decls
) -> bind_disj (List.map k decls
)
156 | _
-> option_default) in
158 let astfvfullType recursor k ty
=
160 (match Ast.unwrap ty
with
161 Ast.DisjType
(types) -> bind_disj (List.map k
types)
162 | _
-> option_default) in
164 let astfvtypeC recursor k ty
=
166 (match Ast.unwrap ty
with
167 Ast.MetaType
(name
,_
,_
) -> [metaid name
]
168 | _
-> option_default) in
170 let astfvinit recursor k ty
=
172 (match Ast.unwrap ty
with
173 Ast.MetaInit
(name
,_
,_
) -> [metaid name
]
174 | Ast.MetaInitList
(name
,Ast.MetaListLen
(lenname
,_
,_
),_
,_
) ->
175 [metaid name
;metaid lenname
]
176 | Ast.MetaInitList
(name
,_
,_
,_
) -> [metaid name
]
177 | _
-> option_default) in
179 let astfvparam recursor k p
=
181 (match Ast.unwrap p
with
182 Ast.MetaParam
(name
,_
,_
) -> [metaid name
]
183 | Ast.MetaParamList
(name
,Ast.MetaListLen
(lenname
,_
,_
),_
,_
) ->
184 [metaid name
;metaid lenname
]
185 | Ast.MetaParamList
(name
,_
,_
,_
) -> [metaid name
]
186 | _
-> option_default) in
188 let astfvrule_elem recursor k re
=
189 (*within a rule_elem, pattern3 manages the coherence of the bindings*)
192 (match Ast.unwrap re
with
193 Ast.MetaRuleElem
(name
,_
,_
) | Ast.MetaStmt
(name
,_
,_
,_
)
194 | Ast.MetaStmtList
(name
,_
,_
) -> [metaid name
]
195 | _
-> option_default)) in
197 let astfvstatement recursor k s
=
199 (match Ast.unwrap s
with
201 bind_disj (List.map recursor
.V.combiner_statement_dots stms
)
202 | _
-> option_default) in
205 if include_constraints
209 (function Ast.MetaPos
(name
,constraints
,_
,_
,_
) ->
210 (metaid name
)::constraints
)
211 (Ast.get_pos_var mc
))
212 else option_default in
214 V.combiner
bind option_default
215 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
216 donothing donothing donothing donothing donothing
217 astfvident astfvexpr astfvfullType astfvtypeC astfvinit astfvparam
218 astfvdecls astfvrule_elem astfvstatement donothing donothing donothing_a
220 let collect_all_refs = collect_refs true
221 let collect_non_constraint_refs = collect_refs false
223 let collect_all_rule_refs minirules
=
224 List.fold_left
(@) []
225 (List.map
collect_all_refs.V.combiner_top_level minirules
)
227 let collect_all_minirule_refs = collect_all_refs.V.combiner_top_level
229 (* ---------------------------------------------------------------- *)
232 let bind = Common.union_set
in
233 let option_default = [] in
235 let donothing recursor k e
= k e
in (* just combine in the normal way *)
237 let metaid (x
,_
,_
,_
) = x
in
239 (* cases for metavariables *)
240 let astfvident recursor k i
=
242 (match Ast.unwrap i
with
243 Ast.MetaId
(name
,_
,TC.Saved
,_
)
244 | Ast.MetaFunc
(name
,_
,TC.Saved
,_
)
245 | Ast.MetaLocalFunc
(name
,_
,TC.Saved
,_
) -> [metaid name
]
246 | _
-> option_default) in
248 let rec type_collect res
= function
249 TC.ConstVol
(_
,ty
) | TC.Pointer
(ty
) | TC.FunctionPointer
(ty
)
250 | TC.Array
(ty
) -> type_collect res ty
251 | TC.EnumName
(TC.MV
(tyname
,TC.Saved
,_
)) ->
253 | TC.StructUnionName
(_
,TC.MV
(tyname
,TC.Saved
,_
)) ->
255 | TC.MetaType
(tyname
,TC.Saved
,_
) ->
257 | TC.SignedT
(_
,Some ty
) -> type_collect res ty
260 let astfvexpr recursor k e
=
262 match Ast.unwrap e
with
263 Ast.MetaExpr
(name
,_
,_
,Some type_list
,_
,_
) ->
264 List.fold_left
type_collect option_default type_list
268 (match Ast.unwrap e
with
269 Ast.MetaErr
(name
,_
,TC.Saved
,_
) | Ast.MetaExpr
(name
,_
,TC.Saved
,_
,_
,_
)
271 | Ast.MetaExprList
(name
,Ast.MetaListLen
(lenname
,ls
,_
),ns
,_
) ->
273 match ns
with TC.Saved
-> [metaid name
] | _
-> [] in
275 match ls
with TC.Saved
-> [metaid lenname
] | _
-> [] in
277 | Ast.MetaExprList
(name
,_
,TC.Saved
,_
) -> [metaid name
]
278 | _
-> option_default) in
281 let astfvtypeC recursor k ty
=
283 (match Ast.unwrap ty
with
284 Ast.MetaType
(name
,TC.Saved
,_
) -> [metaid name
]
285 | _
-> option_default) in
287 let astfvinit recursor k ty
=
289 (match Ast.unwrap ty
with
290 Ast.MetaInit
(name
,TC.Saved
,_
) -> [metaid name
]
291 | Ast.MetaInitList
(name
,Ast.MetaListLen
(lenname
,ls
,_
),ns
,_
) ->
293 match ns
with TC.Saved
-> [metaid name
] | _
-> [] in
295 match ls
with TC.Saved
-> [metaid lenname
] | _
-> [] in
297 | _
-> option_default) in
299 let astfvparam recursor k p
=
301 (match Ast.unwrap p
with
302 Ast.MetaParam
(name
,TC.Saved
,_
) -> [metaid name
]
303 | Ast.MetaParamList
(name
,Ast.MetaListLen
(lenname
,ls
,_
),ns
,_
) ->
305 match ns
with TC.Saved
-> [metaid name
] | _
-> [] in
307 match ls
with TC.Saved
-> [metaid lenname
] | _
-> [] in
309 | Ast.MetaParamList
(name
,_
,TC.Saved
,_
) -> [metaid name
]
310 | _
-> option_default) in
312 let astfvdecls recursor k d
=
314 (match Ast.unwrap d
with
315 Ast.MetaDecl
(name
,TC.Saved
,_
) | Ast.MetaField
(name
,TC.Saved
,_
) ->
317 | Ast.MetaFieldList
(name
,Ast.MetaListLen
(lenname
,ls
,_
),ns
,_
) ->
319 match ns
with TC.Saved
-> [metaid name
] | _
-> [] in
321 match ls
with TC.Saved
-> [metaid lenname
] | _
-> [] in
323 | Ast.MetaFieldList
(name
,_
,TC.Saved
,_
) -> [metaid name
]
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
339 Ast.MetaPos
(name
,_
,_
,TC.Saved
,_
) -> (metaid name
) :: acc
341 option_default (Ast.get_pos_var e
) in
343 V.combiner
bind option_default
344 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
345 donothing donothing donothing donothing donothing
346 astfvident astfvexpr donothing astfvtypeC astfvinit astfvparam
347 astfvdecls astfvrule_elem donothing donothing donothing donothing
349 (* ---------------------------------------------------------------- *)
351 (* For the rules under a given metavariable declaration, collect all of the
352 variables that occur in the plus code *)
354 let cip_mcodekind r mck
=
355 let process_anything_list_list anythings
=
356 let astfvs = collect_all_refs.V.combiner_anything
in
357 List.fold_left
(@) []
358 (List.map
(function l
-> List.fold_left
(@) [] (List.map
astfvs l
))
361 Ast.MINUS
(_
,_
,_
,replacement
) ->
362 (match replacement
with
363 Ast.REPLACEMENT
(anythings
,_
) -> process_anything_list_list anythings
364 | Ast.NOREPLACEMENT
-> [])
365 | Ast.CONTEXT
(_
,befaft
) ->
367 Ast.BEFORE
(ll
,_
) -> process_anything_list_list ll
368 | Ast.AFTER
(ll
,_
) -> process_anything_list_list ll
369 | Ast.BEFOREAFTER
(llb
,lla
,_
) ->
370 (process_anything_list_list lla
) @
371 (process_anything_list_list llb
)
376 let collect_fresh_seed_env metavars l
=
381 Ast.MetaFreshIdDecl
(_
,seed
) as x
->
382 ((Ast.get_meta_name x
),seed
)::prev
385 let (seed_env
,seeds
) =
387 (function (seed_env
,seeds
) as prev
->
390 (let v = List.assoc x
fresh in
397 Ast.SeedId
(id
) -> id
::prev
400 ((x
,ids)::seed_env
,Common.union_set
ids seeds
)
401 | _
-> ((x
,[])::seed_env
,seeds
))
402 with Not_found
-> prev
)
404 (List.rev seed_env
,List.rev seeds
)
406 let collect_fresh_seed metavars l
=
407 let (_
,seeds
) = collect_fresh_seed_env metavars l
in seeds
409 let collect_in_plus_term =
411 let bind x y
= x
@ y
in
412 let option_default = [] in
413 let donothing r k e
= k e
in
415 (* no positions in the + code *)
416 let mcode r
(_
,_
,mck
,_
) = cip_mcodekind r mck
in
418 (* case for things with bef/aft mcode *)
420 let astfvrule_elem recursor k re
=
421 match Ast.unwrap re
with
422 Ast.FunHeader
(bef
,_
,fi
,nm
,_
,params
,_
) ->
427 Ast.FType
(ty
) -> collect_all_refs.V.combiner_fullType ty
430 let nm_metas = collect_all_refs.V.combiner_ident nm
in
432 match Ast.unwrap params
with
433 Ast.DOTS
(params
) | Ast.CIRCLES
(params
) ->
437 match Ast.unwrap p
with
438 Ast.VoidParam
(t
) | Ast.Param
(t
,_
) ->
439 collect_all_refs.V.combiner_fullType t
442 | _
-> failwith
"not allowed for params" in
446 (bind (cip_mcodekind recursor bef
) (k re
))))
447 | Ast.Decl
(bef
,_
,_
) ->
448 bind (cip_mcodekind recursor bef
) (k re
)
451 let astfvstatement recursor k s
=
452 match Ast.unwrap s
with
453 Ast.IfThen
(_
,_
,(_
,_
,_
,aft
)) | Ast.IfThenElse
(_
,_
,_
,_
,(_
,_
,_
,aft
))
454 | Ast.While
(_
,_
,(_
,_
,_
,aft
)) | Ast.For
(_
,_
,(_
,_
,_
,aft
))
455 | Ast.Iterator
(_
,_
,(_
,_
,_
,aft
)) ->
456 bind (k s
) (cip_mcodekind recursor aft
)
459 V.combiner
bind option_default
460 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
461 donothing donothing donothing donothing donothing
462 donothing donothing donothing donothing donothing donothing
463 donothing astfvrule_elem astfvstatement donothing donothing donothing
465 let collect_in_plus metavars minirules
=
467 (collect_fresh_seed metavars
469 (List.map
collect_in_plus_term.V.combiner_top_level minirules
)))
471 (* ---------------------------------------------------------------- *)
473 (* For the rules under a given metavariable declaration, collect all of the
474 variables that occur only once and more than once in the minus code *)
476 let collect_all_multirefs minirules
=
477 let refs = List.map
collect_all_refs.V.combiner_top_level minirules
in
478 collect_unitary_nonunitary (List.concat
refs)
480 (* ---------------------------------------------------------------- *)
482 (* classify as unitary (no binding) or nonunitary (env binding) or saved
485 let classify_variables metavar_decls minirules used_after
=
486 let metavars = List.map
Ast.get_meta_name metavar_decls
in
487 let (unitary,nonunitary) = collect_all_multirefs minirules
in
488 let inplus = collect_in_plus metavar_decls minirules
in
490 let donothing r k e
= k e
in
491 let check_unitary name inherited
=
492 if List.mem name
inplus or List.mem name used_after
494 else if not inherited
&& List.mem name
unitary
496 else TC.Nonunitary
in
498 let get_option f
= function Some x
-> Some
(f x
) | None
-> None
in
500 let classify (name
,_
,_
,_
) =
501 let inherited = not
(List.mem name
metavars) in
502 (check_unitary name
inherited,inherited) in
507 (function Ast.MetaPos
(name
,constraints
,per
,unitary,inherited) ->
508 let (unitary,inherited) = classify name
in
509 Ast.MetaPos
(name
,constraints
,per
,unitary,inherited))
510 (Ast.get_pos_var mc
) in
511 Ast.set_pos_var
p mc
in
515 match Ast.unwrap
e with
516 Ast.MetaId
(name
,constraints
,_
,_
) ->
517 let (unitary,inherited) = classify name
in
519 (Ast.MetaId
(name
,constraints
,unitary,inherited))
520 | Ast.MetaFunc
(name
,constraints
,_
,_
) ->
521 let (unitary,inherited) = classify name
in
522 Ast.rewrap
e (Ast.MetaFunc
(name
,constraints
,unitary,inherited))
523 | Ast.MetaLocalFunc
(name
,constraints
,_
,_
) ->
524 let (unitary,inherited) = classify name
in
525 Ast.rewrap
e (Ast.MetaLocalFunc
(name
,constraints
,unitary,inherited))
528 let rec type_infos = function
529 TC.ConstVol
(cv
,ty
) -> TC.ConstVol
(cv
,type_infos ty
)
530 | TC.Pointer
(ty
) -> TC.Pointer
(type_infos ty
)
531 | TC.FunctionPointer
(ty
) -> TC.FunctionPointer
(type_infos ty
)
532 | TC.Array
(ty
) -> TC.Array
(type_infos ty
)
533 | TC.EnumName
(TC.MV
(name
,_
,_
)) ->
534 let (unitary,inherited) = classify (name
,(),(),[]) in
535 TC.EnumName
(TC.MV
(name
,unitary,inherited))
536 | TC.StructUnionName
(su
,TC.MV
(name
,_
,_
)) ->
537 let (unitary,inherited) = classify (name
,(),(),[]) in
538 TC.StructUnionName
(su
,TC.MV
(name
,unitary,inherited))
539 | TC.MetaType
(name
,_
,_
) ->
540 let (unitary,inherited) = classify (name
,(),(),[]) in
541 Type_cocci.MetaType
(name
,unitary,inherited)
542 | TC.SignedT
(sgn
,Some ty
) -> TC.SignedT
(sgn
,Some
(type_infos ty
))
545 let expression r k
e =
547 match Ast.unwrap
e with
548 Ast.MetaErr
(name
,constraints
,_
,_
) ->
549 let (unitary,inherited) = classify name
in
550 Ast.rewrap
e (Ast.MetaErr
(name
,constraints
,unitary,inherited))
551 | Ast.MetaExpr
(name
,constraints
,_
,ty
,form
,_
) ->
552 let (unitary,inherited) = classify name
in
553 let ty = get_option (List.map
type_infos) ty in
554 Ast.rewrap
e (Ast.MetaExpr
(name
,constraints
,unitary,ty,form
,inherited))
555 | Ast.MetaExprList
(name
,Ast.MetaListLen
(lenname
,_
,_
),_
,_
) ->
556 (* lenname should have the same properties of being unitary or
558 let (unitary,inherited) = classify name
in
559 let (lenunitary
,leninherited
) = classify lenname
in
563 Ast.MetaListLen
(lenname
,lenunitary
,leninherited
),
565 | Ast.MetaExprList
(name
,lenname
,_
,_
) ->
566 (* lenname should have the same properties of being unitary or
568 let (unitary,inherited) = classify name
in
569 Ast.rewrap
e (Ast.MetaExprList
(name
,lenname
,unitary,inherited))
574 match Ast.unwrap
e with
575 Ast.MetaType
(name
,_
,_
) ->
576 let (unitary,inherited) = classify name
in
577 Ast.rewrap
e (Ast.MetaType
(name
,unitary,inherited))
582 match Ast.unwrap
e with
583 Ast.MetaInit
(name
,_
,_
) ->
584 let (unitary,inherited) = classify name
in
585 Ast.rewrap
e (Ast.MetaInit
(name
,unitary,inherited))
586 | Ast.MetaInitList
(name
,Ast.MetaListLen
(lenname
,_
,_
),_
,_
) ->
587 let (unitary,inherited) = classify name
in
588 let (lenunitary
,leninherited
) = classify lenname
in
591 (name
,Ast.MetaListLen
(lenname
,lenunitary
,leninherited
),
593 | Ast.MetaInitList
(name
,lenname
,_
,_
) ->
594 let (unitary,inherited) = classify name
in
595 Ast.rewrap
e (Ast.MetaInitList
(name
,lenname
,unitary,inherited))
600 match Ast.unwrap
e with
601 Ast.MetaParam
(name
,_
,_
) ->
602 let (unitary,inherited) = classify name
in
603 Ast.rewrap
e (Ast.MetaParam
(name
,unitary,inherited))
604 | Ast.MetaParamList
(name
,Ast.MetaListLen
(lenname
,_
,_
),_
,_
) ->
605 let (unitary,inherited) = classify name
in
606 let (lenunitary
,leninherited
) = classify lenname
in
609 (name
,Ast.MetaListLen
(lenname
,lenunitary
,leninherited
),
611 | Ast.MetaParamList
(name
,lenname
,_
,_
) ->
612 let (unitary,inherited) = classify name
in
613 Ast.rewrap
e (Ast.MetaParamList
(name
,lenname
,unitary,inherited))
618 match Ast.unwrap
e with
619 Ast.MetaDecl
(name
,_
,_
) ->
620 let (unitary,inherited) = classify name
in
621 Ast.rewrap
e (Ast.MetaDecl
(name
,unitary,inherited))
622 | Ast.MetaField
(name
,_
,_
) ->
623 let (unitary,inherited) = classify name
in
624 Ast.rewrap
e (Ast.MetaField
(name
,unitary,inherited))
625 | Ast.MetaFieldList
(name
,Ast.MetaListLen
(lenname
,_
,_
),_
,_
) ->
626 let (unitary,inherited) = classify name
in
627 let (lenunitary
,leninherited
) = classify lenname
in
630 (name
,Ast.MetaListLen
(lenname
,lenunitary
,leninherited
),
632 | Ast.MetaFieldList
(name
,lenname
,_
,_
) ->
633 let (unitary,inherited) = classify name
in
634 Ast.rewrap
e (Ast.MetaFieldList
(name
,lenname
,unitary,inherited))
637 let rule_elem r k
e =
639 match Ast.unwrap
e with
640 Ast.MetaStmt
(name
,_
,msi
,_
) ->
641 let (unitary,inherited) = classify name
in
642 Ast.rewrap
e (Ast.MetaStmt
(name
,unitary,msi
,inherited))
643 | Ast.MetaStmtList
(name
,_
,_
) ->
644 let (unitary,inherited) = classify name
in
645 Ast.rewrap
e (Ast.MetaStmtList
(name
,unitary,inherited))
649 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
650 donothing donothing donothing donothing donothing
651 ident expression donothing typeC init param decl rule_elem
652 donothing donothing donothing donothing in
654 List.map
fn.V.rebuilder_top_level minirules
656 (* ---------------------------------------------------------------- *)
658 (* For a minirule, collect the set of non-local (not in "bound") variables that
659 are referenced. Store them in a hash table. *)
661 (* bound means the metavariable was declared previously, not locally *)
663 (* Highly inefficient, because we call collect_all_refs on nested code
664 multiple times. But we get the advantage of not having too many variants
665 of the same functions. *)
667 (* Inherited doesn't include position constraints. If they are not bound
668 then there is no constraint. *)
670 let astfvs metavars bound
=
675 Ast.MetaFreshIdDecl
(_
,seed
) as x
->
676 ((Ast.get_meta_name x
),seed
)::prev
680 let collect_fresh l
=
681 let (matched
,freshvars
) =
683 (function (matched
,freshvars
) ->
685 try let v = List.assoc x
fresh in (matched
,(x
,v)::freshvars
)
686 with Not_found
-> (x
::matched
,freshvars
))
688 (List.rev matched
, List.rev freshvars
) in
690 (* cases for the elements of anything *)
691 let simple_setup getter k re
=
692 let minus_free = nub (getter
collect_all_refs re
) in
694 nub (getter
collect_non_constraint_refs re
) in
696 collect_fresh_seed metavars (getter
collect_in_plus_term re
) in
697 let free = Common.union_set
minus_free plus_free in
698 let nc_free = Common.union_set
minus_nc_free plus_free in
700 List.filter
(function x
-> not
(List.mem x bound
)) free in
702 List.filter
(function x
-> List.mem x bound
) nc_free in
704 List.filter
(function x
-> not
(List.mem x bound
)) minus_free in
705 let (matched
,fresh) = collect_fresh unbound in
707 Ast.free_vars
= matched
;
708 Ast.minus_free_vars
= munbound;
709 Ast.fresh_vars
= fresh;
710 Ast.inherited = inherited;
711 Ast.saved_witness
= []} in
713 let astfvrule_elem recursor k re
=
714 simple_setup (function x
-> x
.V.combiner_rule_elem
) k re
in
716 let astfvstatement recursor k s
=
717 let minus_free = nub (collect_all_refs.V.combiner_statement s
) in
719 nub (collect_non_constraint_refs.V.combiner_statement s
) in
721 collect_fresh_seed metavars
722 (collect_in_plus_term.V.combiner_statement s
) in
723 let free = Common.union_set
minus_free plus_free in
724 let nc_free = Common.union_set
minus_nc_free plus_free in
725 let classify free minus_free =
726 let (unbound,inherited) =
727 List.partition
(function x
-> not
(List.mem x bound
)) free in
729 List.filter
(function x
-> not
(List.mem x bound
)) minus_free in
730 let (matched
,fresh) = collect_fresh unbound in
731 (matched
,munbound,fresh,inherited) in
735 collect_fresh_seed metavars
736 (cip_mcodekind collect_in_plus_term aft
) in
737 match Ast.unwrap
res with
738 Ast.IfThen
(header
,branch
,(_
,_
,_
,aft
)) ->
739 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
740 Ast.IfThen
(header
,branch
,(unbound,fresh,inherited,aft
))
741 | Ast.IfThenElse
(header
,branch1
,els
,branch2
,(_
,_
,_
,aft
)) ->
742 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
743 Ast.IfThenElse
(header
,branch1
,els
,branch2
,
744 (unbound,fresh,inherited,aft
))
745 | Ast.While
(header
,body
,(_
,_
,_
,aft
)) ->
746 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
747 Ast.While
(header
,body
,(unbound,fresh,inherited,aft
))
748 | Ast.For
(header
,body
,(_
,_
,_
,aft
)) ->
749 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
750 Ast.For
(header
,body
,(unbound,fresh,inherited,aft
))
751 | Ast.Iterator
(header
,body
,(_
,_
,_
,aft
)) ->
752 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
753 Ast.Iterator
(header
,body
,(unbound,fresh,inherited,aft
))
756 let (matched
,munbound,fresh,_
) = classify free minus_free in
758 List.filter
(function x
-> List.mem x bound
) nc_free in
761 Ast.free_vars
= matched
;
762 Ast.minus_free_vars
= munbound;
763 Ast.fresh_vars
= fresh;
764 Ast.inherited = inherited;
765 Ast.saved_witness
= []} in
767 let astfvstatement_dots recursor k sd
=
768 simple_setup (function x
-> x
.V.combiner_statement_dots
) k sd
in
770 let astfvcase_line recursor k cl
=
771 simple_setup (function x
-> x
.V.combiner_case_line
) k cl
in
773 let astfvtoplevel recursor k tl
=
774 let saved = collect_saved.V.combiner_top_level tl
in
775 {(k tl
) with Ast.saved_witness
= saved} in
778 let donothing r k
e = k
e in
781 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
782 donothing donothing astfvstatement_dots donothing donothing
783 donothing donothing donothing donothing donothing donothing donothing
784 astfvrule_elem astfvstatement astfvcase_line astfvtoplevel donothing
787 let collect_astfvs rules =
788 let rec loop bound = function
790 | (metavars,(nm,rule_info,minirules))::rules ->
792 Common.minus_set bound (List.map Ast.get_meta_name metavars) in
794 (List.map (astfvs metavars bound).V.rebuilder_top_level minirules))::
795 (loop ((List.map Ast.get_meta_name metavars)@bound) rules) in
799 let collect_astfvs rules
=
800 let rec loop bound = function
802 | (metavars, rule
)::rules
->
804 Ast.ScriptRule
(_
,_
,_
,_
,script_vars
,_
) ->
805 (* why are metavars in rule, but outside for cocci rule??? *)
806 let bound = script_vars
@ bound in
807 rule
::(loop bound rules
)
808 | Ast.InitialScriptRule
(_
,_
,_
,_
) | Ast.FinalScriptRule
(_
,_
,_
,_
) ->
809 (* bound stays as is because script rules have no names, so no
810 inheritance is possible *)
811 rule
::(loop bound rules
)
812 | Ast.CocciRule
(nm
, rule_info
, minirules
, isexp
, ruletype
) ->
814 Common.minus_set
bound (List.map
Ast.get_meta_name
metavars) in
817 (List.map
(astfvs metavars bound).V.rebuilder_top_level
820 (loop ((List.map
Ast.get_meta_name
metavars)@bound) rules
) in
823 (* ---------------------------------------------------------------- *)
824 (* position variables that appear as a constraint on another position variable.
825 a position variable also cannot appear both positively and negatively in a
828 let get_neg_pos_list (_
,rule
) used_after_list
=
829 let donothing r k
e = k
e in
830 let bind (p1
,np1
) (p2
,np2
) =
831 (Common.union_set p1 p2
, Common.union_set np1 np2
) in
832 let option_default = ([],[]) in
833 let metaid (x
,_
,_
,_
) = x
in
838 Ast.MetaPos
(name
,constraints
,Ast.PER
,_
,_
) ->
839 ((metaid name
)::a
,constraints
@b
)
840 | Ast.MetaPos
(name
,constraints
,Ast.ALL
,_
,_
) ->
841 (a
,(metaid name
)::constraints
@b
)))
842 option_default (Ast.get_pos_var mc
) in
844 V.combiner
bind option_default
845 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
846 donothing donothing donothing donothing donothing
847 donothing donothing donothing donothing donothing donothing
848 donothing donothing donothing donothing donothing donothing in
850 Ast.CocciRule
(_
,_
,minirules
,_
,_
) ->
852 (function toplevel
->
853 let (positions
,neg_positions
) = v.V.combiner_top_level toplevel
in
854 (if List.exists
(function p -> List.mem
p neg_positions
) positions
857 "a variable cannot be used both as a position and a constraint");
860 | Ast.ScriptRule _
| Ast.InitialScriptRule _
| Ast.FinalScriptRule _
->
861 (*no negated positions*) []
863 (* ---------------------------------------------------------------- *)
865 (* collect used after lists, per minirule *)
867 (* defined is a list of variables that were declared in a previous metavar
870 (* Top-level used after: For each rule collect the set of variables that
871 are inherited, ie used but not defined. These are accumulated back to
872 their point of definition. *)
875 let collect_top_level_used_after metavar_rule_list
=
876 let drop_virt = List.filter
(function ("virtual",_
) -> false | _
-> true) in
877 let (used_after
,used_after_lists
) =
879 (function (metavar_list
,r
) ->
880 function (used_after
,used_after_lists
) ->
881 let locally_defined =
883 Ast.ScriptRule
(_
,_
,_
,_
,free_vars
,_
) -> free_vars
884 | _
-> List.map
Ast.get_meta_name metavar_list
in
885 let continue_propagation =
886 List.filter
(function x
-> not
(List.mem x
locally_defined))
890 Ast.ScriptRule
(_
,_
,_
,mv
,_
,_
) ->
891 drop_virt(List.map
(function (_
,(r
,v),_
) -> (r
,v)) mv
)
892 | Ast.InitialScriptRule
(_
,_
,_
,_
)
893 | Ast.FinalScriptRule
(_
,_
,_
,_
) -> []
894 | Ast.CocciRule
(_
,_
,rule
,_
,_
) ->
896 (Common.union_set
(nub (collect_all_rule_refs rule
))
897 (collect_in_plus metavar_list rule
)) in
899 List.filter
(function x
-> not
(List.mem x
locally_defined))
901 (Common.union_set
inherited continue_propagation,
902 used_after
::used_after_lists
))
903 metavar_rule_list
([],[]) in
904 match used_after
with
905 [] -> used_after_lists
908 (Printf.sprintf
"collect_top_level_used_after: unbound variables %s"
909 (String.concat
" " (List.map
(function (_
,x
) -> x
) used_after
)))
911 let collect_local_used_after metavars minirules used_after
=
912 let locally_defined = List.map
Ast.get_meta_name
metavars in
913 let rec loop = function
914 [] -> (used_after
,[],[],[],[])
916 (* In a rule there are three kinds of local variables:
917 1. Variables referenced in the minus or context code.
918 These get a value by matching. This value can be used in
920 2. Fresh variables referenced in the plus code.
921 3. Variables referenced in the seeds of the fresh variables.
922 There are also non-local variables. These may either be variables
923 referenced in the minus, context, or plus code, or they may be
924 variables referenced in the seeds of the fresh variables. *)
925 (* Step 1: collect all references in minus/context, plus, seed
927 let variables_referenced_in_minus_context_code =
928 nub (collect_all_minirule_refs minirule
) in
929 let variables_referenced_in_plus_code =
930 collect_in_plus_term.V.combiner_top_level minirule
in
931 let (env_of_fresh_seeds
,seeds_and_plus
) =
932 collect_fresh_seed_env
933 metavars variables_referenced_in_plus_code in
935 Common.union_set
variables_referenced_in_minus_context_code
937 (* Step 2: identify locally defined ones *)
938 let local_fresh = List.map fst env_of_fresh_seeds
in
940 List.partition
(function x
-> List.mem x
locally_defined) in
941 let local_env_of_fresh_seeds =
942 (* these have to be restricted to only one value if the associated
943 fresh variable is used after *)
944 List.map
(function (f
,ss
) -> (f
,is_local ss
)) env_of_fresh_seeds
in
945 let (local_all_free_vars
,nonlocal_all_free_vars
) =
946 is_local all_free_vars in
947 (* Step 3, recurse on the rest of the rules, making available whatever
948 has been defined in this one *)
949 let (mini_used_after
,fvs_lists
,mini_used_after_lists
,
950 mini_fresh_used_after_lists
,mini_fresh_used_after_seeds
) =
952 (* Step 4: collect the results. These are:
953 1. All of the variables used non-locally in the rules starting
955 2. All of the free variables to the end of the semantic patch
956 3. The variables that are used afterwards and defined here by
957 matching (minus or context code)
958 4. The variables that are used afterwards and are defined here as
960 5. The variables that are used as seeds in computing the bindings
961 of the variables collected in part 4. *)
962 let (local_used_after
, nonlocal_used_after
) =
963 is_local mini_used_after
in
964 let (fresh_local_used_after
(*4*),matched_local_used_after
) =
965 List.partition
(function x
-> List.mem x
local_fresh)
967 let matched_local_used_after(*3*) =
968 Common.union_set
matched_local_used_after nonlocal_used_after
in
969 let new_used_after = (*1*)
970 Common.union_set nonlocal_all_free_vars nonlocal_used_after
in
971 let fresh_local_used_after_seeds =
973 (* no point to keep variables that already are gtd to have only
975 (function x
-> not
(List.mem x
matched_local_used_after))
976 (List.fold_left
(function p -> function c
-> Common.union_set c
p)
980 fst
(List.assoc fua
local_env_of_fresh_seeds))
981 fresh_local_used_after
)) in
982 (new_used_after,all_free_vars::fvs_lists
(*2*),
983 matched_local_used_after::mini_used_after_lists
,
984 fresh_local_used_after
::mini_fresh_used_after_lists
,
985 fresh_local_used_after_seeds::mini_fresh_used_after_seeds
) in
986 let (_
,fvs_lists
,used_after_lists
(*ua*),
987 fresh_used_after_lists
(*fua*),fresh_used_after_lists_seeds
(*fuas*)) =
989 (fvs_lists
,used_after_lists
,
990 fresh_used_after_lists
,fresh_used_after_lists_seeds
)
994 let collect_used_after metavar_rule_list
=
995 let used_after_lists = collect_top_level_used_after metavar_rule_list
in
997 (function (metavars,r
) ->
998 function used_after
->
1000 Ast.ScriptRule
(_
,_
,_
,_
,_
,_
) (* no minirules, so nothing to do? *)
1001 | Ast.InitialScriptRule
(_
,_
,_
,_
) | Ast.FinalScriptRule
(_
,_
,_
,_
) ->
1002 ([], [used_after
], [[]], [])
1003 | Ast.CocciRule
(name
, rule_info
, minirules
, _
,_
) ->
1004 collect_local_used_after metavars minirules used_after
1006 metavar_rule_list
used_after_lists
1008 let rec split4 = function
1010 | (a
,b
,c
,d
)::l
-> let (a1
,b1
,c1
,d1
) = split4 l
in (a
::a1
,b
::b1
,c
::c1
,d
::d1
)
1012 (* ---------------------------------------------------------------- *)
1015 let free_vars rules
=
1016 let metavars = List.map
(function (mv
,rule
) -> mv
) rules
in
1017 let (fvs_lists
,used_after_matched_lists
,
1018 fresh_used_after_lists
,fresh_used_after_lists_seeds
) =
1019 split4 (collect_used_after rules
) in
1021 List.map2
get_neg_pos_list rules used_after_matched_lists
in
1022 let positions_list = (* for all rules, assume all positions are used after *)
1024 (function (mv
, r
) ->
1026 Ast.ScriptRule _
(* doesn't declare position variables *)
1027 | Ast.InitialScriptRule _
| Ast.FinalScriptRule _
-> []
1028 | Ast.CocciRule
(_
,_
,rule
,_
,_
) ->
1032 function Ast.MetaPosDecl
(_
,nm
) -> nm
::prev
| _
-> prev
)
1034 List.map
(function _
-> positions) rule
)
1039 function (ua
,fua
) ->
1042 | Ast.InitialScriptRule _
| Ast.FinalScriptRule _
-> r
1043 | Ast.CocciRule
(nm
, rule_info
, r
, is_exp
,ruletype
) ->
1046 classify_variables mv r
1047 ((List.concat ua
) @ (List.concat fua
)),
1049 rules
(List.combine used_after_matched_lists fresh_used_after_lists
) in
1050 let new_rules = collect_astfvs (List.combine
metavars new_rules) in
1051 (metavars,new_rules,
1052 fvs_lists
,neg_pos_lists,
1053 (used_after_matched_lists
,
1054 fresh_used_after_lists
,fresh_used_after_lists_seeds
),