1#!/usr/bin/env python3 2 3# Copyright (C) 2013-2023 Free Software Foundation, Inc. 4# 5# This file is part of GDB. 6# 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. 11# 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. 16# 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/>. 19 20# Usage: 21# make-target-delegates.py 22 23import re 24import gdbcopyright 25 26 27# The line we search for in target.h that marks where we should start 28# looking for methods. 29TRIGGER = re.compile(r"^struct target_ops$") 30# The end of the methods part. 31ENDER = re.compile(r"^\s*};$") 32 33# Match a C symbol. 34SYMBOL = "[a-zA-Z_][a-zA-Z0-9_]*" 35# Match the name part of a method in struct target_ops. 36NAME_PART = r"(?P<name>" + SYMBOL + ")\s" 37# Match the arguments to a method. 38ARGS_PART = r"(?P<args>\(.*\))" 39# We strip the indentation so here we only need the caret. 40INTRO_PART = r"^" 41 42POINTER_PART = r"\s*(\*)?\s*" 43 44# Match a C++ symbol, including scope operators and template 45# parameters. E.g., 'std::vector<something>'. 46CP_SYMBOL = r"[a-zA-Z_][a-zA-Z0-9_<>:]*" 47# Match the return type when it is "ordinary". 48SIMPLE_RETURN_PART = r"((struct|class|enum|union)\s+)?" + CP_SYMBOL 49 50# Match a return type. 51RETURN_PART = r"((const|volatile)\s+)?(" + SIMPLE_RETURN_PART + ")" + POINTER_PART 52 53# Match "virtual". 54VIRTUAL_PART = r"virtual\s" 55 56# Match the TARGET_DEFAULT_* attribute for a method. 57TARGET_DEFAULT_PART = r"TARGET_DEFAULT_(?P<style>[A-Z_]+)\s*\((?P<default_arg>.*)\)" 58 59# Match the arguments and trailing attribute of a method definition. 60# Note we don't match the trailing ";". 61METHOD_TRAILER = r"\s*" + TARGET_DEFAULT_PART + "$" 62 63# Match an entire method definition. 64METHOD = re.compile( 65 INTRO_PART 66 + VIRTUAL_PART 67 + "(?P<return_type>" 68 + RETURN_PART 69 + ")" 70 + NAME_PART 71 + ARGS_PART 72 + METHOD_TRAILER 73) 74 75# Regular expression used to dissect argument types. 76ARGTYPES = re.compile( 77 "^(" 78 + r"(?P<E>enum\s+" 79 + SYMBOL 80 + r"\s*)(" 81 + SYMBOL 82 + ")?" 83 + r"|(?P<T>.*(enum\s+)?" 84 + SYMBOL 85 + r".*(\s|\*|&))" 86 + SYMBOL 87 + ")$" 88) 89 90# Match TARGET_DEBUG_PRINTER in an argument type. 91# This must match the whole "sub-expression" including the parens. 92TARGET_DEBUG_PRINTER = r"\s*TARGET_DEBUG_PRINTER\s*\((?P<arg>[^)]*)\)\s*" 93 94 95def scan_target_h(): 96 found_trigger = False 97 all_the_text = "" 98 with open("target.h", "r") as target_h: 99 for line in target_h: 100 line = line.strip() 101 if not found_trigger: 102 if TRIGGER.match(line): 103 found_trigger = True 104 elif "{" in line: 105 # Skip the open brace. 106 pass 107 elif ENDER.match(line): 108 break 109 else: 110 # Strip // comments. 111 line = re.split("//", line)[0] 112 all_the_text = all_the_text + " " + line 113 if not found_trigger: 114 raise "Could not find trigger line" 115 # Now strip out the C comments. 116 all_the_text = re.sub(r"/\*(.*?)\*/", "", all_the_text) 117 # Replace sequences whitespace with a single space character. 118 # We need the space because the method may have been split 119 # between multiple lines, like e.g.: 120 # 121 # virtual std::vector<long_type_name> 122 # my_long_method_name () 123 # TARGET_DEFAULT_IGNORE (); 124 # 125 # If we didn't preserve the space, then we'd end up with: 126 # 127 # virtual std::vector<long_type_name>my_long_method_name ()TARGET_DEFAULT_IGNORE () 128 # 129 # ... which wouldn't later be parsed correctly. 130 all_the_text = re.sub(r"\s+", " ", all_the_text) 131 return all_the_text.split(";") 132 133 134# Parse arguments into a list. 135def parse_argtypes(typestr): 136 # Remove the outer parens. 137 typestr = re.sub(r"^\((.*)\)$", r"\1", typestr) 138 result = [] 139 for item in re.split(r",\s*", typestr): 140 if item == "void" or item == "": 141 continue 142 m = ARGTYPES.match(item) 143 if m: 144 if m.group("E"): 145 onetype = m.group("E") 146 else: 147 onetype = m.group("T") 148 else: 149 onetype = item 150 result.append(onetype.strip()) 151 return result 152 153 154# Write function header given name, return type, and argtypes. 155# Returns a list of actual argument names. 156def write_function_header(f, decl, name, return_type, argtypes): 157 print(return_type, file=f, end="") 158 if decl: 159 if not return_type.endswith("*"): 160 print(" ", file=f, end="") 161 else: 162 print("", file=f) 163 print(name + " (", file=f, end="") 164 argdecls = [] 165 actuals = [] 166 for i in range(len(argtypes)): 167 val = re.sub(TARGET_DEBUG_PRINTER, "", argtypes[i]) 168 if not val.endswith("*") and not val.endswith("&"): 169 val = val + " " 170 vname = "arg" + str(i) 171 val = val + vname 172 argdecls.append(val) 173 actuals.append(vname) 174 print(", ".join(argdecls) + ")", file=f, end="") 175 if decl: 176 print(" override;", file=f) 177 else: 178 print("\n{", file=f) 179 return actuals 180 181 182# Write out a declaration. 183def write_declaration(f, name, return_type, argtypes): 184 write_function_header(f, True, name, return_type, argtypes) 185 186 187# Write out a delegation function. 188def write_delegator(f, name, return_type, argtypes): 189 names = write_function_header( 190 f, False, "target_ops::" + name, return_type, argtypes 191 ) 192 print(" ", file=f, end="") 193 if return_type != "void": 194 print("return ", file=f, end="") 195 print("this->beneath ()->" + name + " (", file=f, end="") 196 print(", ".join(names), file=f, end="") 197 print(");", file=f) 198 print("}\n", file=f) 199 200 201# Write out a default function. 202def write_tdefault(f, content, style, name, return_type, argtypes): 203 name = "dummy_target::" + name 204 names = write_function_header(f, False, name, return_type, argtypes) 205 if style == "FUNC": 206 print(" ", file=f, end="") 207 if return_type != "void": 208 print("return ", file=f, end="") 209 print(content + " (", file=f, end="") 210 names.insert(0, "this") 211 print(", ".join(names) + ");", file=f) 212 elif style == "RETURN": 213 print(" return " + content + ";", file=f) 214 elif style == "NORETURN": 215 print(" " + content + ";", file=f) 216 elif style == "IGNORE": 217 # Nothing. 218 pass 219 else: 220 raise "unrecognized style: " + style 221 print("}\n", file=f) 222 223 224def munge_type(typename): 225 m = re.search(TARGET_DEBUG_PRINTER, typename) 226 if m: 227 return m.group("arg") 228 typename = typename.rstrip() 229 typename = re.sub("[ ()<>:]", "_", typename) 230 typename = re.sub("[*]", "p", typename) 231 typename = re.sub("&", "r", typename) 232 # Identifers with double underscores are reserved to the C++ 233 # implementation. 234 typename = re.sub("_+", "_", typename) 235 # Avoid ending the function name with underscore, for 236 # cosmetics. Trailing underscores appear after munging types 237 # with template parameters, like e.g. "foo<int>". 238 typename = re.sub("_+$", "", typename) 239 return "target_debug_print_" + typename 240 241 242# Write out a debug method. 243def write_debugmethod(f, content, name, return_type, argtypes): 244 debugname = "debug_target::" + name 245 names = write_function_header(f, False, debugname, return_type, argtypes) 246 if return_type != "void": 247 print(" " + return_type + " result;", file=f) 248 print( 249 ' gdb_printf (gdb_stdlog, "-> %s->' 250 + name 251 + ' (...)\\n", this->beneath ()->shortname ());', 252 file=f, 253 ) 254 255 # Delegate to the beneath target. 256 print(" ", file=f, end="") 257 if return_type != "void": 258 print("result = ", file=f, end="") 259 print("this->beneath ()->" + name + " (", file=f, end="") 260 print(", ".join(names), file=f, end="") 261 print(");", file=f) 262 263 # Now print the arguments. 264 print( 265 ' gdb_printf (gdb_stdlog, "<- %s->' 266 + name 267 + ' (", this->beneath ()->shortname ());', 268 file=f, 269 ) 270 for i in range(len(argtypes)): 271 if i > 0: 272 print(' gdb_puts (", ", gdb_stdlog);', file=f) 273 printer = munge_type(argtypes[i]) 274 print(" " + printer + " (" + names[i] + ");", file=f) 275 if return_type != "void": 276 print(' gdb_puts (") = ", gdb_stdlog);', file=f) 277 printer = munge_type(return_type) 278 print(" " + printer + " (result);", file=f) 279 print(' gdb_puts ("\\n", gdb_stdlog);', file=f) 280 else: 281 print(' gdb_puts (")\\n", gdb_stdlog);', file=f) 282 283 if return_type != "void": 284 print(" return result;", file=f) 285 286 print("}\n", file=f) 287 288 289def print_class(f, class_name, delegators, entries): 290 print("struct " + class_name + " : public target_ops", file=f) 291 print("{", file=f) 292 print(" const target_info &info () const override;", file=f) 293 print("", file=f) 294 print(" strata stratum () const override;", file=f) 295 print("", file=f) 296 297 for name in delegators: 298 return_type = entries[name]["return_type"] 299 argtypes = entries[name]["argtypes"] 300 print(" ", file=f, end="") 301 write_declaration(f, name, return_type, argtypes) 302 303 print("};\n", file=f) 304 305 306delegators = [] 307entries = {} 308for current_line in scan_target_h(): 309 # See comments in scan_target_h. Here we strip away the leading 310 # and trailing whitespace. 311 current_line = current_line.strip() 312 m = METHOD.match(current_line) 313 if not m: 314 continue 315 data = m.groupdict() 316 data["argtypes"] = parse_argtypes(data["args"]) 317 data["return_type"] = data["return_type"].strip() 318 entries[data["name"]] = data 319 320 delegators.append(data["name"]) 321 322with open("target-delegates.c", "w") as f: 323 print( 324 gdbcopyright.copyright( 325 "make-target-delegates.py", "Boilerplate target methods for GDB" 326 ), 327 file=f, 328 ) 329 print_class(f, "dummy_target", delegators, entries) 330 print_class(f, "debug_target", delegators, entries) 331 332 for name in delegators: 333 tdefault = entries[name]["default_arg"] 334 return_type = entries[name]["return_type"] 335 style = entries[name]["style"] 336 argtypes = entries[name]["argtypes"] 337 338 write_delegator(f, name, return_type, argtypes) 339 write_tdefault(f, tdefault, style, name, return_type, argtypes) 340 write_debugmethod(f, tdefault, name, return_type, argtypes) 341