3 # Copyright (C) 2013-2023 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
):
152 if item
== "void" or item
== "":
154 m
= ARGTYPES
.match(item
)
157 onetype
= m
.group("E")
159 onetype
= m
.group("T")
162 result
.append(onetype
.strip())
166 # Write function header given name, return type, and argtypes.
167 # Returns a list of actual argument names.
168 def write_function_header(
169 f
: TextIO
, decl
: bool, name
: str, return_type
: str, argtypes
: List
[str]
171 print(return_type
, file=f
, end
="")
173 if not return_type
.endswith("*"):
174 print(" ", file=f
, end
="")
177 print(name
+ " (", file=f
, end
="")
178 argdecls
: list[str] = []
179 actuals
: list[str] = []
180 for i
in range(len(argtypes
)):
181 val
= re
.sub(TARGET_DEBUG_PRINTER
, "", argtypes
[i
])
182 if not val
.endswith("*") and not val
.endswith("&"):
184 vname
= "arg" + str(i
)
187 actuals
.append(vname
)
188 print(", ".join(argdecls
) + ")", file=f
, end
="")
190 print(" override;", file=f
)
196 # Write out a declaration.
197 def write_declaration(f
: TextIO
, name
: str, return_type
: str, argtypes
: List
[str]):
198 write_function_header(f
, True, name
, return_type
, argtypes
)
201 # Write out a delegation function.
202 def write_delegator(f
: TextIO
, name
: str, return_type
: str, argtypes
: List
[str]):
204 names
= write_function_header(
205 f
, False, "target_ops::" + name
, return_type
, argtypes
207 print(" ", file=f
, end
="")
208 if return_type
!= "void":
209 print("return ", file=f
, end
="")
210 print("this->beneath ()->" + name
+ " (", file=f
, end
="")
211 print(", ".join(names
), file=f
, end
="")
216 # Write out a default function.
226 name
= "dummy_target::" + name
227 names
= write_function_header(f
, False, name
, return_type
, argtypes
)
229 print(" ", file=f
, end
="")
230 if return_type
!= "void":
231 print("return ", file=f
, end
="")
232 print(content
+ " (", file=f
, end
="")
233 names
.insert(0, "this")
234 print(", ".join(names
) + ");", file=f
)
235 elif style
== "RETURN":
236 print(" return " + content
+ ";", file=f
)
237 elif style
== "NORETURN":
238 print(" " + content
+ ";", file=f
)
239 elif style
== "IGNORE":
243 raise RuntimeError("unrecognized style: " + style
)
247 def munge_type(typename
: str):
248 m
= re
.search(TARGET_DEBUG_PRINTER
, typename
)
250 return m
.group("arg")
251 typename
= typename
.rstrip()
252 # There's no reason to have these keywords in the name, and their
253 # presence makes it harder to change styles.
254 typename
= re
.sub("\\b(struct|enum|class|union) ", "", typename
)
255 typename
= re
.sub("[ ()<>:]", "_", typename
)
256 typename
= re
.sub("[*]", "p", typename
)
257 typename
= re
.sub("&", "r", typename
)
258 # Identifiers with double underscores are reserved to the C++
260 typename
= re
.sub("_+", "_", typename
)
261 # Avoid ending the function name with underscore, for
262 # cosmetics. Trailing underscores appear after munging types
263 # with template parameters, like e.g. "foo<int>".
264 typename
= re
.sub("_+$", "", typename
)
265 return "target_debug_print_" + typename
268 # Write out a debug method.
269 def write_debugmethod(
270 f
: TextIO
, content
: str, name
: str, return_type
: str, argtypes
: List
[str]
273 debugname
= "debug_target::" + name
274 names
= write_function_header(f
, False, debugname
, return_type
, argtypes
)
276 ' gdb_printf (gdb_stdlog, "-> %s->'
278 + ' (...)\\n", this->beneath ()->shortname ());',
282 # Delegate to the beneath target.
283 if return_type
!= "void":
284 print(" " + return_type
+ " result", file=f
)
285 print(" = ", file=f
, end
="")
287 print(" ", file=f
, end
="")
288 print("this->beneath ()->" + name
+ " (", file=f
, end
="")
289 print(", ".join(names
), file=f
, end
="")
292 # Now print the arguments.
294 ' gdb_printf (gdb_stdlog, "<- %s->'
296 + ' (", this->beneath ()->shortname ());',
299 for i
in range(len(argtypes
)):
301 print(' gdb_puts (", ", gdb_stdlog);', file=f
)
302 printer
= munge_type(argtypes
[i
])
303 print(" " + printer
+ " (" + names
[i
] + ");", file=f
)
304 if return_type
!= "void":
305 print(' gdb_puts (") = ", gdb_stdlog);', file=f
)
306 printer
= munge_type(return_type
)
307 print(" " + printer
+ " (result);", file=f
)
308 print(' gdb_puts ("\\n", gdb_stdlog);', file=f
)
310 print(' gdb_puts (")\\n", gdb_stdlog);', file=f
)
312 if return_type
!= "void":
313 print(" return result;", file=f
)
321 delegators
: List
[str],
322 entries
: Dict
[str, Entry
],
325 print("struct " + class_name
+ " : public target_ops", file=f
)
327 print(" const target_info &info () const override;", file=f
)
329 print(" strata stratum () const override;", file=f
)
332 for name
in delegators
:
333 print(" ", file=f
, end
="")
334 entry
= entries
[name
]
335 write_declaration(f
, name
, entry
.return_type
, entry
.argtypes
)
340 delegators
: List
[str] = []
341 entries
: Dict
[str, Entry
] = {}
343 for current_line
in scan_target_h():
344 # See comments in scan_target_h. Here we strip away the leading
345 # and trailing whitespace.
346 current_line
= current_line
.strip()
347 m
= METHOD
.match(current_line
)
352 argtypes
= parse_argtypes(data
["args"])
353 return_type
= data
["return_type"].strip()
354 style
= data
["style"]
355 default_arg
= data
["default_arg"]
356 entries
[name
] = Entry(argtypes
, return_type
, style
, default_arg
)
358 delegators
.append(name
)
360 with
open("target-delegates.c", "w") as f
:
362 gdbcopyright
.copyright(
363 "make-target-delegates.py", "Boilerplate target methods for GDB"
367 print_class(f
, "dummy_target", delegators
, entries
)
368 print_class(f
, "debug_target", delegators
, entries
)
370 for name
in delegators
:
371 entry
= entries
[name
]
373 write_delegator(f
, name
, entry
.return_type
, entry
.argtypes
)