1#!/usr/bin/env python3 2# ex: set filetype=python: 3 4"""Define and implement the Abstract Syntax Tree for the XDR language.""" 5 6import sys 7from typing import List 8from dataclasses import dataclass, KW_ONLY 9 10from lark import ast_utils, Transformer 11from lark.tree import Meta 12 13this_module = sys.modules[__name__] 14 15big_endian = [] 16excluded_apis = [] 17header_name = "none" 18public_apis = [] 19structs = set() 20pass_by_reference = set() 21 22# (type_name, member_name) pairs whose variable-length array or 23# optional-data list member is marked "pragma aggregate" -- codec 24# emission streams the member through application hooks instead of 25# iterating a materialized C array. 26aggregate_members = set() 27 28# Source position of each "pragma aggregate" marker, so a directive 29# that cannot be honored is reported where it was written. 30aggregate_member_meta = {} 31 32# (type_name, member_name) pairs marked "pragma pages": the member's 33# content resides in the pages of the Receive or Reply buffer, so 34# the emitted codec captures or inserts those pages by reference 35# instead of copying. 36pages_members = set() 37 38# The same pairs, keyed to the directive's identifier node so a 39# diagnostic can point at the directive rather than at the type. 40pages_member_meta = {} 41 42# Names of types reachable from an RPC procedure argument, closed over 43# the types their members reach. A "pragma pages" member of one is 44# decoded in place rather than encoded by reference, and the two need 45# different C representations; see pages_member_is_decoded(). 46argument_types = set() 47 48# Names of types reachable from an RPC procedure result, closed the 49# same way. A marked member of a type in both sets would need both 50# representations at once, so the front end rejects it. 51result_types = set() 52 53# Typedefs of a variable-length opaque or string, mapped to the length 54# bound. A "pragma pages" member declared through one ("path data;" 55# with "typedef string path<NFS_MAXPATHLEN>") reaches the AST as a 56# basic type reference, so the bound must be recovered from here. 57varlen_object_typedefs = {} 58 59constants = {} 60 61 62def xdr_quadlen(val: str) -> int: 63 """Return integer XDR width of an XDR type""" 64 if val in constants: 65 octets = constants[val] 66 else: 67 octets = int(val) 68 return int((octets + 3) / 4) 69 70 71symbolic_widths = { 72 "void": ["XDR_void"], 73 "bool": ["XDR_bool"], 74 "short": ["XDR_short"], 75 "unsigned_short": ["XDR_unsigned_short"], 76 "int": ["XDR_int"], 77 "unsigned_int": ["XDR_unsigned_int"], 78 "long": ["XDR_long"], 79 "unsigned_long": ["XDR_unsigned_long"], 80 "hyper": ["XDR_hyper"], 81 "unsigned_hyper": ["XDR_unsigned_hyper"], 82} 83 84# Numeric XDR widths are tracked in a dictionary that is keyed 85# by type_name because sometimes a caller has nothing more than 86# the type_name to use to figure out the numeric width. 87max_widths = { 88 "void": 0, 89 "bool": 1, 90 "short": 1, 91 "unsigned_short": 1, 92 "int": 1, 93 "unsigned_int": 1, 94 "long": 1, 95 "unsigned_long": 1, 96 "hyper": 2, 97 "unsigned_hyper": 2, 98} 99 100 101@dataclass 102class _XdrAst(ast_utils.Ast): 103 """Base class for the XDR abstract syntax tree""" 104 105 # Source position of the construct's declared identifier, when 106 # the transformer records one, so semantic diagnostics can point 107 # at the exact declaration. The KW_ONLY marker makes the fields 108 # keyword-only, so they never disturb the positional child 109 # ordering lark uses to build each node; 0 means the position was 110 # not recorded. 111 _: KW_ONLY 112 line: int = 0 113 column: int = 0 114 115 116@dataclass 117class _XdrIdentifier(_XdrAst): 118 """Corresponds to 'identifier' in the XDR language grammar""" 119 120 symbol: str 121 122 123@dataclass 124class _XdrValue(_XdrAst): 125 """Corresponds to 'value' in the XDR language grammar""" 126 127 value: str 128 129 130@dataclass 131class _XdrConstantValue(_XdrAst): 132 """Corresponds to 'constant' in the XDR language grammar""" 133 134 value: int 135 136 137@dataclass 138class _XdrTypeSpecifier(_XdrAst): 139 """Corresponds to 'type_specifier' in the XDR language grammar""" 140 141 type_name: str 142 c_classifier: str = "" 143 144 145@dataclass 146class _XdrDefinedType(_XdrTypeSpecifier): 147 """Corresponds to a type defined by the input specification""" 148 149 def symbolic_width(self) -> List: 150 """Return list containing XDR width of type's components""" 151 return [get_header_name().upper() + "_" + self.type_name + "_sz"] 152 153 def __post_init__(self): 154 if self.type_name in structs: 155 self.c_classifier = "struct " 156 symbolic_widths[self.type_name] = self.symbolic_width() 157 158 159@dataclass 160class _XdrBuiltInType(_XdrTypeSpecifier): 161 """Corresponds to a built-in XDR type""" 162 163 def symbolic_width(self) -> List: 164 """Return list containing XDR width of type's components""" 165 return symbolic_widths[self.type_name] 166 167 168@dataclass 169class _XdrDeclaration(_XdrAst): 170 """Base class of XDR type declarations""" 171 172 173@dataclass 174class _XdrFixedLengthOpaque(_XdrDeclaration): 175 """A fixed-length opaque declaration""" 176 177 name: str 178 size: str 179 template: str = "fixed_length_opaque" 180 181 def max_width(self) -> int: 182 """Return width of type in XDR_UNITS""" 183 return xdr_quadlen(self.size) 184 185 def symbolic_width(self) -> List: 186 """Return list containing XDR width of type's components""" 187 return ["XDR_QUADLEN(" + self.size + ")"] 188 189 def __post_init__(self): 190 max_widths[self.name] = self.max_width() 191 symbolic_widths[self.name] = self.symbolic_width() 192 193 194@dataclass 195class _XdrVariableLengthOpaque(_XdrDeclaration): 196 """A variable-length opaque declaration""" 197 198 name: str 199 maxsize: str 200 template: str = "variable_length_opaque" 201 202 def max_width(self) -> int: 203 """Return width of type in XDR_UNITS""" 204 return 1 + xdr_quadlen(self.maxsize) 205 206 def symbolic_width(self) -> List: 207 """Return list containing XDR width of type's components""" 208 widths = ["XDR_unsigned_int"] 209 if self.maxsize != "0": 210 widths.append("XDR_QUADLEN(" + self.maxsize + ")") 211 return widths 212 213 def __post_init__(self): 214 max_widths[self.name] = self.max_width() 215 symbolic_widths[self.name] = self.symbolic_width() 216 217 218@dataclass 219class _XdrString(_XdrDeclaration): 220 """A (NUL-terminated) variable-length string declaration""" 221 222 name: str 223 maxsize: str 224 template: str = "string" 225 226 def max_width(self) -> int: 227 """Return width of type in XDR_UNITS""" 228 return 1 + xdr_quadlen(self.maxsize) 229 230 def symbolic_width(self) -> List: 231 """Return list containing XDR width of type's components""" 232 widths = ["XDR_unsigned_int"] 233 if self.maxsize != "0": 234 widths.append("XDR_QUADLEN(" + self.maxsize + ")") 235 return widths 236 237 def __post_init__(self): 238 max_widths[self.name] = self.max_width() 239 symbolic_widths[self.name] = self.symbolic_width() 240 241 242@dataclass 243class _XdrFixedLengthArray(_XdrDeclaration): 244 """A fixed-length array declaration""" 245 246 name: str 247 spec: _XdrTypeSpecifier 248 size: str 249 template: str = "fixed_length_array" 250 251 def max_width(self) -> int: 252 """Return width of type in XDR_UNITS""" 253 return xdr_quadlen(self.size) * max_widths[self.spec.type_name] 254 255 def symbolic_width(self) -> List: 256 """Return list containing XDR width of type's components""" 257 item_width = " + ".join(symbolic_widths[self.spec.type_name]) 258 return ["(" + self.size + " * (" + item_width + "))"] 259 260 def __post_init__(self): 261 max_widths[self.name] = self.max_width() 262 symbolic_widths[self.name] = self.symbolic_width() 263 264 265@dataclass 266class _XdrVariableLengthArray(_XdrDeclaration): 267 """A variable-length array declaration""" 268 269 name: str 270 spec: _XdrTypeSpecifier 271 maxsize: str 272 template: str = "variable_length_array" 273 274 def max_width(self) -> int: 275 """Return width of type in XDR_UNITS""" 276 return 1 + (xdr_quadlen(self.maxsize) * max_widths[self.spec.type_name]) 277 278 def symbolic_width(self) -> List: 279 """Return list containing XDR width of type's components""" 280 widths = ["XDR_unsigned_int"] 281 if self.maxsize != "0": 282 item_width = " + ".join(symbolic_widths[self.spec.type_name]) 283 widths.append("(" + self.maxsize + " * (" + item_width + "))") 284 return widths 285 286 def __post_init__(self): 287 max_widths[self.name] = self.max_width() 288 symbolic_widths[self.name] = self.symbolic_width() 289 290 291@dataclass 292class _XdrOptionalData(_XdrDeclaration): 293 """An 'optional_data' declaration""" 294 295 name: str 296 spec: _XdrTypeSpecifier 297 template: str = "optional_data" 298 299 def max_width(self) -> int: 300 """Return width of type in XDR_UNITS""" 301 return 1 302 303 def symbolic_width(self) -> List: 304 """Return list containing XDR width of type's components""" 305 return ["XDR_bool"] 306 307 def __post_init__(self): 308 structs.add(self.name) 309 pass_by_reference.add(self.name) 310 max_widths[self.name] = self.max_width() 311 symbolic_widths[self.name] = self.symbolic_width() 312 313 314@dataclass 315class _XdrBasic(_XdrDeclaration): 316 """A 'basic' declaration""" 317 318 name: str 319 spec: _XdrTypeSpecifier 320 template: str = "basic" 321 322 def max_width(self) -> int: 323 """Return width of type in XDR_UNITS""" 324 return max_widths[self.spec.type_name] 325 326 def symbolic_width(self) -> List: 327 """Return list containing XDR width of type's components""" 328 return symbolic_widths[self.spec.type_name] 329 330 def __post_init__(self): 331 max_widths[self.name] = self.max_width() 332 symbolic_widths[self.name] = self.symbolic_width() 333 334 335@dataclass 336class _XdrVoid(_XdrDeclaration): 337 """A void declaration""" 338 339 name: str = "void" 340 template: str = "void" 341 342 def max_width(self) -> int: 343 """Return width of type in XDR_UNITS""" 344 return 0 345 346 def symbolic_width(self) -> List: 347 """Return list containing XDR width of type's components""" 348 return [] 349 350 351@dataclass 352class _XdrConstant(_XdrAst): 353 """Corresponds to 'constant_def' in the grammar""" 354 355 name: str 356 value: str 357 358 def __post_init__(self): 359 if self.value not in constants: 360 constants[self.name] = int(self.value, 0) 361 362 363@dataclass 364class _XdrEnumerator(_XdrAst): 365 """An 'identifier = value' enumerator""" 366 367 name: str 368 value: str 369 370 def __post_init__(self): 371 if self.value not in constants: 372 constants[self.name] = int(self.value, 0) 373 374 375@dataclass 376class _XdrEnum(_XdrAst): 377 """An XDR enum definition""" 378 379 name: str 380 enumerators: List[_XdrEnumerator] 381 382 def max_width(self) -> int: 383 """Return width of type in XDR_UNITS""" 384 return 1 385 386 def symbolic_width(self) -> List: 387 """Return list containing XDR width of type's components""" 388 return ["XDR_int"] 389 390 def __post_init__(self): 391 max_widths[self.name] = self.max_width() 392 symbolic_widths[self.name] = self.symbolic_width() 393 394 395@dataclass 396class _XdrStruct(_XdrAst): 397 """An XDR struct definition""" 398 399 name: str 400 fields: List[_XdrDeclaration] 401 402 def max_width(self) -> int: 403 """Return width of type in XDR_UNITS""" 404 width = 0 405 for field in self.fields: 406 width += field.max_width() 407 return width 408 409 def symbolic_width(self) -> List: 410 """Return list containing XDR width of type's components""" 411 widths = [] 412 for field in self.fields: 413 widths += field.symbolic_width() 414 return widths 415 416 def __post_init__(self): 417 structs.add(self.name) 418 pass_by_reference.add(self.name) 419 max_widths[self.name] = self.max_width() 420 symbolic_widths[self.name] = self.symbolic_width() 421 422 423@dataclass 424class _XdrPointer(_XdrAst): 425 """An XDR pointer definition""" 426 427 name: str 428 fields: List[_XdrDeclaration] 429 430 def max_width(self) -> int: 431 """Return width of type in XDR_UNITS""" 432 width = 1 433 for field in self.fields[0:-1]: 434 width += field.max_width() 435 return width 436 437 def symbolic_width(self) -> List: 438 """Return list containing XDR width of type's components""" 439 widths = [] 440 widths += ["XDR_bool"] 441 for field in self.fields[0:-1]: 442 widths += field.symbolic_width() 443 return widths 444 445 def __post_init__(self): 446 structs.add(self.name) 447 pass_by_reference.add(self.name) 448 max_widths[self.name] = self.max_width() 449 symbolic_widths[self.name] = self.symbolic_width() 450 451 452@dataclass 453class _XdrTypedef(_XdrAst): 454 """An XDR typedef""" 455 456 declaration: _XdrDeclaration 457 458 def max_width(self) -> int: 459 """Return width of type in XDR_UNITS""" 460 return self.declaration.max_width() 461 462 def symbolic_width(self) -> List: 463 """Return list containing XDR width of type's components""" 464 return self.declaration.symbolic_width() 465 466 def __post_init__(self): 467 if isinstance(self.declaration, (_XdrVariableLengthOpaque, _XdrString)): 468 varlen_object_typedefs[self.declaration.name] = self.declaration.maxsize 469 if isinstance(self.declaration, _XdrBasic): 470 new_type = self.declaration 471 if isinstance(new_type.spec, _XdrDefinedType): 472 if new_type.spec.type_name in pass_by_reference: 473 pass_by_reference.add(new_type.name) 474 if new_type.spec.type_name in varlen_object_typedefs: 475 varlen_object_typedefs[new_type.name] = varlen_object_typedefs[ 476 new_type.spec.type_name 477 ] 478 max_widths[new_type.name] = self.max_width() 479 symbolic_widths[new_type.name] = self.symbolic_width() 480 481 482def pages_member_maxsize(field: _XdrDeclaration): 483 """Return the length bound of a "pragma pages" member. 484 485 Return None when FIELD is neither a variable-length opaque nor a 486 string, whether declared inline or through a typedef. 487 """ 488 if isinstance(field, (_XdrVariableLengthOpaque, _XdrString)): 489 return field.maxsize 490 if isinstance(field, _XdrBasic) and field.spec.type_name in varlen_object_typedefs: 491 return varlen_object_typedefs[field.spec.type_name] 492 return None 493 494 495def pages_member_is_decoded(struct_name: str, member_name: str) -> bool: 496 """Return True when a "pragma pages" member is decoded in place. 497 498 A server decodes arguments and encodes results. A page-resident 499 member of an argument type is decoded from the pages of the 500 Receive buffer into an xdr_buf that captures the content by 501 reference; one of a result type is encoded from the Reply buffer's 502 pages and keeps the ordinary opaque or string representation. 503 """ 504 return (struct_name, member_name) in pages_members and ( 505 struct_name in argument_types 506 ) 507 508 509@dataclass 510class _XdrCaseSpec(_XdrAst): 511 """One case in an XDR union""" 512 513 values: List[str] 514 arm: _XdrDeclaration 515 template: str = "case_spec" 516 517 518@dataclass 519class _XdrDefaultSpec(_XdrAst): 520 """Default case in an XDR union""" 521 522 arm: _XdrDeclaration 523 template: str = "default_spec" 524 525 526@dataclass 527class _XdrUnion(_XdrAst): 528 """An XDR union""" 529 530 name: str 531 discriminant: _XdrDeclaration 532 cases: List[_XdrCaseSpec] 533 default: _XdrDeclaration 534 535 def max_width(self) -> int: 536 """Return width of type in XDR_UNITS""" 537 max_width = 0 538 for case in self.cases: 539 if case.arm.max_width() > max_width: 540 max_width = case.arm.max_width() 541 if self.default: 542 if self.default.arm.max_width() > max_width: 543 max_width = self.default.arm.max_width() 544 return 1 + max_width 545 546 def symbolic_width(self) -> List: 547 """Return list containing XDR width of type's components""" 548 max_width = 0 549 for case in self.cases: 550 if case.arm.max_width() > max_width: 551 max_width = case.arm.max_width() 552 width = case.arm.symbolic_width() 553 if self.default: 554 if self.default.arm.max_width() > max_width: 555 max_width = self.default.arm.max_width() 556 width = self.default.arm.symbolic_width() 557 return symbolic_widths[self.discriminant.name] + width 558 559 def __post_init__(self): 560 structs.add(self.name) 561 pass_by_reference.add(self.name) 562 max_widths[self.name] = self.max_width() 563 symbolic_widths[self.name] = self.symbolic_width() 564 565 566@dataclass 567class _RpcProcedure(_XdrAst): 568 """RPC procedure definition""" 569 570 name: str 571 number: int 572 argument: _XdrTypeSpecifier 573 result: _XdrTypeSpecifier 574 575 576@dataclass 577class _RpcVersion(_XdrAst): 578 """RPC version definition""" 579 580 name: str 581 number: int 582 procedures: List[_RpcProcedure] 583 584 585@dataclass 586class _RpcProgram(_XdrAst): 587 """RPC program definition""" 588 589 name: str 590 number: int 591 versions: List[_RpcVersion] 592 593 594@dataclass 595class _Pragma(_XdrAst): 596 """Empty class for pragma directives""" 597 598 599@dataclass 600class _XdrPassthru(_XdrAst): 601 """Passthrough line to emit verbatim in output""" 602 603 content: str 604 605 606@dataclass 607class Definition(_XdrAst, ast_utils.WithMeta): 608 """Corresponds to 'definition' in the grammar""" 609 610 meta: Meta 611 value: _XdrAst 612 613 614@dataclass 615class Specification(_XdrAst, ast_utils.AsList): 616 """Corresponds to 'specification' in the grammar""" 617 618 definitions: List[Definition] 619 620 621class ParseToAst(Transformer): 622 """Functions that transform productions into AST nodes""" 623 624 def identifier(self, children): 625 """Instantiate one _XdrIdentifier object""" 626 token = children[0] 627 return _XdrIdentifier(token.value, line=token.line, column=token.column) 628 629 def value(self, children): 630 """Instantiate one _XdrValue object""" 631 if isinstance(children[0], _XdrIdentifier): 632 return _XdrValue(children[0].symbol) 633 return _XdrValue(children[0].children[0].value) 634 635 def constant(self, children): 636 """Instantiate one _XdrConstantValue object""" 637 match children[0].data: 638 case "decimal_constant": 639 value = int(children[0].children[0].value, base=10) 640 case "hexadecimal_constant": 641 value = int(children[0].children[0].value, base=16) 642 case "octal_constant": 643 value = int(children[0].children[0].value, base=8) 644 return _XdrConstantValue(value) 645 646 def type_specifier(self, children): 647 """Instantiate one _XdrTypeSpecifier object""" 648 if isinstance(children[0], _XdrIdentifier): 649 name = children[0].symbol 650 return _XdrDefinedType(type_name=name) 651 652 name = children[0].data.value 653 return _XdrBuiltInType(type_name=name) 654 655 def constant_def(self, children): 656 """Instantiate one _XdrConstant object""" 657 ident = children[0] 658 value = children[1].value 659 return _XdrConstant(ident.symbol, value, line=ident.line, column=ident.column) 660 661 def enum(self, children): 662 """Instantiate one _XdrEnum object""" 663 name_ident = children[0] 664 665 i = 0 666 enumerators = [] 667 body = children[1] 668 while i < len(body.children): 669 ident = body.children[i] 670 value = body.children[i + 1].value 671 enumerators.append( 672 _XdrEnumerator( 673 ident.symbol, value, line=ident.line, column=ident.column 674 ) 675 ) 676 i = i + 2 677 678 return _XdrEnum( 679 name_ident.symbol, 680 enumerators, 681 line=name_ident.line, 682 column=name_ident.column, 683 ) 684 685 def fixed_length_opaque(self, children): 686 """Instantiate one _XdrFixedLengthOpaque declaration object""" 687 ident = children[0] 688 size = children[1].value 689 690 return _XdrFixedLengthOpaque( 691 ident.symbol, size, line=ident.line, column=ident.column 692 ) 693 694 def variable_length_opaque(self, children): 695 """Instantiate one _XdrVariableLengthOpaque declaration object""" 696 ident = children[0] 697 if children[1] is not None: 698 maxsize = children[1].value 699 else: 700 maxsize = "0" 701 702 return _XdrVariableLengthOpaque( 703 ident.symbol, maxsize, line=ident.line, column=ident.column 704 ) 705 706 def string(self, children): 707 """Instantiate one _XdrString declaration object""" 708 ident = children[0] 709 if children[1] is not None: 710 maxsize = children[1].value 711 else: 712 maxsize = "0" 713 714 return _XdrString(ident.symbol, maxsize, line=ident.line, column=ident.column) 715 716 def fixed_length_array(self, children): 717 """Instantiate one _XdrFixedLengthArray declaration object""" 718 spec = children[0] 719 ident = children[1] 720 size = children[2].value 721 722 return _XdrFixedLengthArray( 723 ident.symbol, spec, size, line=ident.line, column=ident.column 724 ) 725 726 def variable_length_array(self, children): 727 """Instantiate one _XdrVariableLengthArray declaration object""" 728 spec = children[0] 729 ident = children[1] 730 if children[2] is not None: 731 maxsize = children[2].value 732 else: 733 maxsize = "0" 734 735 return _XdrVariableLengthArray( 736 ident.symbol, spec, maxsize, line=ident.line, column=ident.column 737 ) 738 739 def optional_data(self, children): 740 """Instantiate one _XdrOptionalData declaration object""" 741 spec = children[0] 742 ident = children[1] 743 744 return _XdrOptionalData( 745 ident.symbol, spec, line=ident.line, column=ident.column 746 ) 747 748 def basic(self, children): 749 """Instantiate one _XdrBasic object""" 750 spec = children[0] 751 ident = children[1] 752 753 return _XdrBasic(ident.symbol, spec, line=ident.line, column=ident.column) 754 755 def void(self, children): 756 """Instantiate one _XdrVoid declaration object""" 757 758 return _XdrVoid() 759 760 def struct(self, children): 761 """Instantiate one _XdrStruct object""" 762 ident = children[0] 763 name = ident.symbol 764 fields = children[1].children 765 pos = {"line": ident.line, "column": ident.column} 766 767 last_field = fields[-1] 768 if ( 769 isinstance(last_field, _XdrOptionalData) 770 and name == last_field.spec.type_name 771 ): 772 return _XdrPointer(name, fields, **pos) 773 774 return _XdrStruct(name, fields, **pos) 775 776 def typedef(self, children): 777 """Instantiate one _XdrTypedef object""" 778 new_type = children[0] 779 780 return _XdrTypedef(new_type) 781 782 def case_spec(self, children): 783 """Instantiate one _XdrCaseSpec object""" 784 values = [] 785 for item in children[0:-1]: 786 values.append(item.value) 787 arm = children[-1] 788 789 return _XdrCaseSpec(values, arm) 790 791 def default_spec(self, children): 792 """Instantiate one _XdrDefaultSpec object""" 793 arm = children[0] 794 795 return _XdrDefaultSpec(arm) 796 797 def union(self, children): 798 """Instantiate one _XdrUnion object""" 799 ident = children[0] 800 801 body = children[1] 802 discriminant = body.children[0].children[0] 803 cases = body.children[1:-1] 804 default = body.children[-1] 805 806 return _XdrUnion( 807 ident.symbol, 808 discriminant, 809 cases, 810 default, 811 line=ident.line, 812 column=ident.column, 813 ) 814 815 def procedure_def(self, children): 816 """Instantiate one _RpcProcedure object""" 817 result = children[0] 818 ident = children[1] 819 argument = children[2] 820 number = children[3].value 821 822 argument_types.add(argument.type_name) 823 result_types.add(result.type_name) 824 return _RpcProcedure( 825 ident.symbol, 826 number, 827 argument, 828 result, 829 line=ident.line, 830 column=ident.column, 831 ) 832 833 def version_def(self, children): 834 """Instantiate one _RpcVersion object""" 835 ident = children[0] 836 number = children[-1].value 837 procedures = children[1:-1] 838 839 return _RpcVersion( 840 ident.symbol, number, procedures, line=ident.line, column=ident.column 841 ) 842 843 def program_def(self, children): 844 """Instantiate one _RpcProgram object""" 845 ident = children[0] 846 number = children[-1].value 847 versions = children[1:-1] 848 849 return _RpcProgram( 850 ident.symbol, number, versions, line=ident.line, column=ident.column 851 ) 852 853 def pragma_def(self, children): 854 """Instantiate one _Pragma object""" 855 directive = children[0].children[0].data 856 match directive: 857 case "big_endian_directive": 858 big_endian.append(children[1].symbol) 859 case "exclude_directive": 860 excluded_apis.append(children[1].symbol) 861 case "header_directive": 862 global header_name 863 header_name = children[1].symbol 864 case "public_directive": 865 public_apis.append(children[1].symbol) 866 case "aggregate_directive": 867 if children[2] is None: 868 raise XdrSemanticError( 869 "pragma aggregate requires a type name and a member name", 870 children[1], 871 ) 872 marked = (children[1].symbol, children[2].symbol) 873 aggregate_members.add(marked) 874 aggregate_member_meta[marked] = children[2] 875 case "pages_directive": 876 if children[2] is None: 877 raise XdrSemanticError( 878 "pragma pages requires a type name and a member name", 879 children[1], 880 ) 881 marked = (children[1].symbol, children[2].symbol) 882 pages_members.add(marked) 883 pages_member_meta[marked] = children[2] 884 case _: 885 raise NotImplementedError("Directive not supported") 886 return _Pragma() 887 888 def passthru_def(self, children): 889 """Instantiate one _XdrPassthru object""" 890 token = children[0] 891 content = token.value[1:] 892 return _XdrPassthru(content) 893 894 895transformer = ast_utils.create_transformer(this_module, ParseToAst()) 896 897 898def _merge_consecutive_passthru(definitions: List[Definition]) -> List[Definition]: 899 """Merge consecutive passthru definitions into single nodes""" 900 result = [] 901 i = 0 902 while i < len(definitions): 903 if isinstance(definitions[i].value, _XdrPassthru): 904 lines = [definitions[i].value.content] 905 meta = definitions[i].meta 906 j = i + 1 907 while j < len(definitions) and isinstance( 908 definitions[j].value, _XdrPassthru 909 ): 910 lines.append(definitions[j].value.content) 911 j += 1 912 merged = _XdrPassthru("\n".join(lines)) 913 result.append(Definition(meta, merged)) 914 i = j 915 else: 916 result.append(definitions[i]) 917 i += 1 918 return result 919 920 921def _meta_line(meta) -> int: 922 """Return the 1-based source line for a node's meta, or 0 if unknown""" 923 try: 924 return meta.line 925 except AttributeError: 926 return 0 927 928 929class XdrSemanticError(Exception): 930 """A specification that parses but violates an XDR semantic rule. 931 932 Detection lives in the language-independent front end because a 933 duplicate name is malformed XDR regardless of the output language. 934 """ 935 936 def __init__(self, message: str, meta): 937 super().__init__(message) 938 self.message = message 939 self.line = _meta_line(meta) 940 self.column = getattr(meta, "column", 0) 941 942 943def _introduced_names(value): 944 """Yield (name, node) for each identifier a definition introduces.""" 945 if isinstance(value, (_XdrStruct, _XdrUnion, _XdrPointer)): 946 yield value.name, value 947 elif isinstance(value, _XdrEnum): 948 yield value.name, value 949 for enumerator in value.enumerators: 950 yield enumerator.name, enumerator 951 elif isinstance(value, _XdrTypedef): 952 yield value.declaration.name, value.declaration 953 elif isinstance(value, _XdrConstant): 954 yield value.name, value 955 elif isinstance(value, _RpcProgram): 956 yield value.name, value 957 958 959def _check_rpc_scope_names(program: "_RpcProgram") -> None: 960 """Enforce RFC 5531 Section 12.3 scoping within an RPC program. 961 962 A version name and number are unique within the program and a 963 procedure name and number are unique within its version. 964 """ 965 version_names = set() 966 version_numbers = set() 967 for version in program.versions: 968 if version.name in version_names: 969 raise XdrSemanticError( 970 f"duplicate version name '{version.name}'" 971 f" in program '{program.name}'", 972 version, 973 ) 974 version_names.add(version.name) 975 if version.number in version_numbers: 976 raise XdrSemanticError( 977 f"duplicate version number {version.number}" 978 f" in program '{program.name}'", 979 version, 980 ) 981 version_numbers.add(version.number) 982 procedure_names = set() 983 procedure_numbers = set() 984 for procedure in version.procedures: 985 if procedure.name in procedure_names: 986 raise XdrSemanticError( 987 f"duplicate procedure name '{procedure.name}'" 988 f" in version '{version.name}'", 989 procedure, 990 ) 991 procedure_names.add(procedure.name) 992 if procedure.number in procedure_numbers: 993 raise XdrSemanticError( 994 f"duplicate procedure number {procedure.number}" 995 f" in version '{version.name}'", 996 procedure, 997 ) 998 procedure_numbers.add(procedure.number) 999 1000 1001def check_duplicate_definitions(root: "Specification") -> None: 1002 """Reject a spec that declares an identifier more than once. 1003 1004 RFC 4506 Section 6.4 places constant and type identifiers in a 1005 single name space that must be unique within a specification. 1006 RFC 5531 Section 12.3 adds RPC program names to that name space 1007 and scopes version names and numbers to their program and 1008 procedure names and numbers to their version. 1009 """ 1010 seen = {} 1011 for definition in root.definitions: 1012 for name, node in _introduced_names(definition.value): 1013 where = node if node.line else definition.meta 1014 first = seen.get(name) 1015 if first is not None: 1016 raise XdrSemanticError( 1017 f"duplicate identifier '{name}'" 1018 f" (first declared at line {_meta_line(first)})", 1019 where, 1020 ) 1021 seen[name] = where 1022 if isinstance(definition.value, _RpcProgram): 1023 _check_rpc_scope_names(definition.value) 1024 1025 1026# RFC 5531 (Section 9) encodes program, version, and procedure numbers 1027# as unsigned 32-bit integers, so each must fall within [0, 2**32 - 1]. 1028_RPC_NUMBER_MAX = 2**32 - 1 1029 1030 1031def _check_rpc_number(kind: str, number: int, scope: str, meta) -> None: 1032 """Reject one RPC number that is negative or wider than 32 bits.""" 1033 if number < 0: 1034 raise XdrSemanticError( 1035 f"negative {kind} number {number} {scope}", 1036 meta, 1037 ) 1038 if number > _RPC_NUMBER_MAX: 1039 raise XdrSemanticError( 1040 f"{kind} number {number} {scope} exceeds {_RPC_NUMBER_MAX}", 1041 meta, 1042 ) 1043 1044 1045def check_rpc_number_range(root: "Specification") -> None: 1046 """Reject an out-of-range program, version, or procedure number. 1047 1048 RFC 5531 assigns only unsigned constants to program, version, and 1049 procedure numbers (Section 12.3) and encodes each as an unsigned 1050 32-bit integer (Section 9). RFC 4506 Section 6.2 permits a signed 1051 decimal constant for XDR constants in general and sets no ceiling on 1052 magnitude, so the grammar accepts an out-of-range value; the range 1053 is enforced here instead. The parser retains no per-version or 1054 per-procedure source location, so a violation is reported against the 1055 program definition. 1056 """ 1057 for definition in root.definitions: 1058 program = definition.value 1059 if not isinstance(program, _RpcProgram): 1060 continue 1061 _check_rpc_number( 1062 "program", 1063 program.number, 1064 f"in program '{program.name}'", 1065 definition.meta, 1066 ) 1067 for version in program.versions: 1068 _check_rpc_number( 1069 "version", 1070 version.number, 1071 f"in program '{program.name}'", 1072 definition.meta, 1073 ) 1074 for procedure in version.procedures: 1075 _check_rpc_number( 1076 "procedure", 1077 procedure.number, 1078 f"in version '{version.name}'", 1079 definition.meta, 1080 ) 1081 1082 1083def _pages_candidate_members(value): 1084 """Yield (member_name, declaration, container) for a type's members. 1085 1086 CONTAINER is the AST node holding the declaration; it decides how 1087 the member is rendered in the target language. 1088 """ 1089 if isinstance(value, _XdrStruct): 1090 for field in value.fields: 1091 yield field.name, field, value 1092 elif isinstance(value, _XdrPointer): 1093 # The trailing field is the self-reference that makes this an 1094 # XDR pointer type; the emitter does not encode it. 1095 for field in value.fields[0:-1]: 1096 yield field.name, field, value 1097 elif isinstance(value, _XdrUnion): 1098 cases = list(value.cases) 1099 if value.default is not None: 1100 cases.append(value.default) 1101 for case in cases: 1102 if not isinstance(case.arm, _XdrVoid): 1103 yield case.arm.name, case.arm, value 1104 1105 1106def check_pages_directives(root: "Specification") -> None: 1107 """Reject a "pragma pages" directive that cannot be honored. 1108 1109 Run in the front end so the diagnostic carries the directive's own 1110 source position rather than surfacing as a traceback from whichever 1111 emitter reaches the member. A directive that binds to nothing would 1112 otherwise degrade silently to the copying codec. 1113 """ 1114 payloads = {} 1115 resolved = set() 1116 for definition in root.definitions: 1117 value = definition.value 1118 type_name = getattr(value, "name", None) 1119 members = dict( 1120 (name, (field, container)) 1121 for name, field, container in _pages_candidate_members(value) 1122 ) 1123 for marked in sorted(pages_members): 1124 if marked[0] != type_name: 1125 continue 1126 meta = pages_member_meta.get(marked) 1127 if marked[1] not in members: 1128 raise XdrSemanticError( 1129 f"type '{type_name}' has no member '{marked[1]}'", 1130 meta, 1131 ) 1132 field, container = members[marked[1]] 1133 # A union arm declared directly as a string is generated as 1134 # a char *, with no length field for the page encoder to 1135 # read, and the union generator has no arm representation 1136 # for a direct opaque at all; inside a struct either 1137 # becomes a { len, data } object. 1138 if isinstance(container, _XdrUnion) and isinstance( 1139 field, (_XdrString, _XdrVariableLengthOpaque) 1140 ): 1141 raise XdrSemanticError( 1142 f"union arm '{type_name}.{marked[1]}' is declared" 1143 " directly as a string or opaque; declare it through" 1144 " a typedef instead", 1145 meta, 1146 ) 1147 # A decoded member and an encoded one take different C 1148 # representations, and a type carries only one. 1149 if type_name in argument_types and type_name in result_types: 1150 raise XdrSemanticError( 1151 f"'{type_name}' is reachable from both an RPC" 1152 " argument and an RPC result; a page-resident" 1153 " member needs distinct argument and result types", 1154 meta, 1155 ) 1156 # The union generator does not consult 1157 # pages_member_is_decoded(), so a marked arm would reach the 1158 # copying decoder and pull the payload through the bounded 1159 # scratch buffer. 1160 if isinstance(container, _XdrUnion) and type_name in argument_types: 1161 raise XdrSemanticError( 1162 f"union arm '{type_name}.{marked[1]}' cannot be" 1163 " decoded in place; declare a page-resident member" 1164 " of an argument type in a struct instead", 1165 meta, 1166 ) 1167 if pages_member_maxsize(field) is None: 1168 raise XdrSemanticError( 1169 f"'{type_name}.{marked[1]}' is neither a" 1170 " variable-length opaque nor a string", 1171 meta, 1172 ) 1173 # svcxdr_encode_opaque_payload() consumes the whole page 1174 # vector and moves the stream into the tail; a second 1175 # payload would overwrite the first one's framing. 1176 first = payloads.get(type_name) 1177 if first is not None: 1178 raise XdrSemanticError( 1179 f"'{type_name}' already marks member '{first}' as" 1180 " page-resident; an encoder emits at most one" 1181 " page-resident payload", 1182 meta, 1183 ) 1184 payloads[type_name] = marked[1] 1185 resolved.add(marked) 1186 1187 for marked in sorted(pages_members - resolved): 1188 raise XdrSemanticError( 1189 f"pragma pages names unknown type '{marked[0]}'", 1190 pages_member_meta.get(marked), 1191 ) 1192 1193 _check_pages_containment(root, payloads) 1194 1195 1196def _member_type_names(value) -> list: 1197 """Return the type names a definition's members reference, one 1198 entry per member so a type used twice is counted twice. An array 1199 member's encoder emits the element type once per element, so its 1200 element type is counted twice as well.""" 1201 if isinstance(value, _XdrStruct): 1202 fields = value.fields 1203 elif isinstance(value, _XdrPointer): 1204 # The trailing self-reference is list framing that the 1205 # caller's loop walks, not a member the pointer's own 1206 # encoder emits. 1207 fields = value.fields[0:-1] 1208 elif isinstance(value, _XdrUnion): 1209 fields = [case.arm for case in value.cases] 1210 if value.default: 1211 fields.append(value.default.arm) 1212 elif isinstance(value, _XdrTypedef): 1213 fields = [value.declaration] 1214 else: 1215 return [] 1216 names = [] 1217 for field in fields: 1218 spec = getattr(field, "spec", None) 1219 if spec is not None: 1220 names.append(spec.type_name) 1221 if isinstance(field, (_XdrFixedLengthArray, _XdrVariableLengthArray)): 1222 names.append(spec.type_name) 1223 return names 1224 1225 1226def _reachable_payloads(type_name, members, payloads, active) -> list: 1227 """Return the marked members an encoder for TYPE_NAME emits, one 1228 entry per path that reaches one, so a marked type held by two 1229 members appears twice.""" 1230 if type_name in active: 1231 return [] 1232 active.add(type_name) 1233 found = [] 1234 if type_name in payloads: 1235 found.append((type_name, payloads[type_name])) 1236 for member_type in members.get(type_name, ()): 1237 found += _reachable_payloads(member_type, members, payloads, active) 1238 active.discard(type_name) 1239 return found 1240 1241 1242def _check_pages_containment(root: "Specification", payloads: dict) -> None: 1243 """Reject a type whose encoder reaches more than one page-resident 1244 payload through the types its members contain. 1245 1246 The one-payload check in check_pages_directives() sees only the 1247 directly marked type. A type that holds two members of a marked 1248 type, or members of two marked types, would call 1249 svcxdr_encode_opaque_payload() once per member and overwrite the 1250 first payload's framing just as a second marked member would. 1251 """ 1252 members = {} 1253 for definition in root.definitions: 1254 value = definition.value 1255 name = getattr(value, "name", None) 1256 if name is None and isinstance(value, _XdrTypedef): 1257 name = value.declaration.name 1258 if name is not None: 1259 members[name] = _member_type_names(value) 1260 1261 for name in members: 1262 reached = _reachable_payloads(name, members, payloads, set()) 1263 if len(reached) > 1: 1264 raise XdrSemanticError( 1265 f"'{name}' reaches {len(reached)} page-resident payloads" 1266 " through its members; an encoder emits at most one" 1267 " page-resident payload", 1268 pages_member_meta.get(reached[1]), 1269 ) 1270 1271 1272def _named_type_definitions(root: "Specification") -> dict: 1273 """Return the specification's named type definitions, keyed by 1274 the name a member's type specifier refers to.""" 1275 named = {} 1276 for definition in root.definitions: 1277 value = definition.value 1278 if isinstance(value, _XdrTypedef): 1279 named[value.declaration.name] = value 1280 elif isinstance(value, (_XdrStruct, _XdrUnion, _XdrPointer)): 1281 named[value.name] = value 1282 return named 1283 1284 1285def _unsized_member(type_name: str, named_types: dict, seen: set): 1286 """Return "<type>.<member>" for the first variable-length array or 1287 optional-data member reachable from TYPE_NAME, or None when the 1288 type's storage is entirely inline.""" 1289 if type_name in seen: 1290 return None 1291 seen.add(type_name) 1292 value = named_types.get(type_name) 1293 if isinstance(value, _XdrTypedef): 1294 declaration = value.declaration 1295 if isinstance(declaration, (_XdrVariableLengthArray, _XdrOptionalData)): 1296 return declaration.name 1297 spec = getattr(declaration, "spec", None) 1298 if spec is None: 1299 return None 1300 return _unsized_member(spec.type_name, named_types, seen) 1301 if isinstance(value, _XdrStruct): 1302 fields = value.fields 1303 elif isinstance(value, _XdrPointer): 1304 # The trailing self-reference is list framing the element 1305 # decoder does not decode, so it needs no storage. 1306 fields = value.fields[0:-1] 1307 elif isinstance(value, _XdrUnion): 1308 fields = [case.arm for case in value.cases] 1309 if value.default: 1310 fields.append(value.default.arm) 1311 else: 1312 return None 1313 for field in fields: 1314 if isinstance(field, (_XdrVariableLengthArray, _XdrOptionalData)): 1315 return f"{type_name}.{field.name}" 1316 spec = getattr(field, "spec", None) 1317 if spec is None: 1318 continue 1319 found = _unsized_member(spec.type_name, named_types, seen) 1320 if found is not None: 1321 return found 1322 return None 1323 1324 1325def check_aggregate_directives(root: "Specification") -> None: 1326 """Reject a "pragma aggregate" directive that cannot be honored. 1327 1328 As with the pages checks, this runs in the front end so a directive 1329 naming a missing type or member, or a member with no hook-driven 1330 codec, is reported at its own source position. Left to the 1331 emitters, an unbound directive would degrade silently to the 1332 materializing codec, and a malformed one would surface as a 1333 traceback from whichever emitter reached it. 1334 """ 1335 if aggregate_members and header_name == "none": 1336 raise XdrSemanticError( 1337 "pragma aggregate derives its external hook symbols from the" 1338 " pragma header name, which this specification does not set", 1339 aggregate_member_meta.get(min(aggregate_members)), 1340 ) 1341 1342 named_types = _named_type_definitions(root) 1343 resolved = set() 1344 for definition in root.definitions: 1345 value = definition.value 1346 if not isinstance(value, _XdrStruct): 1347 continue 1348 fields = dict((field.name, field) for field in value.fields) 1349 element_type = None 1350 for marked in sorted(aggregate_members): 1351 if marked[0] != value.name: 1352 continue 1353 meta = aggregate_member_meta.get(marked) 1354 if marked[1] not in fields: 1355 raise XdrSemanticError( 1356 f"type '{value.name}' has no member '{marked[1]}'", 1357 meta, 1358 ) 1359 field = fields[marked[1]] 1360 # Any other member form would emit hook prototypes that 1361 # nothing calls. 1362 if not isinstance( 1363 field, (_XdrVariableLengthArray, _XdrOptionalData) 1364 ): 1365 raise XdrSemanticError( 1366 f"'{value.name}.{marked[1]}' is neither a" 1367 " variable-length array nor an optional-data list", 1368 meta, 1369 ) 1370 if isinstance(field, _XdrVariableLengthArray): 1371 # The generated decoder hands each element to the 1372 # ordinary element decoder from a zero-filled local, so 1373 # a nested variable-length array or optional-data 1374 # member, which that decoder fills through a pointer 1375 # the caller was to supply, would be written through 1376 # NULL. 1377 nested = _unsized_member( 1378 field.spec.type_name, named_types, set() 1379 ) 1380 if nested is not None: 1381 raise XdrSemanticError( 1382 f"'{value.name}.{marked[1]}' has element type" 1383 f" '{field.spec.type_name}', which reaches the" 1384 f" variable-length or optional-data member" 1385 f" '{nested}'; the aggregate decoder has no" 1386 " storage for it", 1387 meta, 1388 ) 1389 # The decoder hands the wire count to the begin hook 1390 # before any element is decoded, and a begin hook may 1391 # size storage from it, so the count needs a bound the 1392 # framing enforces. 1393 if field.maxsize == "0": 1394 raise XdrSemanticError( 1395 f"'{value.name}.{marked[1]}' declares no maximum" 1396 " size; the aggregate decoder hands the element" 1397 " count to the begin hook unbounded", 1398 meta, 1399 ) 1400 else: 1401 # The optional-data encoder passes each element by 1402 # address and closes the list by encoding NULL, so the 1403 # element codec must be one that takes a pointer and 1404 # writes the value-follows framing itself. 1405 if not isinstance( 1406 named_types.get(field.spec.type_name), _XdrPointer 1407 ): 1408 raise XdrSemanticError( 1409 f"'{value.name}.{marked[1]}' has element type" 1410 f" '{field.spec.type_name}', which is not a pointer" 1411 " type; an optional-data aggregate needs an element" 1412 " codec that writes the value-follows framing", 1413 meta, 1414 ) 1415 # The emitted optional-data decoder fails outright, so 1416 # an argument type carrying one could never be decoded. 1417 if value.name in argument_types: 1418 raise XdrSemanticError( 1419 f"'{value.name}' is reachable from an RPC argument," 1420 f" and the optional-data aggregate '{marked[1]}'" 1421 " cannot be decoded", 1422 meta, 1423 ) 1424 if element_type is None: 1425 element_type = field.spec.type_name 1426 elif field.spec.type_name != element_type: 1427 raise XdrSemanticError( 1428 f"'{value.name}.{marked[1]}' has element type" 1429 f" '{field.spec.type_name}', but '{value.name}' already" 1430 f" marks a member of element type '{element_type}';" 1431 " one hook set serves all of a type's marked members", 1432 meta, 1433 ) 1434 resolved.add(marked) 1435 1436 for marked in sorted(aggregate_members - resolved): 1437 raise XdrSemanticError( 1438 f"pragma aggregate names unknown struct '{marked[0]}'", 1439 aggregate_member_meta.get(marked), 1440 ) 1441 1442 1443def _referenced_type_names(value) -> set: 1444 """Return the type names an aggregate references through its 1445 members.""" 1446 if isinstance(value, (_XdrStruct, _XdrPointer)): 1447 fields = value.fields 1448 elif isinstance(value, _XdrUnion): 1449 fields = [case.arm for case in value.cases] 1450 if value.default: 1451 fields.append(value.default.arm) 1452 else: 1453 return set() 1454 names = set() 1455 for field in fields: 1456 spec = getattr(field, "spec", None) 1457 if spec is not None: 1458 names.add(spec.type_name) 1459 return names 1460 1461 1462def _expand_procedure_types(root: "Specification") -> None: 1463 """Close argument_types and result_types over the types reachable 1464 from an RPC argument or result. 1465 1466 A "pragma pages" member decoded in place can sit in a struct nested 1467 within an argument type (symlinkdata3 within SYMLINK3args), so the 1468 top-level argument types alone do not suffice. 1469 """ 1470 references = {} 1471 for definition in root.definitions: 1472 value = definition.value 1473 name = getattr(value, "name", None) 1474 if name is not None: 1475 references[name] = _referenced_type_names(value) 1476 elif isinstance(value, _XdrTypedef): 1477 # An argument named through an alias reaches whatever 1478 # aggregate the alias resolves to. 1479 declaration = value.declaration 1480 spec = getattr(declaration, "spec", None) 1481 if spec is not None: 1482 references[declaration.name] = {spec.type_name} 1483 1484 for types in (argument_types, result_types): 1485 worklist = list(types) 1486 while worklist: 1487 for referenced in references.get(worklist.pop(), ()): 1488 if referenced in references and referenced not in types: 1489 types.add(referenced) 1490 worklist.append(referenced) 1491 1492 1493def transform_parse_tree(parse_tree): 1494 """Transform productions into an abstract syntax tree""" 1495 ast = transformer.transform(parse_tree) 1496 ast.definitions = _merge_consecutive_passthru(ast.definitions) 1497 check_duplicate_definitions(ast) 1498 check_rpc_number_range(ast) 1499 # The pages checks consult argument_types and result_types, so 1500 # close those sets over the nested aggregates before the 1501 # directives are validated. 1502 _expand_procedure_types(ast) 1503 check_pages_directives(ast) 1504 check_aggregate_directives(ast) 1505 return ast 1506 1507 1508def get_header_name() -> str: 1509 """Return header name set by pragma header directive""" 1510 return header_name 1511