1#!/usr/bin/env python3 2# ex: set filetype=python: 3 4"""Generate code to handle XDR struct types""" 5 6from jinja2 import Environment 7 8from generators import SourceGenerator, kernel_c_type 9from generators import create_jinja2_environment, get_jinja2_template 10 11from xdr_ast import _XdrBasic, _XdrString 12from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque 13from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray 14from xdr_ast import _XdrOptionalData, _XdrStruct, _XdrDeclaration 15from xdr_ast import public_apis, get_header_name, aggregate_members 16from xdr_ast import pages_members, pages_member_maxsize, pages_member_is_decoded 17 18 19def aggregate_hook_base(struct_name: str) -> str: 20 """Return the application hook base name for a struct's aggregate members. 21 22 The pragma header name prefixes it so two programs that share a 23 type name (nfs_acl2 vs nfs_acl3) derive distinct external hook 24 symbols. All marked members of one struct share a hook set; the 25 cursor's member_id tells them apart. 26 """ 27 return "_".join((get_header_name(), struct_name)) 28 29 30def aggregate_member_symbol(struct_name: str, member_name: str) -> str: 31 """Return the symbolic member id for one marked aggregate member. 32 33 The generated framing sets the cursor's member_id to this constant 34 and the hooks compare against it. The enum in aggregate_hooks.j2 35 assigns the values implicitly in field order, so a hook that 36 compared against the bare integer would silently bind to the 37 wrong member once the specification's members were reordered. 38 """ 39 return "_".join((aggregate_hook_base(struct_name), member_name)).upper() 40 41 42def emit_struct_declaration(environment: Environment, node: _XdrStruct) -> None: 43 """Emit one declaration pair for an XDR struct type""" 44 if node.name in public_apis: 45 template = get_jinja2_template(environment, "declaration", "close") 46 print(template.render(name=node.name)) 47 marked = [ 48 field 49 for field in node.fields 50 if (node.name, field.name) in aggregate_members 51 ] 52 if marked: 53 template = get_jinja2_template( 54 environment, "declaration", "aggregate_hooks" 55 ) 56 print( 57 template.render( 58 hook=aggregate_hook_base(node.name), 59 c_type=kernel_c_type(marked[0].spec), 60 classifier=marked[0].spec.c_classifier, 61 members=[ 62 aggregate_member_symbol(node.name, field.name) 63 for field in marked 64 ], 65 ) 66 ) 67 68 69def emit_struct_member_definition( 70 environment: Environment, field: _XdrDeclaration, struct_name: str, peer: str 71) -> None: 72 """Emit a definition for one field in an XDR struct""" 73 if peer == "server" and pages_member_is_decoded(struct_name, field.name): 74 template = get_jinja2_template(environment, "definition", "pages_opaque") 75 print(template.render(name=field.name)) 76 return 77 if isinstance(field, _XdrBasic): 78 template = get_jinja2_template(environment, "definition", field.template) 79 print( 80 template.render( 81 name=field.name, 82 type=kernel_c_type(field.spec), 83 classifier=field.spec.c_classifier, 84 ) 85 ) 86 elif isinstance(field, _XdrFixedLengthOpaque): 87 template = get_jinja2_template(environment, "definition", field.template) 88 print( 89 template.render( 90 name=field.name, 91 size=field.size, 92 ) 93 ) 94 elif isinstance(field, _XdrVariableLengthOpaque): 95 template = get_jinja2_template(environment, "definition", field.template) 96 print(template.render(name=field.name)) 97 elif isinstance(field, _XdrString): 98 template = get_jinja2_template(environment, "definition", field.template) 99 print(template.render(name=field.name)) 100 elif isinstance(field, _XdrFixedLengthArray): 101 template = get_jinja2_template(environment, "definition", field.template) 102 print( 103 template.render( 104 name=field.name, 105 type=kernel_c_type(field.spec), 106 size=field.size, 107 ) 108 ) 109 elif isinstance(field, _XdrVariableLengthArray): 110 template = get_jinja2_template(environment, "definition", field.template) 111 print( 112 template.render( 113 name=field.name, 114 type=kernel_c_type(field.spec), 115 classifier=field.spec.c_classifier, 116 ) 117 ) 118 elif isinstance(field, _XdrOptionalData): 119 template = get_jinja2_template(environment, "definition", field.template) 120 print( 121 template.render( 122 name=field.name, 123 type=kernel_c_type(field.spec), 124 classifier=field.spec.c_classifier, 125 ) 126 ) 127 128 129def emit_struct_definition( 130 environment: Environment, node: _XdrStruct, peer: str 131) -> None: 132 """Emit one definition for an XDR struct type""" 133 template = get_jinja2_template(environment, "definition", "open") 134 print(template.render(name=node.name)) 135 136 for field in node.fields: 137 emit_struct_member_definition(environment, field, node.name, peer) 138 139 template = get_jinja2_template(environment, "definition", "close") 140 print(template.render(name=node.name)) 141 142 143def emit_struct_member_decoder( 144 environment: Environment, 145 field: _XdrDeclaration, 146 struct_name: str, 147 peer: str, 148) -> None: 149 """Emit a decoder for one field in an XDR struct""" 150 if peer == "server" and pages_member_is_decoded(struct_name, field.name): 151 template = get_jinja2_template(environment, "decoder", "pages_opaque") 152 print( 153 template.render( 154 name=field.name, 155 maxsize=pages_member_maxsize(field), 156 ) 157 ) 158 return 159 if isinstance(field, _XdrVariableLengthArray) and ( 160 (struct_name, field.name) in aggregate_members 161 ): 162 if peer != "server": 163 raise NotImplementedError( 164 "pragma aggregate is server-side only; " 165 + peer 166 + " generation is not yet supported" 167 ) 168 template = get_jinja2_template(environment, "decoder", "aggregate_array") 169 print( 170 template.render( 171 name=field.name, 172 type=field.spec.type_name, 173 c_type=kernel_c_type(field.spec), 174 classifier=field.spec.c_classifier, 175 maxsize=field.maxsize, 176 hook=aggregate_hook_base(struct_name), 177 member_sym=aggregate_member_symbol(struct_name, field.name), 178 ) 179 ) 180 return 181 if isinstance(field, _XdrOptionalData) and ( 182 (struct_name, field.name) in aggregate_members 183 ): 184 if peer != "server": 185 raise NotImplementedError( 186 "pragma aggregate is server-side only; " 187 + peer 188 + " generation is not yet supported" 189 ) 190 template = get_jinja2_template(environment, "decoder", "aggregate_optional") 191 print( 192 template.render( 193 name=field.name, 194 type=field.spec.type_name, 195 c_type=kernel_c_type(field.spec), 196 classifier=field.spec.c_classifier, 197 hook=aggregate_hook_base(struct_name), 198 member_sym=aggregate_member_symbol(struct_name, field.name), 199 ) 200 ) 201 return 202 if isinstance(field, _XdrBasic): 203 template = get_jinja2_template(environment, "decoder", field.template) 204 print( 205 template.render( 206 name=field.name, 207 type=field.spec.type_name, 208 classifier=field.spec.c_classifier, 209 ) 210 ) 211 elif isinstance(field, _XdrFixedLengthOpaque): 212 template = get_jinja2_template(environment, "decoder", field.template) 213 print( 214 template.render( 215 name=field.name, 216 size=field.size, 217 ) 218 ) 219 elif isinstance(field, _XdrVariableLengthOpaque): 220 template = get_jinja2_template(environment, "decoder", field.template) 221 print( 222 template.render( 223 name=field.name, 224 maxsize=field.maxsize, 225 ) 226 ) 227 elif isinstance(field, _XdrString): 228 template = get_jinja2_template(environment, "decoder", field.template) 229 print( 230 template.render( 231 name=field.name, 232 maxsize=field.maxsize, 233 ) 234 ) 235 elif isinstance(field, _XdrFixedLengthArray): 236 template = get_jinja2_template(environment, "decoder", field.template) 237 print( 238 template.render( 239 name=field.name, 240 type=field.spec.type_name, 241 size=field.size, 242 classifier=field.spec.c_classifier, 243 ) 244 ) 245 elif isinstance(field, _XdrVariableLengthArray): 246 template = get_jinja2_template(environment, "decoder", field.template) 247 print( 248 template.render( 249 name=field.name, 250 type=field.spec.type_name, 251 maxsize=field.maxsize, 252 classifier=field.spec.c_classifier, 253 ) 254 ) 255 elif isinstance(field, _XdrOptionalData): 256 template = get_jinja2_template(environment, "decoder", field.template) 257 print( 258 template.render( 259 name=field.name, 260 type=field.spec.type_name, 261 classifier=field.spec.c_classifier, 262 ) 263 ) 264 265 266def emit_struct_decoder( 267 environment: Environment, node: _XdrStruct, peer: str 268) -> None: 269 """Emit one decoder function for an XDR struct type""" 270 template = get_jinja2_template(environment, "decoder", "open") 271 print(template.render(name=node.name)) 272 273 for field in node.fields: 274 emit_struct_member_decoder(environment, field, node.name, peer) 275 # The optional-data aggregate decoder fails and closes the 276 # function; the members after it are never decoded. 277 if isinstance(field, _XdrOptionalData) and ( 278 (node.name, field.name) in aggregate_members 279 ): 280 return 281 282 template = get_jinja2_template(environment, "decoder", "close") 283 print(template.render()) 284 285 286def emit_struct_member_encoder( 287 environment: Environment, 288 field: _XdrDeclaration, 289 struct_name: str, 290 peer: str, 291) -> None: 292 """Emit an encoder for one field in an XDR struct""" 293 if isinstance(field, _XdrVariableLengthArray) and ( 294 (struct_name, field.name) in aggregate_members 295 ): 296 if peer != "server": 297 raise NotImplementedError( 298 "pragma aggregate is server-side only; " 299 + peer 300 + " generation is not yet supported" 301 ) 302 template = get_jinja2_template(environment, "encoder", "aggregate_array") 303 print( 304 template.render( 305 name=field.name, 306 type=field.spec.type_name, 307 c_type=kernel_c_type(field.spec), 308 classifier=field.spec.c_classifier, 309 maxsize=field.maxsize, 310 hook=aggregate_hook_base(struct_name), 311 member_sym=aggregate_member_symbol(struct_name, field.name), 312 ) 313 ) 314 return 315 if isinstance(field, _XdrOptionalData) and ( 316 (struct_name, field.name) in aggregate_members 317 ): 318 if peer != "server": 319 raise NotImplementedError( 320 "pragma aggregate is server-side only; " 321 + peer 322 + " generation is not yet supported" 323 ) 324 template = get_jinja2_template(environment, "encoder", "aggregate_optional") 325 print( 326 template.render( 327 name=field.name, 328 type=field.spec.type_name, 329 c_type=kernel_c_type(field.spec), 330 classifier=field.spec.c_classifier, 331 hook=aggregate_hook_base(struct_name), 332 member_sym=aggregate_member_symbol(struct_name, field.name), 333 ) 334 ) 335 return 336 if (struct_name, field.name) in pages_members: 337 if peer != "server": 338 raise NotImplementedError( 339 "pragma pages is server-side only; " 340 + peer 341 + " generation is not yet supported" 342 ) 343 # Both representations carry the length field, which is all the 344 # pages encoder reads, so pages_member_is_decoded() does not 345 # gate this site. 346 template = get_jinja2_template(environment, "encoder", "pages_opaque") 347 print( 348 template.render( 349 name=field.name, 350 maxsize=pages_member_maxsize(field), 351 ) 352 ) 353 return 354 if isinstance(field, _XdrBasic): 355 template = get_jinja2_template(environment, "encoder", field.template) 356 print( 357 template.render( 358 name=field.name, 359 type=field.spec.type_name, 360 ) 361 ) 362 elif isinstance(field, _XdrFixedLengthOpaque): 363 template = get_jinja2_template(environment, "encoder", field.template) 364 print( 365 template.render( 366 name=field.name, 367 size=field.size, 368 ) 369 ) 370 elif isinstance(field, _XdrVariableLengthOpaque): 371 template = get_jinja2_template(environment, "encoder", field.template) 372 print( 373 template.render( 374 name=field.name, 375 maxsize=field.maxsize, 376 ) 377 ) 378 elif isinstance(field, _XdrString): 379 template = get_jinja2_template(environment, "encoder", field.template) 380 print( 381 template.render( 382 name=field.name, 383 maxsize=field.maxsize, 384 ) 385 ) 386 elif isinstance(field, _XdrFixedLengthArray): 387 template = get_jinja2_template(environment, "encoder", field.template) 388 print( 389 template.render( 390 name=field.name, 391 type=field.spec.type_name, 392 size=field.size, 393 ) 394 ) 395 elif isinstance(field, _XdrVariableLengthArray): 396 template = get_jinja2_template(environment, "encoder", field.template) 397 print( 398 template.render( 399 name=field.name, 400 type=field.spec.type_name, 401 maxsize=field.maxsize, 402 ) 403 ) 404 elif isinstance(field, _XdrOptionalData): 405 template = get_jinja2_template(environment, "encoder", field.template) 406 print( 407 template.render( 408 name=field.name, 409 type=field.spec.type_name, 410 classifier=field.spec.c_classifier, 411 ) 412 ) 413 414 415def emit_struct_encoder(environment: Environment, node: _XdrStruct, peer: str) -> None: 416 """Emit one encoder function for an XDR struct type""" 417 template = get_jinja2_template(environment, "encoder", "open") 418 print(template.render(name=node.name)) 419 420 for field in node.fields: 421 emit_struct_member_encoder(environment, field, node.name, peer) 422 423 template = get_jinja2_template(environment, "encoder", "close") 424 print(template.render()) 425 426 427def emit_struct_maxsize(environment: Environment, node: _XdrStruct) -> None: 428 """Emit one maxsize macro for an XDR struct type""" 429 macro_name = get_header_name().upper() + "_" + node.name + "_sz" 430 template = get_jinja2_template(environment, "maxsize", "struct") 431 print( 432 template.render( 433 macro=macro_name, 434 width=" + ".join(node.symbolic_width()), 435 ) 436 ) 437 438 439class XdrStructGenerator(SourceGenerator): 440 """Generate source code for XDR structs""" 441 442 def __init__(self, language: str, peer: str): 443 """Initialize an instance of this class""" 444 self.environment = create_jinja2_environment(language, "struct") 445 self.peer = peer 446 447 def emit_declaration(self, node: _XdrStruct) -> None: 448 """Emit one declaration pair for an XDR struct type""" 449 emit_struct_declaration(self.environment, node) 450 451 def emit_definition(self, node: _XdrStruct) -> None: 452 """Emit one definition for an XDR struct type""" 453 emit_struct_definition(self.environment, node, self.peer) 454 455 def emit_decoder(self, node: _XdrStruct) -> None: 456 """Emit one decoder function for an XDR struct type""" 457 emit_struct_decoder(self.environment, node, self.peer) 458 459 def emit_encoder(self, node: _XdrStruct) -> None: 460 """Emit one encoder function for an XDR struct type""" 461 emit_struct_encoder(self.environment, node, self.peer) 462 463 def emit_maxsize(self, node: _XdrStruct) -> None: 464 """Emit one maxsize macro for an XDR struct type""" 465 emit_struct_maxsize(self.environment, node) 466