Files
codeql/swift/codegen/generators/trapgen.py
Paolo Tranquilli 7d7966e711 Swift: make trap key prefixes readable
This replaces numeric tag-based prefixes with the actual tag name.
While this means in general slightly larger trap files, it aids
debugging them for a human.

In the future we can make this conditional on some kind of trap debug
option, but for the moment it does not seem detrimental.
2022-07-28 12:43:30 +02:00

96 lines
2.6 KiB
Python
Executable File

"""
C++ trap entry generation
`generate(opts, renderer)` will generate `TrapTags.h` (for types of labels) and `TrapEntries.h` (for trap emission) out
of a dbscheme file.
Each table in the `dbscheme` gets a corresponding `struct` defined in `TrapEntries.h` with a field for each column and
an appropriate streaming operator for the trap emission.
Unions in the `dbscheme` are used to populate a hierarchy of tags (empty structs) in `TrapTags.h` that is used to
enforce a type system on trap labels (see `TrapLabel.h`).
"""
import logging
import pathlib
import inflection
from toposort import toposort_flatten
from swift.codegen.lib import dbscheme, cpp
log = logging.getLogger(__name__)
def get_tag_name(s):
assert s.startswith("@")
return inflection.camelize(s[1:])
def get_cpp_type(schema_type: str):
if schema_type.startswith("@"):
tag = get_tag_name(schema_type)
return f"TrapLabel<{tag}Tag>"
if schema_type == "string":
return "std::string"
if schema_type == "boolean":
return "bool"
return schema_type
def get_field(c: dbscheme.Column):
args = {
"field_name": c.schema_name,
"type": c.type,
}
args.update(cpp.get_field_override(c.schema_name))
args["type"] = get_cpp_type(args["type"])
return cpp.Field(**args)
def get_binding_column(t: dbscheme.Table):
try:
return next(c for c in t.columns if c.binding)
except StopIteration:
return None
def get_trap(t: dbscheme.Table):
id = get_binding_column(t)
if id:
id = get_field(id)
return cpp.Trap(
table_name=t.name,
name=inflection.camelize(t.name),
fields=[get_field(c) for c in t.columns],
id=id,
)
def generate(opts, renderer):
assert opts.cpp_output
tag_graph = {}
out = opts.cpp_output
traps = {pathlib.Path(): []}
for e in dbscheme.iterload(opts.dbscheme):
if e.is_table:
traps.setdefault(e.dir, []).append(get_trap(e))
elif e.is_union:
tag_graph.setdefault(e.lhs, set())
for d in e.rhs:
tag_graph.setdefault(d.type, set()).add(e.lhs)
for dir, entries in traps.items():
dir = dir or pathlib.Path()
renderer.render(cpp.TrapList(entries, opts.dbscheme), out / dir / "TrapEntries")
tags = []
for tag in toposort_flatten(tag_graph):
tags.append(cpp.Tag(
name=get_tag_name(tag),
bases=[get_tag_name(b) for b in sorted(tag_graph[tag])],
id=tag,
))
renderer.render(cpp.TagList(tags, opts.dbscheme), out / "TrapTags")