2 * Copyright (C) 2010, University of Copenhagen DIKU and INRIA.
3 * Copyright (C) 2006, 2007 Julia Lawall
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License (GPL)
7 * version 2 as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * file license.txt for more details.
14 * This file was part of Coccinelle.
17 module Ast
= Ast_cocci
18 module V
= Visitor_ast
22 (Printf.sprintf
"unparse_hrule: line: %d, %s" (Ast.get_line x
) s
)
24 let names = ref ([] : (string * int ref) list
)
26 let started_files = ref ([] : (string * bool) list
)
27 let typedefs = ref ([] : (string * string list
ref) list
)
28 let current_outfile = ref ""
30 let prefix = "_cocci_"
32 (* ----------------------------------------------------------------------- *)
33 (* Create rule to check for header include *)
35 let print_header_rule pr srcfile
=
36 match Str.split
(Str.regexp
"/") srcfile
with
38 pr
"@header@\n@@\n\n#include \"";
39 pr x
; pr
"\"\n\n"; true
41 let rec loop = function
44 pr
"@header@\n@@\n\n#include \"";
45 pr x
; pr
"\"\n\n"; true
46 | "include"::(x
::xs
) ->
47 pr
"@header@\n@@\n\n#include <";
49 if Str.string_match
(Str.regexp
"asm-") x 0 then "asm" else x in
50 pr
(String.concat
"/" (x::xs
));
55 (* ----------------------------------------------------------------------- *)
56 (* Print check that we are not in the defining function *)
58 let print_check_rule pr function_name function_name_count header_req
=
60 then pr
(Printf.sprintf
"@same_%s depends on header@\n" function_name_count
)
61 else pr
(Printf.sprintf
"@same_%s@\n" function_name_count
));
64 pr function_name
; pr
"@p(...) { ... }\n\n"
66 (* ----------------------------------------------------------------------- *)
67 (* get parameters of the matched function *)
69 let rec env_lookup fn
= function
70 [] -> failwith
"no binding"
71 | (nm
,vl
)::rest
when fn nm
-> vl
72 | _
::rest
-> env_lookup fn rest
75 let argname = ref ("","") in
76 let fn ((_
,nm
) as name
) =
78 then (argname := name
; true)
80 match env_lookup fn env
with
81 Ast_c.MetaParamListVal
(paramst
) -> (paramst
,!argname)
82 | _
-> failwith
"not possible"
84 let get_function_name rule env
=
85 let donothing r k e
= k e
in
86 let option_default = [] in
87 let bind = Common.union_set
in
88 let do_any_list_list r any_list_list
=
91 (function prev
-> function cur
->
92 bind (r
.V.combiner_anything cur
) prev
))
95 match Ast.get_mcodekind mc
with
96 Ast.MINUS
(_
,_
,_
,any_list_list
) -> do_any_list_list r any_list_list
97 | Ast.CONTEXT
(_
,any_befaft
) ->
98 (match any_befaft
with
99 Ast.BEFORE
(any_list_list
,_
) | Ast.AFTER
(any_list_list
,_
) ->
100 do_any_list_list r any_list_list
101 | Ast.BEFOREAFTER
(ba
,aa
,_
) ->
102 bind (do_any_list_list r ba
) (do_any_list_list r aa
)
104 | Ast.PLUS _
-> [] in
105 let expression r k e
=
107 (match Ast.unwrap e
with
108 Ast.FunCall
(fn,lp
,args
,rp
) ->
109 (match Ast.undots args
with
111 (match Ast.unwrap e
with
112 Ast.MetaExprList
(nm
,_
,_
,_
) ->
113 (match (Ast.unwrap_mcode nm
,Ast.get_mcodekind nm
) with
114 ((_
,"ARGS"), Ast.PLUS _
) ->
115 (match Ast.unwrap
fn with
117 (match Ast.unwrap id
with
119 | Ast.MetaFunc
(nm
,_
,_
,_
)
120 | Ast.MetaLocalFunc
(nm
,_
,_
,_
) ->
121 [Ast.unwrap_mcode nm
]
129 (V.combiner
bind option_default
130 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
131 donothing donothing donothing donothing
132 donothing expression donothing donothing donothing donothing donothing
133 donothing donothing donothing donothing donothing).V.combiner_top_level
137 (match env_lookup (function nm
-> nm
= name
) env
with
138 Ast_c.MetaIdVal
(s
,_
) | Ast_c.MetaFuncVal
(s
)
139 | Ast_c.MetaLocalFuncVal
(s
) -> s
140 | _
-> error rule
"not possible")
141 | _
-> error rule
"inconsistent rule generation"
143 (* ----------------------------------------------------------------------- *)
144 (* Print metavariable declarations *)
146 let rec print_typedef pr
= function
147 (Ast_c.TypeName
(name
,_
),_
) ->
148 let s = Ast_c.str_of_name name
in
150 try List.assoc
!current_outfile !typedefs
153 typedefs := (!current_outfile,td)::!typedefs;
155 if not
(List.mem
s !typedefs)
156 then (typedefs := s::!typedefs; pr
"typedef "; pr
s; pr
";\n")
157 | (Ast_c.Pointer
(_
,ty
),_
) -> print_typedef pr ty
160 let rewrap_str s ii
=
161 {ii
with Ast_c.pinfo
=
162 (match ii
.Ast_c.pinfo
with
163 Ast_c.OriginTok pi
->
164 Ast_c.OriginTok
{ pi
with Common.str
= s;}
165 | Ast_c.ExpandedTok
(pi
,vpi
) ->
166 Ast_c.ExpandedTok
({ pi
with Common.str
= s;},vpi
)
167 | Ast_c.FakeTok
(_
,vpi
) -> Ast_c.FakeTok
(s,vpi
)
168 | Ast_c.AbstractLineTok pi
->
169 Ast_c.AbstractLineTok
{ pi
with Common.str
= s;})}
171 let rewrap_prefix_name prefix name
=
173 | Ast_c.RegularName
(s, iiname
) ->
174 let iis = Common.tuple_of_list1 iiname
in
175 let iis'
= rewrap_str (prefix^
s) iis in
176 Ast_c.RegularName
(prefix ^
s, [iis'
])
177 | Ast_c.CppConcatenatedName _
| Ast_c.CppVariadicName _
178 | Ast_c.CppIdentBuilder _
182 let print_metavar pr
= function
183 | {Ast_c.p_namei
= Some name
;
184 p_type
= (_
,(Ast_c.Pointer
(_
,(Ast_c.BaseType
(Ast_c.Void
),_
)),_
));
187 let param = Ast_c.str_of_name name
in
188 pr
("expression "^
prefix); pr
param
189 | ({Ast_c.p_namei
= Some name
; p_type
= (_
,ty
)} : Ast_c.parameterType
) ->
191 let name'
= rewrap_prefix_name prefix name in
195 Pretty_print_c.pp_param_gen
197 let str = Ast_c.str_of_info
x in
198 if not
(List.mem
str ["const";"volatile"])
200 (function _
-> pr
" ")
201 {Ast_c.p_register
= (false,[]);
202 p_namei
= Some
name'
;
203 p_type
= (({Ast_c.const
= false; Ast_c.volatile
= false},[]),ty
)
205 | _
-> failwith
"function must have named parameters"
207 let make_exp = function
208 ({Ast_c.p_namei
= Some
name; p_type
= ty
}, comma_ii
) ->
209 let no_info = (None
,Ast_c.NotTest
) in
211 let name'
= rewrap_prefix_name prefix name in
214 ((Ast_c.Ident
(name'
),ref no_info),Ast_c.noii
) in
215 (name,(Common.Left
exp,comma_ii
))
216 | _
-> failwith
"bad parameter"
218 let print_extra_typedefs pr env
=
220 { Visitor_c.default_visitor_c
with
221 Visitor_c.ktype
= (fun (k
, bigf) ty
->
223 (_
,((Ast_c.TypeName
(_
,_
),_
) as ty
)) -> print_typedef pr ty
228 Ast_c.MetaIdVal
(_
) | Ast_c.MetaFuncVal
(_
)
229 | Ast_c.MetaLocalFuncVal
(_
) -> ()
230 | Ast_c.MetaExprVal
(exp,_
) -> Visitor_c.vk_expr
bigf exp
231 | Ast_c.MetaExprListVal
(args
) -> Visitor_c.vk_argument_list
bigf args
232 | Ast_c.MetaParamVal
(param) -> Visitor_c.vk_param
bigf param
233 | Ast_c.MetaParamListVal
(params
) -> Visitor_c.vk_param_list
bigf params
235 | Ast_c.MetaTypeVal
(ty
) -> Visitor_c.vk_type
bigf ty
236 | Ast_c.MetaInitVal
(ty
) -> Visitor_c.vk_ini
bigf ty
237 | Ast_c.MetaStmtVal
(stm
) -> Visitor_c.vk_statement
bigf stm
238 | Ast_c.MetaPosVal _
| Ast_c.MetaPosValList _
239 | Ast_c.MetaListlenVal _
-> ())
242 let rename argids env
=
243 let argenv = List.map
(function name ->
244 let arg = Ast_c.str_of_name
name in
247 let lookup x = try List.assoc
x argenv with Not_found
-> x in
249 { Visitor_c.default_visitor_c_s
with
250 Visitor_c.kexpr_s
= (fun (k
,bigf) e
->
252 ((Ast_c.Ident
(name), info
), []) ->
254 (* pad: assert is_regular_ident ? *)
255 let s = Ast_c.str_of_name
name in
256 let ii = Ast_c.info_of_name
name in
257 let new_name = lookup s in
258 let new_id = Ast_c.RegularName
(new_name, [rewrap_str new_name ii]) in
259 ((Ast_c.Ident
(new_id), info
), Ast_c.noii
)
265 Ast_c.MetaIdVal
(_
) | Ast_c.MetaFuncVal
(_
)
266 | Ast_c.MetaLocalFuncVal
(_
) -> vl
267 | Ast_c.MetaExprVal
(exp,c
) ->
268 Ast_c.MetaExprVal
(Visitor_c.vk_expr_s
bigf exp,c
)
269 | Ast_c.MetaExprListVal
(args
) ->
270 Ast_c.MetaExprListVal
(Visitor_c.vk_arguments_s
bigf args
)
271 | Ast_c.MetaParamVal
(param) ->
272 Ast_c.MetaParamVal
(Visitor_c.vk_param_s
bigf param)
273 | Ast_c.MetaParamListVal
(params
) ->
274 Ast_c.MetaParamListVal
(Visitor_c.vk_params_s
bigf params
)
276 | Ast_c.MetaTypeVal
(ty
) ->
277 Ast_c.MetaTypeVal
(Visitor_c.vk_type_s
bigf ty
)
278 | Ast_c.MetaInitVal
(ini
) ->
279 Ast_c.MetaInitVal
(Visitor_c.vk_ini_s
bigf ini
)
280 | Ast_c.MetaStmtVal
(stm
) ->
281 Ast_c.MetaStmtVal
(Visitor_c.vk_statement_s
bigf stm
)
282 | Ast_c.MetaPosVal _
| Ast_c.MetaPosValList _
283 | Ast_c.MetaListlenVal _
-> vl
))
286 let print_one_type pr env
= function
287 (Type_cocci.MetaType
(name,keep
,inherited
)) as ty
->
289 match List.assoc
name env
with
290 Ast_c.MetaTypeVal ty
->
291 Pretty_print_c.pp_type_gen
292 (function x -> pr
(Ast_c.str_of_info
x))
293 (function _
-> pr
" ")
295 | _
-> failwith
"impossible"
296 with Not_found
-> pr
(Type_cocci.type2c ty
))
297 | ty
-> pr
(Type_cocci.type2c ty
)
299 let print_types pr env
= function
301 | Some
[ty
] -> print_one_type pr env ty
304 Common.print_between
(function _
-> pr
", ") (print_one_type pr env
)
308 let pp_meta_decl pr env decl
=
309 let no_arity = function Ast.NONE
-> () | _
-> failwith
"no arity allowed" in
310 let pp_name (_
,n
) = pr n
in
312 Ast.MetaIdDecl
(ar
, name) ->
314 no_arity ar
; pr
"identifier "; pp_name name; pr
";\n"
315 | Ast.MetaFreshIdDecl
(name, Ast.NoVal
) ->
316 pr
"fresh identifier "; pp_name name; pr
";\n"
317 | Ast.MetaFreshIdDecl
(name, Ast.StringSeed
x) ->
318 pr
"fresh identifier "; pp_name name; pr
" = \""; pr
x; pr
"\";\n"
319 | Ast.MetaFreshIdDecl
(name, Ast.ListSeed
x) ->
320 failwith
"unparse_hrule: not supported"
321 | Ast.MetaTypeDecl
(ar
, name) ->
322 no_arity ar
; pr
"type "; pp_name name; pr
";\n"
323 | Ast.MetaInitDecl
(ar
, name) ->
324 no_arity ar
; pr
"initialiser "; pp_name name; pr
";\n"
325 | Ast.MetaListlenDecl
(name) -> ()
326 | Ast.MetaParamDecl
(ar
, name) ->
327 no_arity ar
; pr
"parameter "; pp_name name; pr
";\n"
328 | Ast.MetaParamListDecl
(ar
, name, None
) ->
329 no_arity ar
; pr
"parameter list "; pp_name name; pr
";\n"
330 | Ast.MetaParamListDecl
(ar
, name, Some len
) ->
331 no_arity ar
; pr
"parameter list "; pp_name name;
332 pr
"["; pp_name len
; pr
"]"; pr
";\n"
333 | Ast.MetaConstDecl
(ar
, name, types
) ->
334 no_arity ar
; pr
"constant "; print_types pr env types
;
335 pp_name name; pr
";\n"
336 | Ast.MetaErrDecl
(ar
, name) ->
337 no_arity ar
; pr
"error "; pp_name name; pr
";\n"
338 | Ast.MetaExpDecl
(ar
, name, None
) ->
339 no_arity ar
; pr
"expression "; pp_name name; pr
";\n"
340 | Ast.MetaExpDecl
(ar
, name, types
) ->
341 no_arity ar
; print_types pr env types
; pp_name name; pr
";\n"
342 | Ast.MetaIdExpDecl
(ar
, name, types
) ->
343 no_arity ar
; pr
"idexpression ";
344 print_types pr env types
; pp_name name; pr
";\n"
345 | Ast.MetaLocalIdExpDecl
(ar
, name, types
) ->
346 no_arity ar
; pr
"local idexpression ";
347 print_types pr env types
; pp_name name; pr
";\n"
348 | Ast.MetaExpListDecl
(ar
, name, None
) ->
349 no_arity ar
; pr
"parameter list "; pp_name name; pr
";\n"
350 | Ast.MetaExpListDecl
(ar
, name, Some len
) ->
351 no_arity ar
; pr
"parameter list ";
352 pp_name name; pr
"["; pp_name len
; pr
"]"; pr
";\n"
353 | Ast.MetaStmDecl
(ar
, name) ->
354 no_arity ar
; pr
"statement "; pp_name name; pr
";\n"
355 | Ast.MetaStmListDecl
(ar
, name) ->
356 no_arity ar
; pr
"statement list "; pp_name name; pr
";\n"
357 | Ast.MetaFuncDecl
(ar
, name) ->
358 no_arity ar
; pr
"function "; pp_name name; pr
";\n"
359 | Ast.MetaLocalFuncDecl
(ar
, name) ->
360 no_arity ar
; pr
"local function "; pp_name name; pr
";\n"
361 | Ast.MetaPosDecl
(ar
, name) ->
362 no_arity ar
; pr
"position "; pp_name name; pr
";\n"
363 | Ast.MetaDeclarerDecl
(ar
, name) ->
364 no_arity ar
; pr
"declarer "; pp_name name; pr
";\n"
365 | Ast.MetaIteratorDecl
(ar
, name) ->
366 no_arity ar
; pr
"iterator "; pp_name name; pr
";\n"
368 let print_metavariables pr local_metas paramst env header_req function_name
=
370 then pr
"@depends on header@\n"
372 pr
(Printf.sprintf
"position _p!=same_%s.p;\n" function_name
);
373 pr
"identifier _f;\n";
374 let rec loop = function
375 [] | [{Ast_c.p_type
=(_
,(Ast_c.BaseType
(Ast_c.Void
),_
))},_
] -> []
376 | ((first
,_
) as f
)::rest
->
377 print_metavar pr first
; pr
";\n";
378 (make_exp f
) :: loop rest
in
379 let args = loop paramst
in
380 print_extra_typedefs pr env
;
381 List.iter
(pp_meta_decl pr env
) local_metas
;
385 (* ----------------------------------------------------------------------- *)
386 (* print_start/end *)
389 pr
"_f@_p(...) { <+...\n"
394 (* ----------------------------------------------------------------------- *)
395 (* Print call to the defined function *)
397 let print_param_name pr
= function
398 {Ast_c.p_namei
= Some
name} -> pr
(Ast_c.str_of_name
name)
399 | _
-> failwith
"function must have named parameters"
401 let pp_def_gen pr defn isexp
=
402 let {Ast_c.f_name
= name; f_type
= (_
, (paramst
, (b
, iib
))); } = defn
in
403 pr
(Ast_c.str_of_name
name); pr
"(";
404 (if b
then failwith
"not handling variable argument functions");
406 [] | [{Ast_c.p_type
= (_
,(Ast_c.BaseType
(Ast_c.Void
),_
))},_
] -> ()
408 print_param_name pr first
;
409 List.iter
(function (x,_
) -> pr
", "; print_param_name pr
x) rest
);
410 pr
")"; if not isexp
then pr
";"
412 (* ----------------------------------------------------------------------- *)
415 let pp_rule local_metas ast env srcfile
=
416 let (paramst
,args_name
) = get_paramst env
in
417 (* get rule information *)
418 let (rule
,printable
) =
420 Ast.CocciRule
(_
,_
,[body
],_
,_
) -> (* could extend to use attributes *)
422 match Ast.unwrap body
with
423 Ast.DECL
(s) -> [[Ast.StatementTag
s]]
424 | Ast.CODE
(ss
) -> [[Ast.StmtDotsTag ss
]]
425 | _
-> error body
"bad rule body")
426 | _
-> failwith
"bad rule" in
427 (* create the output file *)
429 match !Flag.make_hrule
with
430 Some
outdir -> outdir
431 | None
-> error rule
"not possible" in
432 let function_name = get_function_name rule env
in
433 let function_name_count =
435 let cell = List.assoc
function_name !names in
438 function_name ^
(string_of_int
ct)
441 names := (function_name,cell) :: !names;
443 let outfile = outdir ^
"/" ^
444 (if !Flag.hrule_per_file
445 then Filename.chop_extension
(Filename.basename srcfile
)
446 else function_name_count) in
447 let escape_re = Str.regexp_string
"/" in
448 let dir = if !Flag.dir = "" then Filename.dirname srcfile
else !Flag.dir in
449 let outdirfile = Str.global_replace
escape_re "_"dir in
450 let outfile = outfile ^
outdirfile ^
".cocci" in
451 let saved_header_req =
452 try let res = List.assoc
outfile !started_files in Some
res
453 with Not_found
-> None
in
454 current_outfile := outfile;
455 Common.with_open_outfile_append
outfile (fun (pr
,chan
) ->
457 match saved_header_req with
460 let res = print_header_rule pr srcfile
in
461 started_files := (outfile,res)::!started_files;
463 print_check_rule pr
function_name function_name_count header_req;
465 print_metavariables pr local_metas paramst env
header_req
466 function_name_count in
467 let (argids
,args) = List.split
args in
468 let env = rename argids
env in
469 let env = (args_name
,Ast_c.MetaExprListVal
args)::env in
471 (* for printing C tokens *)
473 match Ast_c.pinfo_of_info info
with
474 Ast_c.AbstractLineTok _
-> pr
(Ast_c.str_of_info info
)
475 | Ast_c.FakeTok
(s,_
) -> pr
s
477 Printf.printf
"line: %s\n" (Common.dump info
);
478 error rule
"not an abstract line" in
479 let pr_space _
= pr
" " in
480 Unparse_cocci.pp_list_list_any
481 ([env], (fun s _ _ _ _
-> pr
s), pr_c, pr_space, pr_space, pr
,
482 (fun _ _
-> ()), (function _
-> ()), (function _
-> ()))
483 true printable
Unparse_cocci.InPlace
;