blob: d2bc7c06a2fed7ce554fa39e4dc33e6bff75bf22 [file] [log] [blame]
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +02001import re
2import fileinput
3import glob
4import pprint
5import os
6import xml.etree.ElementTree as ET
7
8
Mateusz Starzyk284471c2021-05-27 12:42:32 +02009# Create dictionary with following structre
10# files_to_visit = {
11# "filepath1" : { "variable_name1": (1, 2, 40, 61), # line numbers
12# "variable_name2": (60, 64),
13# },
14# "filepath2" : { "variable_name1": (1, 2, 40, 61), # line numbers
15# "variable_name2": (60, 64),
16# }, ...
17# }
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020018files_to_visit = {}
19
Mateusz Starzyk284471c2021-05-27 12:42:32 +020020# find xml models for structs parsed by doxygen
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020021struct_files = glob.glob("apidoc/xml/structmbedtls*.xml") + glob.glob("apidoc/xml/structpsa*.xml")
22
23for struct_file in struct_files:
Mateusz Starzyk284471c2021-05-27 12:42:32 +020024 # get all variables from currently processed struct
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020025 struct_file_tree = ET.parse(struct_file)
26 all_struct_members_definitions = struct_file_tree.getroot().findall(".//memberdef[@kind='variable']")
27
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020028 for struct_member_def in all_struct_members_definitions:
Mateusz Starzyk284471c2021-05-27 12:42:32 +020029 # get unique id by which this variable is referenced
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020030 member_id = struct_member_def.attrib["id"]
Mateusz Starzyk284471c2021-05-27 12:42:32 +020031 # find file path for this variable's definition
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020032 location = struct_member_def.find("location")
33 file_path = location.attrib["file"]
Mateusz Starzyk284471c2021-05-27 12:42:32 +020034 # get variable name
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020035 variable_name = struct_member_def.find("name").text
Mateusz Starzyk284471c2021-05-27 12:42:32 +020036 # if file path is not yet in dictionary, create empty sub-dictionary to initialize
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020037 if file_path not in files_to_visit:
38 files_to_visit[file_path] = {}
39 # if variable is not yet in this file's dictionary, create empty set to initialize
40 if variable_name not in files_to_visit[file_path]:
41 files_to_visit[file_path][variable_name] = set()
42
Mateusz Starzyk284471c2021-05-27 12:42:32 +020043 # add variable's definition line number
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020044 files_to_visit[file_path][variable_name].add(int(location.attrib["line"]))
45
46 # check where the variable was referenced
47 references = struct_member_def.findall("referencedby")
48 for reference in references:
49 refid = reference.attrib["refid"]
50 # assuming that compound name is related to header's xml file
Mateusz Starzyk284471c2021-05-27 12:42:32 +020051 header_file_xml = "apidoc/xml/" + reference.attrib["compoundref"] + ".xml"
52 header_file_tree = ET.parse(header_file_xml)
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020053 # check if this reference is created by static inline function
54 static_inline_function_definition = header_file_tree.getroot().find(f".//memberdef[@id='{refid}'][@kind='function'][@static='yes'][@inline='yes']")
55 if static_inline_function_definition:
56 static_inline_function_file_path = static_inline_function_definition.find("location").attrib["file"]
Mateusz Starzyk284471c2021-05-27 12:42:32 +020057 # if file path not yet in dictionary, create empty sub-dictionary to initialize.
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020058 # This could happen if reference is inside header file which was not yet processed in search for variable definitions
59 if static_inline_function_file_path not in files_to_visit:
60 files_to_visit[static_inline_function_file_path] = {}
61 # if variable is not yet in this file's dictionary, create empty set to initialize
62 if variable_name not in files_to_visit[static_inline_function_file_path]:
63 files_to_visit[static_inline_function_file_path][variable_name] = set()
64 # function block scope
65 function_lines_from = int(reference.attrib["startline"])
66 function_lines_to = int(reference.attrib["endline"])
Mateusz Starzyk284471c2021-05-27 12:42:32 +020067 # find codelines referencing currently processed variable. This is using the code listing inside header's xml model.
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020068 codelines_xml = header_file_tree.getroot().findall(f".//ref[@refid='{member_id}']/../..")
69 # filter by function's scope
70 codelines = [int(line.attrib["lineno"]) for line in codelines_xml if int(line.attrib["lineno"]) >= function_lines_from and int(line.attrib["lineno"]) <= function_lines_to]
Mateusz Starzyk284471c2021-05-27 12:42:32 +020071 # add lines referencing currently processed variable
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020072 files_to_visit[static_inline_function_file_path][variable_name].update(codelines)
73
74pp = pprint.PrettyPrinter(indent=4)
75pp.pprint(files_to_visit)
76
Mateusz Starzykcad24bb2021-05-27 13:50:40 +020077mbedtls_private_access_include = "#include \"mbedtls/private_access.h\""
78
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020079for file_path, variables in files_to_visit.items():
Mateusz Starzykcad24bb2021-05-27 13:50:40 +020080 # check if this file has "mbedtls/private_access.h" include
81 file_has_private_access_include = False
82 with open(file_path, 'r') as file:
83 for line in file:
84 if mbedtls_private_access_include in line:
85 file_has_private_access_include = True
86 break
87
Mateusz Starzyk284471c2021-05-27 12:42:32 +020088 # FileInput redirects stdout to to 'file', so every print in this block will be put inside 'file'
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020089 with fileinput.FileInput(file_path, inplace=True) as file:
90 output_line_number = 1
Mateusz Starzyk284471c2021-05-27 12:42:32 +020091 # compile regex matching the header's include guard.
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020092 re_include_guard = re.compile(r"^#define.*{name}$".format(name=os.path.basename(file_path).replace('.','_').upper()))
93 for line in file:
Mateusz Starzykcad24bb2021-05-27 13:50:40 +020094 insert_private_access_include = False
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020095 if re_include_guard.match(line):
Mateusz Starzykcad24bb2021-05-27 13:50:40 +020096 insert_private_access_include = not file_has_private_access_include
Mateusz Starzyk284471c2021-05-27 12:42:32 +020097 # every line in file is checked against variables and lines in which they occur
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +020098 for variable, var_lines in variables.items():
99 for var_line in var_lines:
Mateusz Starzyk284471c2021-05-27 12:42:32 +0200100 # wrap variable with MBEDTLS_PRIVATE(...) macro
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +0200101 if output_line_number == var_line:
Mateusz Starzykcad24bb2021-05-27 13:50:40 +0200102 line = re.sub(r"(^.*?\W+)((?!MBEDTLS_PRIVATE\(){var})(\W+.*$)".format(var=variable), r"\1MBEDTLS_PRIVATE(\2)\3", line)
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +0200103 output_line_number += 1
104 print(line, end='') # fileinput redirects stdout to the target file
Mateusz Starzykcad24bb2021-05-27 13:50:40 +0200105 if insert_private_access_include:
Mateusz Starzyk0d41abb2021-05-12 14:48:40 +0200106 print("#include \"mbedtls/private_access.h\"")