diff options
author | Tom Tromey <tromey@adacore.com> | 2022-02-14 10:19:06 -0700 |
---|---|---|
committer | Tom Tromey <tromey@adacore.com> | 2022-03-02 09:11:30 -0700 |
commit | fb079cb5c419e03d400a0a139c3ccc4eedc33bef (patch) | |
tree | 06583fd6f6e61270050ccb8af732820cdfbc3ae9 /gdb/make-target-delegates.py | |
parent | a8ab094a329bd2d212f17485b24ad84fa409b546 (diff) | |
download | binutils-fb079cb5c419e03d400a0a139c3ccc4eedc33bef.zip binutils-fb079cb5c419e03d400a0a139c3ccc4eedc33bef.tar.gz binutils-fb079cb5c419e03d400a0a139c3ccc4eedc33bef.tar.bz2 |
Rewrite make-target-delegates in Python
I think gdb is probably better off having fewer languages involved
when generating code. 'sh' is unavoidable for build-time generation,
but for other things, let's use Python.
This rewrites make-target-delegates in Python. I've stuck pretty
closely to the original code in this rewrite, so it may look slightly
weird from a Python perspective.
The only output difference is that a copyright header is now
generated, using the code introduced in the previous patch.
make-target-delegates.py is simpler to invoke, as it knows the correct
input file to scan and it creates the output file itself.
Diffstat (limited to 'gdb/make-target-delegates.py')
-rwxr-xr-x | gdb/make-target-delegates.py | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/gdb/make-target-delegates.py b/gdb/make-target-delegates.py new file mode 100755 index 0000000..9717807 --- /dev/null +++ b/gdb/make-target-delegates.py @@ -0,0 +1,340 @@ +#!/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( + ' fprintf_unfiltered (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( + ' fprintf_unfiltered (gdb_stdlog, "<- %s->' + + name + + ' (", this->beneath ()->shortname ());', + file=f, + ) + for i in range(len(argtypes)): + if i > 0: + print(' fputs_unfiltered (", ", gdb_stdlog);', file=f) + printer = munge_type(argtypes[i]) + print(" " + printer + " (" + names[i] + ");", file=f) + if return_type != "void": + print(' fputs_unfiltered (") = ", gdb_stdlog);', file=f) + printer = munge_type(return_type) + print(" " + printer + " (result);", file=f) + print(' fputs_unfiltered ("\\n", gdb_stdlog);', file=f) + else: + print(' fputs_unfiltered (")\\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) |