5bbe7c0b9308f54cca2620a536b4252990d16bc3
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 # Regular expression used to dissect argument types.
77 ARGTYPES
= re
.compile(
84 + r
"|(?P<T>.*(enum\s+)?"
91 # Match TARGET_DEBUG_PRINTER in an argument type.
92 # This must match the whole "sub-expression" including the parens.
93 TARGET_DEBUG_PRINTER
= r
"\s*TARGET_DEBUG_PRINTER\s*\((?P<arg>[^)]*)\)\s*"
98 self
, argtypes
: List
[str], return_type
: str, style
: str, default_arg
: str
100 self
.argtypes
= argtypes
101 self
.return_type
= return_type
103 self
.default_arg
= default_arg
107 found_trigger
= False
109 with
open("target.h", "r") as target_h
:
110 for line
in target_h
:
112 if not found_trigger
:
113 if TRIGGER
.match(line
):
116 # Skip the open brace.
118 elif ENDER
.match(line
):
122 line
= re
.split("//", line
)[0]
123 all_the_text
= all_the_text
+ " " + line
124 if not found_trigger
:
125 raise RuntimeError("Could not find trigger line")
126 # Now strip out the C comments.
127 all_the_text
= re
.sub(r
"/\*(.*?)\*/", "", all_the_text
)
128 # Replace sequences whitespace with a single space character.
129 # We need the space because the method may have been split
130 # between multiple lines, like e.g.:
132 # virtual std::vector<long_type_name>
133 # my_long_method_name ()
134 # TARGET_DEFAULT_IGNORE ();
136 # If we didn't preserve the space, then we'd end up with:
138 # virtual std::vector<long_type_name>my_long_method_name ()TARGET_DEFAULT_IGNORE ()
140 # ... which wouldn't later be parsed correctly.
141 all_the_text
= re
.sub(r
"\s+", " ", all_the_text
)
142 return all_the_text
.split(";")
145 # Parse arguments into a list.
146 def parse_argtypes(typestr
: str):
147 # Remove the outer parens.
148 typestr
= re
.sub(r
"^\((.*)\)$", r
"\1", typestr
)
149 result
: list[str] = []
150 for item
in re
.split(r
",\s*", typestr
):
151 if item
== "void" or item
== "":
153 m
= ARGTYPES
.match(item
)
156 onetype
= m
.group("E")
158 onetype
= m
.group("T")
161 result
.append(onetype
.strip())
165 # Write function header given name, return type, and argtypes.
166 # Returns a list of actual argument names.
167 def write_function_header(
168 f
: TextIO
, decl
: bool, name
: str, return_type
: str, argtypes
: List
[str]
170 print(return_type
, file=f
, end
="")
172 if not return_type
.endswith("*"):
173 print(" ", file=f
, end
="")
176 print(name
+ " (", file=f
, end
="")
177 argdecls
: list[str] = []
178 actuals
: list[str] = []
179 for i
in range(len(argtypes
)):
180 val
= re
.sub(TARGET_DEBUG_PRINTER
, "", argtypes
[i
])
181 if not val
.endswith("*") and not val
.endswith("&"):
183 vname
= "arg" + str(i
)
186 actuals
.append(vname
)
187 print(", ".join(argdecls
) + ")", file=f
, end
="")
189 print(" override;", file=f
)
195 # Write out a declaration.
196 def write_declaration(f
: TextIO
, name
: str, return_type
: str, argtypes
: List
[str]):
197 write_function_header(f
, True, name
, return_type
, argtypes
)
200 # Write out a delegation function.
201 def write_delegator(f
: TextIO
, name
: str, return_type
: str, argtypes
: List
[str]):
203 names
= write_function_header(
204 f
, False, "target_ops::" + name
, return_type
, argtypes
206 print(" ", file=f
, end
="")
207 if return_type
!= "void":
208 print("return ", file=f
, end
="")
209 print("this->beneath ()->" + name
+ " (", file=f
, end
="")
210 print(", ".join(names
), file=f
, end
="")
215 # Write out a default function.
225 name
= "dummy_target::" + name
226 names
= write_function_header(f
, False, name
, return_type
, argtypes
)
228 print(" ", file=f
, end
="")
229 if return_type
!= "void":
230 print("return ", file=f
, end
="")
231 print(content
+ " (", file=f
, end
="")
232 names
.insert(0, "this")
233 print(", ".join(names
) + ");", file=f
)
234 elif style
== "RETURN":
235 print(" return " + content
+ ";", file=f
)
236 elif style
== "NORETURN":
237 print(" " + content
+ ";", file=f
)
238 elif style
== "IGNORE":
242 raise RuntimeError("unrecognized style: " + style
)
246 def munge_type(typename
: str):
247 m
= re
.search(TARGET_DEBUG_PRINTER
, typename
)
249 return m
.group("arg")
250 typename
= typename
.rstrip()
251 # There's no reason to have these keywords in the name, and their
252 # presence makes it harder to change styles.
253 typename
= re
.sub("\\b(struct|enum|class|union) ", "", typename
)
254 typename
= re
.sub("[ ()<>:]", "_", typename
)
255 typename
= re
.sub("[*]", "p", typename
)
256 typename
= re
.sub("&", "r", typename
)
257 # Identifiers with double underscores are reserved to the C++
259 typename
= re
.sub("_+", "_", typename
)
260 # Avoid ending the function name with underscore, for
261 # cosmetics. Trailing underscores appear after munging types
262 # with template parameters, like e.g. "foo<int>".
263 typename
= re
.sub("_+$", "", typename
)
264 return "target_debug_print_" + typename
267 # Write out a debug method.
268 def write_debugmethod(
269 f
: TextIO
, content
: str, name
: str, return_type
: str, argtypes
: List
[str]
272 debugname
= "debug_target::" + name
273 names
= write_function_header(f
, False, debugname
, return_type
, argtypes
)
275 ' gdb_printf (gdb_stdlog, "-> %s->'
277 + ' (...)\\n", 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 # Now print the arguments.
293 ' gdb_printf (gdb_stdlog, "<- %s->'
295 + ' (", this->beneath ()->shortname ());',
298 for i
in range(len(argtypes
)):
300 print(' gdb_puts (", ", gdb_stdlog);', file=f
)
301 printer
= munge_type(argtypes
[i
])
302 print(" " + printer
+ " (" + names
[i
] + ");", file=f
)
303 if return_type
!= "void":
304 print(' gdb_puts (") = ", gdb_stdlog);', file=f
)
305 printer
= munge_type(return_type
)
306 print(" " + printer
+ " (result);", file=f
)
307 print(' gdb_puts ("\\n", gdb_stdlog);', file=f
)
309 print(' gdb_puts (")\\n", gdb_stdlog);', file=f
)
311 if return_type
!= "void":
312 print(" return result;", file=f
)
320 delegators
: List
[str],
321 entries
: Dict
[str, Entry
],
324 print("struct " + class_name
+ " : public target_ops", file=f
)
326 print(" const target_info &info () const override;", file=f
)
328 print(" strata stratum () const override;", file=f
)
331 for name
in delegators
:
332 print(" ", file=f
, end
="")
333 entry
= entries
[name
]
334 write_declaration(f
, name
, entry
.return_type
, entry
.argtypes
)
339 delegators
: List
[str] = []
340 entries
: Dict
[str, Entry
] = {}
342 for current_line
in scan_target_h():
343 # See comments in scan_target_h. Here we strip away the leading
344 # and trailing whitespace.
345 current_line
= current_line
.strip()
346 m
= METHOD
.match(current_line
)
351 argtypes
= parse_argtypes(data
["args"])
352 return_type
= data
["return_type"].strip()
353 style
= data
["style"]
354 default_arg
= data
["default_arg"]
355 entries
[name
] = Entry(argtypes
, return_type
, style
, default_arg
)
357 delegators
.append(name
)
359 with
open("target-delegates.c", "w") as f
:
361 gdbcopyright
.copyright(
362 "make-target-delegates.py", "Boilerplate target methods for GDB"
366 print_class(f
, "dummy_target", delegators
, entries
)
367 print_class(f
, "debug_target", delegators
, entries
)
369 for name
in delegators
:
370 entry
= entries
[name
]
372 write_delegator(f
, name
, entry
.return_type
, entry
.argtypes
)