1# Copyright 2023 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15from typing import Optional, List, Dict, Union, Tuple, Set 16from .ast import * 17 18 19def desugar_field_(field: Field, previous: Field, constraints: Dict[str, Constraint]) -> List[Field]: 20 """Inline group and constrained fields. 21 Constrained fields are transformed into fixed fields. 22 Group fields are inlined and recursively desugared.""" 23 24 if isinstance(field, ScalarField) and field.id in constraints: 25 value = constraints[field.id].value 26 fixed = FixedField(kind='fixed_field', loc=field.loc, width=field.width, value=value) 27 fixed.parent = field.parent 28 return [fixed] 29 30 elif isinstance(field, PaddingField): 31 previous.padded_size = field.size 32 field.padded_field = previous 33 return [field] 34 35 elif isinstance(field, TypedefField) and field.id in constraints: 36 tag_id = constraints[field.id].tag_id 37 fixed = FixedField(kind='fixed_field', loc=field.loc, enum_id=field.type_id, tag_id=tag_id) 38 fixed.parent = field.parent 39 return [fixed] 40 41 elif isinstance(field, GroupField): 42 group = field.parent.file.group_scope[field.group_id] 43 constraints = dict([(c.id, c) for c in field.constraints]) 44 fields = [] 45 for f in group.fields: 46 fields.extend(desugar_field_(f, previous, constraints)) 47 previous = f 48 return fields 49 50 else: 51 return [field] 52 53 54def desugar(file: File): 55 """Inline group fields. 56 Constrained fields are transformed into fixed fields. 57 Group declarations are removed from the file object. 58 **The original file object is modified inline.**""" 59 60 declarations = [] 61 for d in file.declarations: 62 if isinstance(d, GroupDeclaration): 63 continue 64 65 if isinstance(d, (PacketDeclaration, StructDeclaration)): 66 fields = [] 67 for f in d.fields: 68 fields.extend(desugar_field_(f, fields[-1] if len(fields) > 0 else None, {})) 69 d.fields = fields 70 71 declarations.append(d) 72 73 file.declarations = declarations 74 file.group_scope = {} 75 76 77def make_reserved_field(width: int) -> ReservedField: 78 """Create a reserved field of specified width.""" 79 return ReservedField(kind='reserved_field', loc=None, width=width) 80 81 82def get_packet_field(packet: Union[PacketDeclaration, StructDeclaration], id: str) -> Optional[Field]: 83 """Return the field with selected identifier declared in the provided 84 packet or its ancestors.""" 85 id = '_payload_' if id == 'payload' else id 86 for f in packet.fields: 87 if getattr(f, 'id', None) == id: 88 return f 89 if isinstance(packet, PacketDeclaration) and packet.parent_id: 90 parent = packet.file.packet_scope[packet.parent_id] 91 return get_packet_field(parent, id) 92 elif isinstance(packet, StructDeclaration) and packet.parent_id: 93 parent = packet.file.typedef_scope[packet.parent_id] 94 return get_packet_field(parent, id) 95 else: 96 return None 97 98 99def get_packet_fields(decl: Union[PacketDeclaration, StructDeclaration]) -> List[Field]: 100 """Return the list of fields declared in the selected packet and its parents. 101 Payload fields are removed from the parent declarations.""" 102 103 fields = [] 104 if decl.parent: 105 fields = [f for f in get_packet_fields(decl.parent) if not isinstance(f, (PayloadField, BodyField))] 106 return fields + decl.fields 107 108 109def get_packet_shift(packet: Union[PacketDeclaration, StructDeclaration]) -> int: 110 """Return the bit shift of the payload or body field in the parent packet. 111 112 When using packet derivation on bit fields, the body may be shifted. 113 The shift is handled statically in the implementation of child packets, 114 and the incomplete field is included in the body. 115 ``` 116 packet Basic { 117 type: 1, 118 _body_ 119 } 120 ``` 121 """ 122 123 # Traverse empty parents. 124 parent = packet.parent 125 while parent and len(parent.fields) == 1: 126 parent = parent.parent 127 128 if not parent: 129 return 0 130 131 shift = 0 132 for f in packet.parent.fields: 133 if isinstance(f, (BodyField, PayloadField)): 134 return 0 if (shift % 8) == 0 else shift 135 else: 136 # Fields that do not have a constant size are assumed to start 137 # on a byte boundary, and measure an integral number of bytes. 138 # Start the count over. 139 size = get_field_size(f) 140 shift = 0 if size is None else shift + size 141 142 # No payload or body in parent packet. 143 # Not raising an error, the generation will fail somewhere else. 144 return 0 145 146 147def get_packet_ancestor( 148 decl: Union[PacketDeclaration, StructDeclaration]) -> Union[PacketDeclaration, StructDeclaration]: 149 """Return the root ancestor of the selected packet or struct.""" 150 if decl.parent_id is None: 151 return decl 152 else: 153 return get_packet_ancestor(decl.file.packet_scope[decl.parent_id]) 154 155 156def get_derived_packets( 157 decl: Union[PacketDeclaration, StructDeclaration], 158 traverse: bool = True, 159) -> List[Tuple[List[Constraint], Union[PacketDeclaration, StructDeclaration]]]: 160 """Return the list of packets or structs that immediately derive from the 161 selected packet or struct, coupled with the field constraints. 162 Packet aliases (containing no field declarations other than a payload) 163 are traversed.""" 164 165 children = [] 166 for d in decl.file.declarations: 167 if type(d) is type(decl) and d.parent_id == decl.id: 168 if (len(d.fields) == 1 and isinstance(d.fields[0], (PayloadField, BodyField))) and traverse: 169 children.extend([(d.constraints + sub_constraints, sub_child) 170 for (sub_constraints, sub_child) in get_derived_packets(d)]) 171 else: 172 children.append((d.constraints, d)) 173 return children 174 175 176def get_field_size(field: Field, skip_payload: bool = False) -> Optional[int]: 177 """Determine the size of a field in bits, if possible. 178 If the field is dynamically sized (e.g. unsized array or payload field), 179 None is returned instead. If skip_payload is set, payload and body fields 180 are counted as having size 0 rather than a variable size.""" 181 182 if isinstance(field, (ScalarField, SizeField, CountField, ReservedField)): 183 return field.width 184 185 elif isinstance(field, FixedField): 186 return field.width or field.type.width 187 188 elif isinstance(field, PaddingField): 189 # Padding field width is added to the padded field size. 190 return 0 191 192 elif isinstance(field, ArrayField) and field.padded_size is not None: 193 return field.padded_size * 8 194 195 elif isinstance(field, ArrayField) and field.size is not None: 196 element_width = field.width or get_declaration_size(field.type) 197 return element_width * field.size if element_width is not None else None 198 199 elif isinstance(field, TypedefField): 200 return get_declaration_size(field.type) 201 202 elif isinstance(field, ChecksumField): 203 return 0 204 205 elif isinstance(field, (PayloadField, BodyField)) and skip_payload: 206 return 0 207 208 else: 209 return None 210 211 212def get_declaration_size(decl: Declaration, skip_payload: bool = False) -> Optional[int]: 213 """Determine the size of a declaration type in bits, if possible. 214 If the type is dynamically sized (e.g. contains an array or payload), 215 None is returned instead. If skip_payload is set, payload and body fields 216 are counted as having size 0 rather than a variable size.""" 217 218 if isinstance(decl, (EnumDeclaration, CustomFieldDeclaration, ChecksumDeclaration)): 219 return decl.width 220 221 elif isinstance(decl, (PacketDeclaration, StructDeclaration)): 222 parent = decl.parent 223 packet_size = get_declaration_size(parent, skip_payload=True) if parent else 0 224 if packet_size is None: 225 return None 226 for f in decl.fields: 227 field_size = get_field_size(f, skip_payload=skip_payload) 228 if field_size is None: 229 return None 230 packet_size += field_size 231 return packet_size 232 233 else: 234 return None 235 236 237def get_array_field_size(field: ArrayField) -> Union[None, int, Field]: 238 """Return the array static size, size field, or count field. 239 If the array is unsized None is returned instead.""" 240 241 if field.size is not None: 242 return field.size 243 for f in field.parent.fields: 244 if isinstance(f, (SizeField, CountField)) and f.field_id == field.id: 245 return f 246 return None 247 248 249def get_payload_field_size(field: Union[PayloadField, BodyField]) -> Optional[Field]: 250 """Return the payload or body size field. 251 If the payload is unsized None is returned instead.""" 252 253 for f in field.parent.fields: 254 if isinstance(f, SizeField) and f.field_id == field.id: 255 return f 256 return None 257 258 259def get_array_element_size(field: ArrayField) -> Optional[int]: 260 """Return the array element size, if possible. 261 If the element size is not known at compile time, 262 None is returned instead.""" 263 264 return field.width or get_declaration_size(field.type) 265 266 267def get_field_offset_from_start(field: Field) -> Optional[int]: 268 """Return the field bit offset from the start of the parent packet, if it 269 can be statically computed. If the offset is variable None is returned 270 instead.""" 271 offset = 0 272 field_index = field.parent.fields.index(field) 273 for f in field.parent.fields[:field_index]: 274 size = get_field_size(f) 275 if size is None: 276 return None 277 278 offset += size 279 return offset 280 281 282def get_field_offset_from_end(field: Field) -> Optional[int]: 283 """Return the field bit offset from the end of the parent packet, if it 284 can be statically computed. If the offset is variable None is returned 285 instead. The selected field size is not counted towards the offset.""" 286 offset = 0 287 field_index = field.parent.fields.index(field) 288 for f in field.parent.fields[field_index + 1:]: 289 size = get_field_size(f) 290 if size is None: 291 return None 292 offset += size 293 return offset 294 295 296def get_unconstrained_parent_fields(decl: Union[PacketDeclaration, StructDeclaration]) -> List[Field]: 297 """Return the list of fields from the parent declarations that have an identifier 298 but that do not have a value fixed by any of the parent constraints. 299 The fields are returned in order of declaration.""" 300 301 def constraint_ids(constraints: List[Constraint]) -> Set[str]: 302 return set([c.id for c in constraints]) 303 304 def aux(decl: Optional[Declaration], constraints: Set[str]) -> List[Field]: 305 if decl is None: 306 return [] 307 fields = aux(decl.parent, constraints.union(constraint_ids(decl.constraints))) 308 for f in decl.fields: 309 if (isinstance(f, (ScalarField, ArrayField, TypedefField)) and not f.id in constraints): 310 fields.append(f) 311 return fields 312 313 return aux(decl.parent, constraint_ids(decl.constraints)) 314 315 316def get_parent_constraints(decl: Union[PacketDeclaration, StructDeclaration]) -> List[Constraint]: 317 """Return the list of constraints from the current and parent declarations.""" 318 parent_constraints = get_parent_constraints(decl.parent) if decl.parent else [] 319 return parent_constraints + decl.constraints 320 321 322def is_bit_field(field: Field) -> bool: 323 """Identify fields that can have bit granularity. 324 These include: ScalarField, FixedField, TypedefField with enum type, 325 SizeField, and CountField.""" 326 327 if isinstance(field, (ScalarField, SizeField, CountField, FixedField, ReservedField)): 328 return True 329 330 elif isinstance(field, TypedefField) and isinstance(field.type, EnumDeclaration): 331 return True 332 333 else: 334 return False 335