binutils-gdb/gdb/make-target-delegates.py
Tom Tromey 6cb06a8cda Unify gdb printf functions
Now that filtered and unfiltered output can be treated identically, we
can unify the printf family of functions.  This is done under the name
"gdb_printf".  Most of this patch was written by script.
2022-03-29 12:46:24 -06:00

341 lines
11 KiB
Python
Executable File

#!/usr/bin/env python3
# Copyright (C) 2013-2022 Free Software Foundation, Inc.
#
# This file is part of GDB.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Usage:
# make-target-delegates.py
import re
import gdbcopyright
# The line we search for in target.h that marks where we should start
# looking for methods.
TRIGGER = re.compile(r"^struct target_ops$")
# The end of the methods part.
ENDER = re.compile(r"^\s*};$")
# Match a C symbol.
SYMBOL = "[a-zA-Z_][a-zA-Z0-9_]*"
# Match the name part of a method in struct target_ops.
NAME_PART = r"(?P<name>" + SYMBOL + ")\s"
# Match the arguments to a method.
ARGS_PART = r"(?P<args>\(.*\))"
# We strip the indentation so here we only need the caret.
INTRO_PART = r"^"
POINTER_PART = r"\s*(\*)?\s*"
# Match a C++ symbol, including scope operators and template
# parameters. E.g., 'std::vector<something>'.
CP_SYMBOL = r"[a-zA-Z_][a-zA-Z0-9_<>:]*"
# Match the return type when it is "ordinary".
SIMPLE_RETURN_PART = r"((struct|class|enum|union)\s+)?" + CP_SYMBOL
# Match a return type.
RETURN_PART = r"((const|volatile)\s+)?(" + SIMPLE_RETURN_PART + ")" + POINTER_PART
# Match "virtual".
VIRTUAL_PART = r"virtual\s"
# Match the TARGET_DEFAULT_* attribute for a method.
TARGET_DEFAULT_PART = r"TARGET_DEFAULT_(?P<style>[A-Z_]+)\s*\((?P<default_arg>.*)\)"
# Match the arguments and trailing attribute of a method definition.
# Note we don't match the trailing ";".
METHOD_TRAILER = r"\s*" + TARGET_DEFAULT_PART + "$"
# Match an entire method definition.
METHOD = re.compile(
INTRO_PART
+ VIRTUAL_PART
+ "(?P<return_type>"
+ RETURN_PART
+ ")"
+ NAME_PART
+ ARGS_PART
+ METHOD_TRAILER
)
# Regular expression used to dissect argument types.
ARGTYPES = re.compile(
"^("
+ r"(?P<E>enum\s+"
+ SYMBOL
+ r"\s*)("
+ SYMBOL
+ ")?"
+ r"|(?P<T>.*(enum\s+)?"
+ SYMBOL
+ r".*(\s|\*|&))"
+ SYMBOL
+ ")$"
)
# Match TARGET_DEBUG_PRINTER in an argument type.
# This must match the whole "sub-expression" including the parens.
TARGET_DEBUG_PRINTER = r"\s*TARGET_DEBUG_PRINTER\s*\((?P<arg>[^)]*)\)\s*"
def scan_target_h():
found_trigger = False
all_the_text = ""
with open("target.h", "r") as target_h:
for line in target_h:
line = line.strip()
if not found_trigger:
if TRIGGER.match(line):
found_trigger = True
elif "{" in line:
# Skip the open brace.
pass
elif ENDER.match(line):
break
else:
# Strip // comments.
line = re.split("//", line)[0]
all_the_text = all_the_text + " " + line
if not found_trigger:
raise "Could not find trigger line"
# Now strip out the C comments.
all_the_text = re.sub(r"/\*(.*?)\*/", "", all_the_text)
# Replace sequences whitespace with a single space character.
# We need the space because the method may have been split
# between multiple lines, like e.g.:
#
# virtual std::vector<long_type_name>
# my_long_method_name ()
# TARGET_DEFAULT_IGNORE ();
#
# If we didn't preserve the space, then we'd end up with:
#
# virtual std::vector<long_type_name>my_long_method_name ()TARGET_DEFAULT_IGNORE ()
#
# ... which wouldn't later be parsed correctly.
all_the_text = re.sub(r"\s+", " ", all_the_text)
return all_the_text.split(";")
# Parse arguments into a list.
def parse_argtypes(typestr):
# Remove the outer parens.
typestr = re.sub(r"^\((.*)\)$", r"\1", typestr)
result = []
for item in re.split(r",\s*", typestr):
if item == "void" or item == "":
continue
m = ARGTYPES.match(item)
if m:
if m.group("E"):
onetype = m.group("E")
else:
onetype = m.group("T")
else:
onetype = item
result.append(onetype.strip())
return result
# Write function header given name, return type, and argtypes.
# Returns a list of actual argument names.
def write_function_header(f, decl, name, return_type, argtypes):
print(return_type, file=f, end="")
if decl:
if not return_type.endswith("*"):
print(" ", file=f, end="")
else:
print("", file=f)
print(name + " (", file=f, end="")
argdecls = []
actuals = []
for i in range(len(argtypes)):
val = re.sub(TARGET_DEBUG_PRINTER, "", argtypes[i])
if not val.endswith("*") and not val.endswith("&"):
val = val + " "
vname = "arg" + str(i)
val = val + vname
argdecls.append(val)
actuals.append(vname)
print(", ".join(argdecls) + ")", file=f, end="")
if decl:
print(" override;", file=f)
else:
print("\n{", file=f)
return actuals
# Write out a declaration.
def write_declaration(f, name, return_type, argtypes):
write_function_header(f, True, name, return_type, argtypes)
# Write out a delegation function.
def write_delegator(f, name, return_type, argtypes):
names = write_function_header(
f, False, "target_ops::" + name, return_type, argtypes
)
print(" ", file=f, end="")
if return_type != "void":
print("return ", file=f, end="")
print("this->beneath ()->" + name + " (", file=f, end="")
print(", ".join(names), file=f, end="")
print(");", file=f)
print("}\n", file=f)
# Write out a default function.
def write_tdefault(f, content, style, name, return_type, argtypes):
name = "dummy_target::" + name
names = write_function_header(f, False, name, return_type, argtypes)
if style == "FUNC":
print(" ", file=f, end="")
if return_type != "void":
print("return ", file=f, end="")
print(content + " (", file=f, end="")
names.insert(0, "this")
print(", ".join(names) + ");", file=f)
elif style == "RETURN":
print(" return " + content + ";", file=f)
elif style == "NORETURN":
print(" " + content + ";", file=f)
elif style == "IGNORE":
# Nothing.
pass
else:
raise "unrecognized style: " + style
print("}\n", file=f)
def munge_type(typename):
m = re.search(TARGET_DEBUG_PRINTER, typename)
if m:
return m.group("arg")
typename = typename.rstrip()
typename = re.sub("[ ()<>:]", "_", typename)
typename = re.sub("[*]", "p", typename)
typename = re.sub("&", "r", typename)
# Identifers with double underscores are reserved to the C++
# implementation.
typename = re.sub("_+", "_", typename)
# Avoid ending the function name with underscore, for
# cosmetics. Trailing underscores appear after munging types
# with template parameters, like e.g. "foo<int>".
typename = re.sub("_+$", "", typename)
return "target_debug_print_" + typename
# Write out a debug method.
def write_debugmethod(f, content, name, return_type, argtypes):
debugname = "debug_target::" + name
names = write_function_header(f, False, debugname, return_type, argtypes)
if return_type != "void":
print(" " + return_type + " result;", file=f)
print(
' gdb_printf (gdb_stdlog, "-> %s->'
+ name
+ ' (...)\\n", this->beneath ()->shortname ());',
file=f,
)
# Delegate to the beneath target.
print(" ", file=f, end="")
if return_type != "void":
print("result = ", file=f, end="")
print("this->beneath ()->" + name + " (", file=f, end="")
print(", ".join(names), file=f, end="")
print(");", file=f)
# Now print the arguments.
print(
' gdb_printf (gdb_stdlog, "<- %s->'
+ name
+ ' (", this->beneath ()->shortname ());',
file=f,
)
for i in range(len(argtypes)):
if i > 0:
print(' gdb_puts (", ", gdb_stdlog);', file=f)
printer = munge_type(argtypes[i])
print(" " + printer + " (" + names[i] + ");", file=f)
if return_type != "void":
print(' gdb_puts (") = ", gdb_stdlog);', file=f)
printer = munge_type(return_type)
print(" " + printer + " (result);", file=f)
print(' gdb_puts ("\\n", gdb_stdlog);', file=f)
else:
print(' gdb_puts (")\\n", gdb_stdlog);', file=f)
if return_type != "void":
print(" return result;", file=f)
print("}\n", file=f)
def print_class(f, class_name, delegators, entries):
print("struct " + class_name + " : public target_ops", file=f)
print("{", file=f)
print(" const target_info &info () const override;", file=f)
print("", file=f)
print(" strata stratum () const override;", file=f)
print("", file=f)
for name in delegators:
return_type = entries[name]["return_type"]
argtypes = entries[name]["argtypes"]
print(" ", file=f, end="")
write_declaration(f, name, return_type, argtypes)
print("};\n", file=f)
delegators = []
entries = {}
for current_line in scan_target_h():
# See comments in scan_target_h. Here we strip away the leading
# and trailing whitespace.
current_line = current_line.strip()
m = METHOD.match(current_line)
if not m:
continue
data = m.groupdict()
data["argtypes"] = parse_argtypes(data["args"])
data["return_type"] = data["return_type"].strip()
entries[data["name"]] = data
delegators.append(data["name"])
with open("target-delegates.c", "w") as f:
print(
gdbcopyright.copyright(
"make-target-delegates.py", "Boilerplate target methods for GDB"
),
file=f,
)
print_class(f, "dummy_target", delegators, entries)
print_class(f, "debug_target", delegators, entries)
for name in delegators:
tdefault = entries[name]["default_arg"]
return_type = entries[name]["return_type"]
style = entries[name]["style"]
argtypes = entries[name]["argtypes"]
write_delegator(f, name, return_type, argtypes)
write_tdefault(f, tdefault, style, name, return_type, argtypes)
write_debugmethod(f, tdefault, name, return_type, argtypes)