1*84e33947SAndroid Build Coastguard Worker#!/usr/bin/python3 2*84e33947SAndroid Build Coastguard Worker# 3*84e33947SAndroid Build Coastguard Worker# Copyright (C) 2023 The Android Open Source Project 4*84e33947SAndroid Build Coastguard Worker# 5*84e33947SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*84e33947SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*84e33947SAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*84e33947SAndroid Build Coastguard Worker# 9*84e33947SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*84e33947SAndroid Build Coastguard Worker# 11*84e33947SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*84e33947SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*84e33947SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*84e33947SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*84e33947SAndroid Build Coastguard Worker# limitations under the License. 16*84e33947SAndroid Build Coastguard Worker# 17*84e33947SAndroid Build Coastguard Worker 18*84e33947SAndroid Build Coastguard Workerimport os 19*84e33947SAndroid Build Coastguard Worker 20*84e33947SAndroid Build Coastguard Workerfrom collections import defaultdict 21*84e33947SAndroid Build Coastguard Workerfrom pyclibrary import CParser 22*84e33947SAndroid Build Coastguard Worker 23*84e33947SAndroid Build Coastguard Workerfrom utils import system_chre_abs_path 24*84e33947SAndroid Build Coastguard Worker 25*84e33947SAndroid Build Coastguard Worker 26*84e33947SAndroid Build Coastguard Workerclass ApiParser: 27*84e33947SAndroid Build Coastguard Worker """Given a file-specific set of annotations (extracted from JSON annotations file), parses a 28*84e33947SAndroid Build Coastguard Worker single API header file into data structures suitable for use with code generation. This class 29*84e33947SAndroid Build Coastguard Worker will contain the parsed representation of the headers when instantiated. 30*84e33947SAndroid Build Coastguard Worker """ 31*84e33947SAndroid Build Coastguard Worker 32*84e33947SAndroid Build Coastguard Worker def __init__(self, json_obj): 33*84e33947SAndroid Build Coastguard Worker """Initialize and parse the API file described in the provided JSON-derived object. 34*84e33947SAndroid Build Coastguard Worker 35*84e33947SAndroid Build Coastguard Worker :param json_obj: Extracted file-specific annotations from JSON 36*84e33947SAndroid Build Coastguard Worker """ 37*84e33947SAndroid Build Coastguard Worker 38*84e33947SAndroid Build Coastguard Worker self.json = json_obj 39*84e33947SAndroid Build Coastguard Worker self.structs_and_unions = {} 40*84e33947SAndroid Build Coastguard Worker self._parse_annotations() 41*84e33947SAndroid Build Coastguard Worker self._parse_api() 42*84e33947SAndroid Build Coastguard Worker 43*84e33947SAndroid Build Coastguard Worker def _parse_annotations(self): 44*84e33947SAndroid Build Coastguard Worker # Converts annotations list to a more usable data structure: dict keyed by structure name, 45*84e33947SAndroid Build Coastguard Worker # containing a dict keyed by field name, containing a list of annotations (as they 46*84e33947SAndroid Build Coastguard Worker # appear in the JSON). In other words, we can easily get all of the annotations for the 47*84e33947SAndroid Build Coastguard Worker # "version" field in "chreWwanCellInfoResult" via 48*84e33947SAndroid Build Coastguard Worker # annotations['chreWwanCellInfoResult']['version']. This is also a defaultdict, so it's safe 49*84e33947SAndroid Build Coastguard Worker # to access if there are no annotations for this structure + field; it'll just give you 50*84e33947SAndroid Build Coastguard Worker # an empty list in that case. 51*84e33947SAndroid Build Coastguard Worker 52*84e33947SAndroid Build Coastguard Worker self.annotations = defaultdict(lambda: defaultdict(list)) 53*84e33947SAndroid Build Coastguard Worker for struct_info in self.json['struct_info']: 54*84e33947SAndroid Build Coastguard Worker for annotation in struct_info['annotations']: 55*84e33947SAndroid Build Coastguard Worker self.annotations[struct_info['name'] 56*84e33947SAndroid Build Coastguard Worker ][annotation['field']].append(annotation) 57*84e33947SAndroid Build Coastguard Worker 58*84e33947SAndroid Build Coastguard Worker def _files_to_parse(self): 59*84e33947SAndroid Build Coastguard Worker """Returns a list of files to supply as input to CParser""" 60*84e33947SAndroid Build Coastguard Worker 61*84e33947SAndroid Build Coastguard Worker # Input paths for CParser are stored in JSON relative to <android_root>/system/chre 62*84e33947SAndroid Build Coastguard Worker # Reformulate these to absolute paths, and add in some default includes that we always 63*84e33947SAndroid Build Coastguard Worker # supply 64*84e33947SAndroid Build Coastguard Worker chre_project_base_dir = system_chre_abs_path() 65*84e33947SAndroid Build Coastguard Worker default_includes = ['api_parser/parser_defines.h', 66*84e33947SAndroid Build Coastguard Worker 'chre_api/include/chre_api/chre/version.h'] 67*84e33947SAndroid Build Coastguard Worker files = default_includes + \ 68*84e33947SAndroid Build Coastguard Worker self.json['includes'] + [self.json['filename']] 69*84e33947SAndroid Build Coastguard Worker return [os.path.join(chre_project_base_dir, file) for file in files] 70*84e33947SAndroid Build Coastguard Worker 71*84e33947SAndroid Build Coastguard Worker def _parse_structs_and_unions(self): 72*84e33947SAndroid Build Coastguard Worker # Starts with the root structures (i.e. those that will appear at the top-level in one 73*84e33947SAndroid Build Coastguard Worker # or more CHPP messages), build a data structure containing all of the information we'll 74*84e33947SAndroid Build Coastguard Worker # need to emit the CHPP structure definition and conversion code. 75*84e33947SAndroid Build Coastguard Worker 76*84e33947SAndroid Build Coastguard Worker structs_and_unions_to_parse = self.json['root_structs'].copy() 77*84e33947SAndroid Build Coastguard Worker while len(structs_and_unions_to_parse) > 0: 78*84e33947SAndroid Build Coastguard Worker type_name = structs_and_unions_to_parse.pop() 79*84e33947SAndroid Build Coastguard Worker if type_name in self.structs_and_unions: 80*84e33947SAndroid Build Coastguard Worker continue 81*84e33947SAndroid Build Coastguard Worker 82*84e33947SAndroid Build Coastguard Worker entry = { 83*84e33947SAndroid Build Coastguard Worker 'appears_in': set(), # Other types this type is nested within 84*84e33947SAndroid Build Coastguard Worker 'dependencies': set(), # Types that are nested in this type 85*84e33947SAndroid Build Coastguard Worker 'has_vla_member': False, # True if this type or any dependency has a VLA member 86*84e33947SAndroid Build Coastguard Worker 'members': [], # Info about each member of this type 87*84e33947SAndroid Build Coastguard Worker } 88*84e33947SAndroid Build Coastguard Worker if type_name in self.parser.defs['structs']: 89*84e33947SAndroid Build Coastguard Worker defs = self.parser.defs['structs'][type_name] 90*84e33947SAndroid Build Coastguard Worker entry['is_union'] = False 91*84e33947SAndroid Build Coastguard Worker elif type_name in self.parser.defs['unions']: 92*84e33947SAndroid Build Coastguard Worker defs = self.parser.defs['unions'][type_name] 93*84e33947SAndroid Build Coastguard Worker entry['is_union'] = True 94*84e33947SAndroid Build Coastguard Worker else: 95*84e33947SAndroid Build Coastguard Worker raise RuntimeError( 96*84e33947SAndroid Build Coastguard Worker "Couldn't find {} in parsed structs/unions".format(type_name)) 97*84e33947SAndroid Build Coastguard Worker 98*84e33947SAndroid Build Coastguard Worker for member_name, member_type, _ in defs['members']: 99*84e33947SAndroid Build Coastguard Worker member_info = { 100*84e33947SAndroid Build Coastguard Worker 'name': member_name, 101*84e33947SAndroid Build Coastguard Worker 'type': member_type, 102*84e33947SAndroid Build Coastguard Worker 'annotations': self.annotations[type_name][member_name], 103*84e33947SAndroid Build Coastguard Worker 'is_nested_type': False, 104*84e33947SAndroid Build Coastguard Worker } 105*84e33947SAndroid Build Coastguard Worker 106*84e33947SAndroid Build Coastguard Worker if member_type.type_spec.startswith('struct ') or \ 107*84e33947SAndroid Build Coastguard Worker member_type.type_spec.startswith('union '): 108*84e33947SAndroid Build Coastguard Worker member_info['is_nested_type'] = True 109*84e33947SAndroid Build Coastguard Worker member_type_name = member_type.type_spec.split(' ')[1] 110*84e33947SAndroid Build Coastguard Worker member_info['nested_type_name'] = member_type_name 111*84e33947SAndroid Build Coastguard Worker entry['dependencies'].add(member_type_name) 112*84e33947SAndroid Build Coastguard Worker structs_and_unions_to_parse.append(member_type_name) 113*84e33947SAndroid Build Coastguard Worker 114*84e33947SAndroid Build Coastguard Worker entry['members'].append(member_info) 115*84e33947SAndroid Build Coastguard Worker 116*84e33947SAndroid Build Coastguard Worker # Flip a flag if this structure has at least one variable-length array member, which 117*84e33947SAndroid Build Coastguard Worker # means that the encoded size can only be computed at runtime 118*84e33947SAndroid Build Coastguard Worker if not entry['has_vla_member']: 119*84e33947SAndroid Build Coastguard Worker for annotation in self.annotations[type_name][member_name]: 120*84e33947SAndroid Build Coastguard Worker if annotation['annotation'] == 'var_len_array': 121*84e33947SAndroid Build Coastguard Worker entry['has_vla_member'] = True 122*84e33947SAndroid Build Coastguard Worker 123*84e33947SAndroid Build Coastguard Worker self.structs_and_unions[type_name] = entry 124*84e33947SAndroid Build Coastguard Worker 125*84e33947SAndroid Build Coastguard Worker # Build reverse linkage of dependency chain (i.e. lookup between a type and the other types 126*84e33947SAndroid Build Coastguard Worker # it appears in) 127*84e33947SAndroid Build Coastguard Worker for type_name, type_info in self.structs_and_unions.items(): 128*84e33947SAndroid Build Coastguard Worker for dependency in type_info['dependencies']: 129*84e33947SAndroid Build Coastguard Worker self.structs_and_unions[dependency]['appears_in'].add( 130*84e33947SAndroid Build Coastguard Worker type_name) 131*84e33947SAndroid Build Coastguard Worker 132*84e33947SAndroid Build Coastguard Worker # Bubble up "has_vla_member" to types each type it appears in, i.e. if this flag is set to 133*84e33947SAndroid Build Coastguard Worker # True on a leaf node, then all its ancestors should also have the flag set to True 134*84e33947SAndroid Build Coastguard Worker for type_name, type_info in self.structs_and_unions.items(): 135*84e33947SAndroid Build Coastguard Worker if type_info['has_vla_member']: 136*84e33947SAndroid Build Coastguard Worker types_to_mark = list(type_info['appears_in']) 137*84e33947SAndroid Build Coastguard Worker while len(types_to_mark) > 0: 138*84e33947SAndroid Build Coastguard Worker type_to_mark = types_to_mark.pop() 139*84e33947SAndroid Build Coastguard Worker self.structs_and_unions[type_to_mark]['has_vla_member'] = True 140*84e33947SAndroid Build Coastguard Worker types_to_mark.extend( 141*84e33947SAndroid Build Coastguard Worker list(self.structs_and_unions[type_to_mark]['appears_in'])) 142*84e33947SAndroid Build Coastguard Worker 143*84e33947SAndroid Build Coastguard Worker def _parse_api(self): 144*84e33947SAndroid Build Coastguard Worker """ 145*84e33947SAndroid Build Coastguard Worker Parses the API and stores the structs and unions. 146*84e33947SAndroid Build Coastguard Worker """ 147*84e33947SAndroid Build Coastguard Worker 148*84e33947SAndroid Build Coastguard Worker file_to_parse = self._files_to_parse() 149*84e33947SAndroid Build Coastguard Worker self.parser = CParser(file_to_parse, cache='parser_cache') 150*84e33947SAndroid Build Coastguard Worker self._parse_structs_and_unions() 151