mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
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.
96 lines
2.6 KiB
Python
Executable File
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")
|