-
Notifications
You must be signed in to change notification settings - Fork 1
/
analyze_transformations.py
90 lines (72 loc) · 3.53 KB
/
analyze_transformations.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import json
from typing import Any
import logging
from collections import Counter
from macros import Macro, PreprocessorData, Invocation
logger = logging.getLogger(__name__)
def filter_definitions(entries: list[dict[str, Any]]) -> list[dict[str, Any]]:
# Count the number of definitions with a given name
name_counts = Counter(obj["Name"] for obj in entries if obj["Kind"] == "Definition")
# Create set of definition names that only appear once
filtered_names = {name for name in name_counts if name_counts[name] == 1}
# Filter out definitions and invocations that are not in filtered_names
filtered_entries = [
obj for obj in entries
if not ((obj["Kind"] == "Definition" and obj["Name"] not in filtered_names) or
(obj["Kind"] == "Invocation" and obj["Name"] not in filtered_names))
]
return filtered_entries
def get_tlna_src_preprocessordata(results_file: str) -> PreprocessorData:
with open(results_file) as fp:
entries = json.load(fp)
# Filter out duplicate definitions: keep none if there is more than one
# We need to do this to avoid (possibly) breaking the one definition rule
# i.e if file B includes file A, both have a definition with the same name
filtered_entries = filter_definitions(entries)
# We need definitions to come first as we map invocations to them
filtered_entries.sort(key=lambda obj: obj["Kind"] == "Definition", reverse=True)
pd = PreprocessorData()
# src directory, to be initialized during the analysis
src_dir = ''
# We need this because invocations don't have all the information necessary
# to construct a macro to use as a key in the macro map (pd.mm)
# NOTE: Currently ignores macros without FileEntry data (i.e compiler built-ins)
macroDefinitionLocationToMacroObject: dict[str, Macro] = {}
for entry in filtered_entries:
if entry["Kind"] == "Definition":
del entry["Kind"]
m = Macro(**entry)
if m not in pd.mm:
pd.mm[m] = set()
if m.IsDefinitionLocationValid:
macroDefinitionLocationToMacroObject[entry["DefinitionLocation"]] = m
logging.debug(f"Adding name {m.Name} to macroDefinitionLocationToMacroObject")
elif entry["Kind"] == 'InspectedByCPP':
pd.inspected_macro_names.add(entry["Name"])
elif entry["Kind"] == "Include":
if entry["IsValid"]:
pd.local_includes.add(entry["IncludeName"])
elif entry["Kind"] == 'Invocation':
del entry["Kind"]
i = Invocation(**entry)
if i.IsDefinitionLocationValid:
m = macroDefinitionLocationToMacroObject[i.DefinitionLocation]
# Only record unique invocations - two invocations may have the same
# location if they are the same nested invocation
if all(j.InvocationLocation != i.InvocationLocation for j in pd.mm[m]):
pd.mm[m].add(i)
# src_pd only records preprocessor data about source macros
src_pd = PreprocessorData(
{m: is_ for m, is_ in pd.mm.items() if m.defined_in(src_dir)},
pd.inspected_macro_names,
pd.local_includes
)
# tlna_src_pd only records preprocessor data about top-level,
# non-argument source macros
tlna_src_pd = PreprocessorData(
{m: is_ for m, is_ in src_pd.mm.items()
if all([i.IsTopLevelNonArgument for i in is_])},
src_pd.inspected_macro_names,
src_pd.local_includes
)
return tlna_src_pd