Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 1 | import re |
| 2 | import fileinput |
| 3 | import glob |
| 4 | import pprint |
| 5 | import os |
| 6 | import xml.etree.ElementTree as ET |
| 7 | |
| 8 | |
Mateusz Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 9 | # 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 Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 18 | files_to_visit = {} |
| 19 | |
Mateusz Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 20 | # find xml models for structs parsed by doxygen |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 21 | struct_files = glob.glob("apidoc/xml/structmbedtls*.xml") + glob.glob("apidoc/xml/structpsa*.xml") |
| 22 | |
| 23 | for struct_file in struct_files: |
Mateusz Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 24 | # get all variables from currently processed struct |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 25 | struct_file_tree = ET.parse(struct_file) |
| 26 | all_struct_members_definitions = struct_file_tree.getroot().findall(".//memberdef[@kind='variable']") |
| 27 | |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 28 | for struct_member_def in all_struct_members_definitions: |
Mateusz Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 29 | # get unique id by which this variable is referenced |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 30 | member_id = struct_member_def.attrib["id"] |
Mateusz Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 31 | # find file path for this variable's definition |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 32 | location = struct_member_def.find("location") |
| 33 | file_path = location.attrib["file"] |
Mateusz Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 34 | # get variable name |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 35 | variable_name = struct_member_def.find("name").text |
Mateusz Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 36 | # if file path is not yet in dictionary, create empty sub-dictionary to initialize |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 37 | 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 Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 43 | # add variable's definition line number |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 44 | 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 Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 51 | header_file_xml = "apidoc/xml/" + reference.attrib["compoundref"] + ".xml" |
| 52 | header_file_tree = ET.parse(header_file_xml) |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 53 | # 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 Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 57 | # if file path not yet in dictionary, create empty sub-dictionary to initialize. |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 58 | # 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 Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 67 | # find codelines referencing currently processed variable. This is using the code listing inside header's xml model. |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 68 | 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 Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 71 | # add lines referencing currently processed variable |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 72 | files_to_visit[static_inline_function_file_path][variable_name].update(codelines) |
| 73 | |
| 74 | pp = pprint.PrettyPrinter(indent=4) |
| 75 | pp.pprint(files_to_visit) |
| 76 | |
Mateusz Starzyk | cad24bb | 2021-05-27 13:50:40 +0200 | [diff] [blame] | 77 | mbedtls_private_access_include = "#include \"mbedtls/private_access.h\"" |
| 78 | |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 79 | for file_path, variables in files_to_visit.items(): |
Mateusz Starzyk | cad24bb | 2021-05-27 13:50:40 +0200 | [diff] [blame] | 80 | # 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 Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 88 | # FileInput redirects stdout to to 'file', so every print in this block will be put inside 'file' |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 89 | with fileinput.FileInput(file_path, inplace=True) as file: |
| 90 | output_line_number = 1 |
Mateusz Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 91 | # compile regex matching the header's include guard. |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 92 | re_include_guard = re.compile(r"^#define.*{name}$".format(name=os.path.basename(file_path).replace('.','_').upper())) |
| 93 | for line in file: |
Mateusz Starzyk | cad24bb | 2021-05-27 13:50:40 +0200 | [diff] [blame] | 94 | insert_private_access_include = False |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 95 | if re_include_guard.match(line): |
Mateusz Starzyk | cad24bb | 2021-05-27 13:50:40 +0200 | [diff] [blame] | 96 | insert_private_access_include = not file_has_private_access_include |
Mateusz Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 97 | # every line in file is checked against variables and lines in which they occur |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 98 | for variable, var_lines in variables.items(): |
| 99 | for var_line in var_lines: |
Mateusz Starzyk | 284471c | 2021-05-27 12:42:32 +0200 | [diff] [blame] | 100 | # wrap variable with MBEDTLS_PRIVATE(...) macro |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 101 | if output_line_number == var_line: |
Mateusz Starzyk | cad24bb | 2021-05-27 13:50:40 +0200 | [diff] [blame] | 102 | line = re.sub(r"(^.*?\W+)((?!MBEDTLS_PRIVATE\(){var})(\W+.*$)".format(var=variable), r"\1MBEDTLS_PRIVATE(\2)\3", line) |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 103 | output_line_number += 1 |
| 104 | print(line, end='') # fileinput redirects stdout to the target file |
Mateusz Starzyk | cad24bb | 2021-05-27 13:50:40 +0200 | [diff] [blame] | 105 | if insert_private_access_include: |
Mateusz Starzyk | 0d41abb | 2021-05-12 14:48:40 +0200 | [diff] [blame] | 106 | print("#include \"mbedtls/private_access.h\"") |