3 # Copyright (C) 2013-2024 Free Software Foundation, Inc.
5 # This file is part of GDB.
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
21 # make-target-delegates.py
24 from typing
import Dict
, List
, TextIO
28 # The line we search for in target.h that marks where we should start
29 # looking for methods.
30 TRIGGER
= re
.compile(r
"^struct target_ops$")
31 # The end of the methods part.
32 ENDER
= re
.compile(r
"^\s*};$")
35 SYMBOL
= "[a-zA-Z_][a-zA-Z0-9_]*"
36 # Match the name part of a method in struct target_ops.
37 NAME_PART
= r
"(?P<name>" + SYMBOL
+ r
")\s"
38 # Match the arguments to a method.
39 ARGS_PART
= r
"(?P<args>\(.*\))"
40 # We strip the indentation so here we only need the caret.
43 POINTER_PART
= r
"\s*(\*|\&)?\s*"
45 # Match a C++ symbol, including scope operators and template
46 # parameters. E.g., 'std::vector<something>'.
47 CP_SYMBOL
= r
"[a-zA-Z_][a-zA-Z0-9_<>:]*"
48 # Match the return type when it is "ordinary".
49 SIMPLE_RETURN_PART
= r
"((struct|class|enum|union)\s+)?" + CP_SYMBOL
51 # Match a return type.
52 RETURN_PART
= r
"((const|volatile)\s+)?(" + SIMPLE_RETURN_PART
+ ")" + POINTER_PART
55 VIRTUAL_PART
= r
"virtual\s"
57 # Match the TARGET_DEFAULT_* attribute for a method.
58 TARGET_DEFAULT_PART
= r
"TARGET_DEFAULT_(?P<style>[A-Z_]+)\s*\((?P<default_arg>.*)\)"
60 # Match the arguments and trailing attribute of a method definition.
61 # Note we don't match the trailing ";".
62 METHOD_TRAILER
= r
"\s*" + TARGET_DEFAULT_PART
+ "$"
64 # Match an entire method definition.
76 # Space-separated symbols.
77 CP_SYMBOLS
= CP_SYMBOL
+ r
"(\s+" + CP_SYMBOL
+ r
")*"
79 # Regular expression used to dissect argument types.
80 ARGTYPES
= re
.compile(
92 # Match TARGET_DEBUG_PRINTER in an argument type.
93 # This must match the whole "sub-expression" including the parens.
94 TARGET_DEBUG_PRINTER
= r
"\s*TARGET_DEBUG_PRINTER\s*\((?P<arg>[^)]*)\)\s*"
99 self
, argtypes
: List
[str], return_type
: str, style
: str, default_arg
: str
101 self
.argtypes
= argtypes
102 self
.return_type
= return_type
104 self
.default_arg
= default_arg
108 found_trigger
= False
110 with
open("target.h", "r") as target_h
:
111 for line
in target_h
:
113 if not found_trigger
:
114 if TRIGGER
.match(line
):
117 # Skip the open brace.
119 elif ENDER
.match(line
):
123 line
= re
.split("//", line
)[0]
124 all_the_text
= all_the_text
+ " " + line
125 if not found_trigger
:
126 raise RuntimeError("Could not find trigger line")
127 # Now strip out the C comments.
128 all_the_text
= re
.sub(r
"/\*(.*?)\*/", "", all_the_text
)
129 # Replace sequences whitespace with a single space character.
130 # We need the space because the method may have been split
131 # between multiple lines, like e.g.:
133 # virtual std::vector<long_type_name>
134 # my_long_method_name ()
135 # TARGET_DEFAULT_IGNORE ();
137 # If we didn't preserve the space, then we'd end up with:
139 # virtual std::vector<long_type_name>my_long_method_name ()TARGET_DEFAULT_IGNORE ()
141 # ... which wouldn't later be parsed correctly.
142 all_the_text
= re
.sub(r
"\s+", " ", all_the_text
)
143 return all_the_text
.split(";")
146 # Parse arguments into a list.
147 def parse_argtypes(typestr
: str):
148 # Remove the outer parens.
149 typestr
= re
.sub(r
"^\((.*)\)$", r
"\1", typestr
)
150 result
: list[str] = []
151 for item
in re
.split(r
",\s*", typestr
):
155 m
= ARGTYPES
.match(item
)
158 onetype
= m
.group("E")
160 onetype
= m
.group("T")
163 result
.append(onetype
.strip())
167 # Write function header given name, return type, and argtypes.
168 # Returns a list of actual argument names.
169 def write_function_header(
170 f
: TextIO
, decl
: bool, name
: str, return_type
: str, argtypes
: List
[str]
172 print(return_type
, file=f
, end
="")
174 if not return_type
.endswith("*"):
175 print(" ", file=f
, end
="")
178 print(name
+ " (", file=f
, end
="")
179 argdecls
: list[str] = []
180 actuals
: list[str] = []
181 for i
in range(len(argtypes
)):
182 val
= re
.sub(TARGET_DEBUG_PRINTER
, "", argtypes
[i
])
183 if not val
.endswith("*") and not val
.endswith("&"):
185 vname
= "arg" + str(i
)
188 actuals
.append(vname
)
189 print(", ".join(argdecls
) + ")", file=f
, end
="")
191 print(" override;", file=f
)
197 # Write out a declaration.
198 def write_declaration(f
: TextIO
, name
: str, return_type
: str, argtypes
: List
[str]):
199 write_function_header(f
, True, name
, return_type
, argtypes
)
202 # Write out a delegation function.
203 def write_delegator(f
: TextIO
, name
: str, return_type
: str, argtypes
: List
[str]):
205 names
= write_function_header(
206 f
, False, "target_ops::" + name
, return_type
, argtypes
208 print(" ", file=f
, end
="")
209 if return_type
!= "void":
210 print("return ", file=f
, end
="")
211 print("this->beneath ()->" + name
+ " (", file=f
, end
="")
212 print(", ".join(names
), file=f
, end
="")
217 # Write out a default function.
227 name
= "dummy_target::" + name
228 names
= write_function_header(f
, False, name
, return_type
, argtypes
)
230 print(" ", file=f
, end
="")
231 if return_type
!= "void":
232 print("return ", file=f
, end
="")
233 print(content
+ " (", file=f
, end
="")
234 names
.insert(0, "this")
235 print(", ".join(names
) + ");", file=f
)
236 elif style
== "RETURN":
237 print(" return " + content
+ ";", file=f
)
238 elif style
== "NORETURN":
239 print(" " + content
+ ";", file=f
)
240 elif style
== "IGNORE":
244 raise RuntimeError("unrecognized style: " + style
)
248 def munge_type(typename
: str):
249 m
= re
.search(TARGET_DEBUG_PRINTER
, typename
)
251 return m
.group("arg")
252 typename
= typename
.rstrip()
253 # There's no reason to have these keywords in the name, and their
254 # presence makes it harder to change styles.
255 typename
= re
.sub("\\b(struct|enum|class|union) ", "", typename
)
256 typename
= re
.sub("[ ()<>:]", "_", typename
)
257 typename
= re
.sub("[*]", "p", typename
)
258 typename
= re
.sub("&", "r", typename
)
259 # Identifiers with double underscores are reserved to the C++
261 typename
= re
.sub("_+", "_", typename
)
262 # Avoid ending the function name with underscore, for
263 # cosmetics. Trailing underscores appear after munging types
264 # with template parameters, like e.g. "foo<int>".
265 typename
= re
.sub("_+$", "", typename
)
266 return "target_debug_print_" + typename
269 # Write out a debug method.
270 def write_debugmethod(
271 f
: TextIO
, content
: str, name
: str, return_type
: str, argtypes
: List
[str]
274 debugname
= "debug_target::" + name
275 names
= write_function_header(f
, False, debugname
, return_type
, argtypes
)
277 f
' target_debug_printf_nofunc ("-> %s->{name} (...)", this->beneath ()->shortname ());',
281 # Delegate to the beneath target.
282 if return_type
!= "void":
283 print(" " + return_type
+ " result", file=f
)
284 print(" = ", file=f
, end
="")
286 print(" ", file=f
, end
="")
287 print("this->beneath ()->" + name
+ " (", file=f
, end
="")
288 print(", ".join(names
), file=f
, end
="")
291 # Generate the debug printf call.
292 args_fmt
= ", ".join(["%s"] * len(argtypes
))
295 (",\n\t {printer} (arg{i}).c_str ()").format(
296 printer
=munge_type(t
), i
=i
298 for i
, t
in enumerate(argtypes
)
302 if return_type
!= "void":
304 ret
= ",\n\t {printer} (result).c_str ()".format(
305 printer
=munge_type(return_type
)
312 f
' target_debug_printf_nofunc ("<- %s->{name} ({args_fmt}){ret_fmt}",\n'
313 f
"\t this->beneath ()->shortname (){args}{ret});",
317 if return_type
!= "void":
318 print(" return result;", file=f
)
326 delegators
: List
[str],
327 entries
: Dict
[str, Entry
],
330 print("struct " + class_name
+ " : public target_ops", file=f
)
332 print(" const target_info &info () const override;", file=f
)
334 print(" strata stratum () const override;", file=f
)
337 for name
in delegators
:
338 print(" ", file=f
, end
="")
339 entry
= entries
[name
]
340 write_declaration(f
, name
, entry
.return_type
, entry
.argtypes
)
345 delegators
: List
[str] = []
346 entries
: Dict
[str, Entry
] = {}
348 for current_line
in scan_target_h():
349 # See comments in scan_target_h. Here we strip away the leading
350 # and trailing whitespace.
351 current_line
= current_line
.strip()
352 m
= METHOD
.match(current_line
)
357 argtypes
= parse_argtypes(data
["args"])
358 return_type
= data
["return_type"].strip()
359 style
= data
["style"]
360 default_arg
= data
["default_arg"]
361 entries
[name
] = Entry(argtypes
, return_type
, style
, default_arg
)
363 delegators
.append(name
)
365 with
open("target-delegates-gen.c", "w") as f
:
367 gdbcopyright
.copyright(
368 "make-target-delegates.py", "Boilerplate target methods for GDB"
372 print_class(f
, "dummy_target", delegators
, entries
)
373 print_class(f
, "debug_target", delegators
, entries
)
375 for name
in delegators
:
376 entry
= entries
[name
]
378 write_delegator(f
, name
, entry
.return_type
, entry
.argtypes
)