1# Copyright (C) 2013 Google Inc. All rights reserved. 2# 3# Redistribution and use in source and binary forms, with or without 4# modification, are permitted provided that the following conditions are 5# met: 6# 7# * Redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer. 9# * Redistributions in binary form must reproduce the above 10# copyright notice, this list of conditions and the following disclaimer 11# in the documentation and/or other materials provided with the 12# distribution. 13# * Neither the name of Google Inc. nor the names of its 14# contributors may be used to endorse or promote products derived from 15# this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29"""Blink IDL Intermediate Representation (IR) classes. 30 31Classes are primarily constructors, which build an IdlDefinitions object 32(and various contained objects) from an AST (produced by blink_idl_parser). 33 34This is in two steps: 35* Constructors walk the AST, creating objects. 36* Typedef resolution. 37 38Typedefs are all resolved here, and not stored in IR. 39 40Typedef resolution uses some auxiliary classes and OOP techniques to make this 41a generic call, via the resolve_typedefs() method. 42 43Class hierarchy (mostly containment, '<' for inheritance): 44 45IdlDefinitions 46 IdlCallbackFunction < TypedObject 47 IdlEnum :: FIXME: remove, just use a dict for enums 48 IdlInterface 49 IdlAttribute < TypedObject 50 IdlConstant < TypedObject 51 IdlLiteral 52 IdlOperation < TypedObject 53 IdlArgument < TypedObject 54 IdlStringifier 55 IdlException < IdlInterface 56 (same contents as IdlInterface) 57 58TypedObject :: mixin for typedef resolution 59 60Design doc: http://www.chromium.org/developers/design-documents/idl-compiler 61""" 62 63import abc 64 65from idl_types import IdlType, IdlUnionType, IdlArrayType, IdlSequenceType, IdlNullableType 66 67SPECIAL_KEYWORD_LIST = ['GETTER', 'SETTER', 'DELETER'] 68STANDARD_TYPEDEFS = { 69 # http://www.w3.org/TR/WebIDL/#common-DOMTimeStamp 70 'DOMTimeStamp': 'unsigned long long', 71} 72 73 74################################################################################ 75# TypedObject (mixin for typedef resolution) 76################################################################################ 77 78class TypedObject(object): 79 """Object with a type, such as an Attribute or Operation (return value). 80 81 The type can be an actual type, or can be a typedef, which must be resolved 82 before passing data to the code generator. 83 """ 84 __metaclass__ = abc.ABCMeta 85 idl_type = None 86 87 def resolve_typedefs(self, typedefs): 88 """Resolve typedefs to actual types in the object.""" 89 # Constructors don't have their own return type, because it's the 90 # interface itself. 91 if not self.idl_type: 92 return 93 # Need to re-assign self.idl_type, not just mutate idl_type, 94 # since type(idl_type) may change. 95 self.idl_type = self.idl_type.resolve_typedefs(typedefs) 96 97 98################################################################################ 99# Definitions (main container class) 100################################################################################ 101 102class IdlDefinitions(object): 103 def __init__(self, idl_name, node): 104 """Args: node: AST root node, class == 'File'""" 105 self.callback_functions = {} 106 self.dictionaries = {} 107 self.enumerations = {} 108 self.interfaces = {} 109 self.idl_name = idl_name 110 111 node_class = node.GetClass() 112 if node_class != 'File': 113 raise ValueError('Unrecognized node class: %s' % node_class) 114 115 typedefs = dict((typedef_name, IdlType(type_name)) 116 for typedef_name, type_name in 117 STANDARD_TYPEDEFS.iteritems()) 118 119 children = node.GetChildren() 120 for child in children: 121 child_class = child.GetClass() 122 if child_class == 'Interface': 123 interface = IdlInterface(idl_name, child) 124 self.interfaces[interface.name] = interface 125 elif child_class == 'Exception': 126 exception = IdlException(idl_name, child) 127 # For simplicity, treat exceptions as interfaces 128 self.interfaces[exception.name] = exception 129 elif child_class == 'Typedef': 130 type_name = child.GetName() 131 typedefs[type_name] = typedef_node_to_type(child) 132 elif child_class == 'Enum': 133 enumeration = IdlEnum(idl_name, child) 134 self.enumerations[enumeration.name] = enumeration 135 elif child_class == 'Callback': 136 callback_function = IdlCallbackFunction(idl_name, child) 137 self.callback_functions[callback_function.name] = callback_function 138 elif child_class == 'Implements': 139 # Implements is handled at the interface merging step 140 pass 141 elif child_class == 'Dictionary': 142 dictionary = IdlDictionary(idl_name, child) 143 self.dictionaries[dictionary.name] = dictionary 144 else: 145 raise ValueError('Unrecognized node class: %s' % child_class) 146 147 # Typedefs are not stored in IR: 148 # Resolve typedefs with the actual types and then discard the Typedefs. 149 # http://www.w3.org/TR/WebIDL/#idl-typedefs 150 self.resolve_typedefs(typedefs) 151 152 def resolve_typedefs(self, typedefs): 153 for callback_function in self.callback_functions.itervalues(): 154 callback_function.resolve_typedefs(typedefs) 155 for interface in self.interfaces.itervalues(): 156 interface.resolve_typedefs(typedefs) 157 158 def update(self, other): 159 """Update with additional IdlDefinitions.""" 160 for interface_name, new_interface in other.interfaces.iteritems(): 161 if not new_interface.is_partial: 162 # Add as new interface 163 self.interfaces[interface_name] = new_interface 164 continue 165 166 # Merge partial to existing interface 167 try: 168 self.interfaces[interface_name].merge(new_interface) 169 except KeyError: 170 raise Exception('Tried to merge partial interface for {0}, ' 171 'but no existing interface by that name' 172 .format(interface_name)) 173 174 # Merge callbacks and enumerations 175 self.enumerations.update(other.enumerations) 176 self.callback_functions.update(other.callback_functions) 177 178 179################################################################################ 180# Callback Functions 181################################################################################ 182 183class IdlCallbackFunction(TypedObject): 184 def __init__(self, idl_name, node): 185 children = node.GetChildren() 186 num_children = len(children) 187 if num_children != 2: 188 raise ValueError('Expected 2 children, got %s' % num_children) 189 type_node, arguments_node = children 190 arguments_node_class = arguments_node.GetClass() 191 if arguments_node_class != 'Arguments': 192 raise ValueError('Expected Arguments node, got %s' % arguments_node_class) 193 194 self.idl_name = idl_name 195 self.name = node.GetName() 196 self.idl_type = type_node_to_type(type_node) 197 self.arguments = arguments_node_to_arguments(idl_name, arguments_node) 198 199 def resolve_typedefs(self, typedefs): 200 TypedObject.resolve_typedefs(self, typedefs) 201 for argument in self.arguments: 202 argument.resolve_typedefs(typedefs) 203 204 205################################################################################ 206# Dictionary 207################################################################################ 208 209class IdlDictionary(object): 210 def __init__(self, idl_name, node): 211 self.extended_attributes = {} 212 self.is_partial = node.GetProperty('Partial') or False 213 self.idl_name = idl_name 214 self.name = node.GetName() 215 self.members = [] 216 self.parent = None 217 for child in node.GetChildren(): 218 child_class = child.GetClass() 219 if child_class == 'Inherit': 220 self.parent = child.GetName() 221 elif child_class == 'Key': 222 self.members.append(IdlDictionaryMember(idl_name, child)) 223 elif child_class == 'ExtAttributes': 224 self.extended_attributes = ( 225 ext_attributes_node_to_extended_attributes(idl_name, child)) 226 else: 227 raise ValueError('Unrecognized node class: %s' % child_class) 228 229 230class IdlDictionaryMember(object): 231 def __init__(self, idl_name, node): 232 self.default_value = None 233 self.extended_attributes = {} 234 self.idl_type = None 235 self.idl_name = idl_name 236 self.name = node.GetName() 237 for child in node.GetChildren(): 238 child_class = child.GetClass() 239 if child_class == 'Type': 240 self.idl_type = type_node_to_type(child) 241 elif child_class == 'Default': 242 self.default_value = default_node_to_idl_literal(child) 243 elif child_class == 'ExtAttributes': 244 self.extended_attributes = ( 245 ext_attributes_node_to_extended_attributes(idl_name, child)) 246 else: 247 raise ValueError('Unrecognized node class: %s' % child_class) 248 249 250################################################################################ 251# Enumerations 252################################################################################ 253 254class IdlEnum(object): 255 # FIXME: remove, just treat enums as a dictionary 256 def __init__(self, idl_name, node): 257 self.idl_name = idl_name 258 self.name = node.GetName() 259 self.values = [] 260 for child in node.GetChildren(): 261 self.values.append(child.GetName()) 262 263 264################################################################################ 265# Interfaces and Exceptions 266################################################################################ 267 268class IdlInterface(object): 269 def __init__(self, idl_name, node=None): 270 self.attributes = [] 271 self.constants = [] 272 self.constructors = [] 273 self.custom_constructors = [] 274 self.extended_attributes = {} 275 self.operations = [] 276 self.parent = None 277 self.stringifier = None 278 if not node: # Early exit for IdlException.__init__ 279 return 280 281 self.is_callback = node.GetProperty('CALLBACK') or False 282 self.is_exception = False 283 # FIXME: uppercase 'Partial' => 'PARTIAL' in base IDL parser 284 self.is_partial = node.GetProperty('Partial') or False 285 self.idl_name = idl_name 286 self.name = node.GetName() 287 288 children = node.GetChildren() 289 for child in children: 290 child_class = child.GetClass() 291 if child_class == 'Attribute': 292 self.attributes.append(IdlAttribute(idl_name, child)) 293 elif child_class == 'Const': 294 self.constants.append(IdlConstant(idl_name, child)) 295 elif child_class == 'ExtAttributes': 296 extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child) 297 self.constructors, self.custom_constructors = ( 298 extended_attributes_to_constructors(idl_name, extended_attributes)) 299 clear_constructor_attributes(extended_attributes) 300 self.extended_attributes = extended_attributes 301 elif child_class == 'Operation': 302 self.operations.append(IdlOperation(idl_name, child)) 303 elif child_class == 'Inherit': 304 self.parent = child.GetName() 305 elif child_class == 'Stringifier': 306 self.stringifier = IdlStringifier(idl_name, child) 307 self.process_stringifier() 308 else: 309 raise ValueError('Unrecognized node class: %s' % child_class) 310 311 def resolve_typedefs(self, typedefs): 312 for attribute in self.attributes: 313 attribute.resolve_typedefs(typedefs) 314 for constant in self.constants: 315 constant.resolve_typedefs(typedefs) 316 for constructor in self.constructors: 317 constructor.resolve_typedefs(typedefs) 318 for custom_constructor in self.custom_constructors: 319 custom_constructor.resolve_typedefs(typedefs) 320 for operation in self.operations: 321 operation.resolve_typedefs(typedefs) 322 323 def process_stringifier(self): 324 """Add the stringifier's attribute or named operation child, if it has 325 one, as a regular attribute/operation of this interface.""" 326 if self.stringifier.attribute: 327 self.attributes.append(self.stringifier.attribute) 328 elif self.stringifier.operation: 329 self.operations.append(self.stringifier.operation) 330 331 def merge(self, other): 332 """Merge in another interface's members (e.g., partial interface)""" 333 self.attributes.extend(other.attributes) 334 self.constants.extend(other.constants) 335 self.operations.extend(other.operations) 336 337 338class IdlException(IdlInterface): 339 # Properly exceptions and interfaces are distinct, and thus should inherit a 340 # common base class (say, "IdlExceptionOrInterface"). 341 # However, there is only one exception (DOMException), and new exceptions 342 # are not expected. Thus it is easier to implement exceptions as a 343 # restricted subclass of interfaces. 344 # http://www.w3.org/TR/WebIDL/#idl-exceptions 345 def __init__(self, idl_name, node): 346 # Exceptions are similar to Interfaces, but simpler 347 IdlInterface.__init__(self, idl_name) 348 self.is_callback = False 349 self.is_exception = True 350 self.is_partial = False 351 self.idl_name = idl_name 352 self.name = node.GetName() 353 354 children = node.GetChildren() 355 for child in children: 356 child_class = child.GetClass() 357 if child_class == 'Attribute': 358 attribute = IdlAttribute(idl_name, child) 359 self.attributes.append(attribute) 360 elif child_class == 'Const': 361 self.constants.append(IdlConstant(idl_name, child)) 362 elif child_class == 'ExtAttributes': 363 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child) 364 elif child_class == 'ExceptionOperation': 365 self.operations.append(IdlOperation.from_exception_operation_node(idl_name, child)) 366 else: 367 raise ValueError('Unrecognized node class: %s' % child_class) 368 369 370################################################################################ 371# Attributes 372################################################################################ 373 374class IdlAttribute(TypedObject): 375 def __init__(self, idl_name, node): 376 self.is_read_only = node.GetProperty('READONLY') or False 377 self.is_static = node.GetProperty('STATIC') or False 378 self.idl_name = idl_name 379 self.name = node.GetName() 380 # Defaults, overridden below 381 self.idl_type = None 382 self.extended_attributes = {} 383 384 children = node.GetChildren() 385 for child in children: 386 child_class = child.GetClass() 387 if child_class == 'Type': 388 self.idl_type = type_node_to_type(child) 389 elif child_class == 'ExtAttributes': 390 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child) 391 else: 392 raise ValueError('Unrecognized node class: %s' % child_class) 393 394 395################################################################################ 396# Constants 397################################################################################ 398 399class IdlConstant(TypedObject): 400 def __init__(self, idl_name, node): 401 children = node.GetChildren() 402 num_children = len(children) 403 if num_children < 2 or num_children > 3: 404 raise ValueError('Expected 2 or 3 children, got %s' % num_children) 405 type_node = children[0] 406 value_node = children[1] 407 value_node_class = value_node.GetClass() 408 if value_node_class != 'Value': 409 raise ValueError('Expected Value node, got %s' % value_node_class) 410 411 self.idl_name = idl_name 412 self.name = node.GetName() 413 # ConstType is more limited than Type, so subtree is smaller and 414 # we don't use the full type_node_to_type function. 415 self.idl_type = type_node_inner_to_type(type_node) 416 # FIXME: This code is unnecessarily complicated due to the rather 417 # inconsistent way the upstream IDL parser outputs default values. 418 # http://crbug.com/374178 419 if value_node.GetProperty('TYPE') == 'float': 420 self.value = value_node.GetProperty('VALUE') 421 else: 422 self.value = value_node.GetName() 423 424 if num_children == 3: 425 ext_attributes_node = children[2] 426 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, ext_attributes_node) 427 else: 428 self.extended_attributes = {} 429 430 431################################################################################ 432# Literals 433################################################################################ 434 435class IdlLiteral(object): 436 def __init__(self, idl_type, value): 437 self.idl_type = idl_type 438 self.value = value 439 self.is_null = False 440 441 def __str__(self): 442 if self.idl_type == 'DOMString': 443 return 'String("%s")' % self.value 444 if self.idl_type == 'integer': 445 return '%d' % self.value 446 if self.idl_type == 'float': 447 return '%g' % self.value 448 if self.idl_type == 'boolean': 449 return 'true' if self.value else 'false' 450 raise ValueError('Unsupported literal type: %s' % self.idl_type) 451 452 453class IdlLiteralNull(IdlLiteral): 454 def __init__(self): 455 self.idl_type = 'NULL' 456 self.value = None 457 self.is_null = True 458 459 def __str__(self): 460 return 'nullptr' 461 462 463def default_node_to_idl_literal(node): 464 # FIXME: This code is unnecessarily complicated due to the rather 465 # inconsistent way the upstream IDL parser outputs default values. 466 # http://crbug.com/374178 467 idl_type = node.GetProperty('TYPE') 468 if idl_type == 'DOMString': 469 value = node.GetProperty('NAME') 470 if '"' in value or '\\' in value: 471 raise ValueError('Unsupported string value: %r' % value) 472 return IdlLiteral(idl_type, value) 473 if idl_type == 'integer': 474 return IdlLiteral(idl_type, int(node.GetProperty('NAME'), base=0)) 475 if idl_type == 'float': 476 return IdlLiteral(idl_type, float(node.GetProperty('VALUE'))) 477 if idl_type == 'boolean': 478 return IdlLiteral(idl_type, node.GetProperty('VALUE')) 479 if idl_type == 'NULL': 480 return IdlLiteralNull() 481 raise ValueError('Unrecognized default value type: %s' % idl_type) 482 483 484################################################################################ 485# Operations 486################################################################################ 487 488class IdlOperation(TypedObject): 489 def __init__(self, idl_name, node=None): 490 self.arguments = [] 491 self.extended_attributes = {} 492 self.specials = [] 493 self.is_constructor = False 494 495 if not node: 496 self.is_static = False 497 return 498 self.idl_name = idl_name 499 self.name = node.GetName() # FIXME: should just be: or '' 500 # FIXME: AST should use None internally 501 if self.name == '_unnamed_': 502 self.name = '' 503 504 self.is_static = node.GetProperty('STATIC') or False 505 property_dictionary = node.GetProperties() 506 for special_keyword in SPECIAL_KEYWORD_LIST: 507 if special_keyword in property_dictionary: 508 self.specials.append(special_keyword.lower()) 509 510 self.idl_type = None 511 children = node.GetChildren() 512 for child in children: 513 child_class = child.GetClass() 514 if child_class == 'Arguments': 515 self.arguments = arguments_node_to_arguments(idl_name, child) 516 elif child_class == 'Type': 517 self.idl_type = type_node_to_type(child) 518 elif child_class == 'ExtAttributes': 519 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child) 520 else: 521 raise ValueError('Unrecognized node class: %s' % child_class) 522 523 @classmethod 524 def from_exception_operation_node(cls, idl_name, node): 525 # Needed to handle one case in DOMException.idl: 526 # // Override in a Mozilla compatible format 527 # [NotEnumerable] DOMString toString(); 528 # FIXME: can we remove this? replace with a stringifier? 529 operation = cls(idl_name) 530 operation.name = node.GetName() 531 children = node.GetChildren() 532 if len(children) < 1 or len(children) > 2: 533 raise ValueError('ExceptionOperation node with %s children, expected 1 or 2' % len(children)) 534 535 type_node = children[0] 536 operation.idl_type = type_node_to_type(type_node) 537 538 if len(children) > 1: 539 ext_attributes_node = children[1] 540 operation.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, ext_attributes_node) 541 542 return operation 543 544 @classmethod 545 def constructor_from_arguments_node(cls, name, idl_name, arguments_node): 546 constructor = cls(idl_name) 547 constructor.name = name 548 constructor.arguments = arguments_node_to_arguments(idl_name, arguments_node) 549 constructor.is_constructor = True 550 return constructor 551 552 def resolve_typedefs(self, typedefs): 553 TypedObject.resolve_typedefs(self, typedefs) 554 for argument in self.arguments: 555 argument.resolve_typedefs(typedefs) 556 557 558################################################################################ 559# Arguments 560################################################################################ 561 562class IdlArgument(TypedObject): 563 def __init__(self, idl_name, node): 564 self.extended_attributes = {} 565 self.idl_type = None 566 self.is_optional = node.GetProperty('OPTIONAL') # syntax: (optional T) 567 self.is_variadic = False # syntax: (T...) 568 self.idl_name = idl_name 569 self.name = node.GetName() 570 self.default_value = None 571 572 children = node.GetChildren() 573 for child in children: 574 child_class = child.GetClass() 575 if child_class == 'Type': 576 self.idl_type = type_node_to_type(child) 577 elif child_class == 'ExtAttributes': 578 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child) 579 elif child_class == 'Argument': 580 child_name = child.GetName() 581 if child_name != '...': 582 raise ValueError('Unrecognized Argument node; expected "...", got "%s"' % child_name) 583 self.is_variadic = child.GetProperty('ELLIPSIS') or False 584 elif child_class == 'Default': 585 self.default_value = default_node_to_idl_literal(child) 586 else: 587 raise ValueError('Unrecognized node class: %s' % child_class) 588 589 590def arguments_node_to_arguments(idl_name, node): 591 # [Constructor] and [CustomConstructor] without arguments (the bare form) 592 # have None instead of an arguments node, but have the same meaning as using 593 # an empty argument list, [Constructor()], so special-case this. 594 # http://www.w3.org/TR/WebIDL/#Constructor 595 if node is None: 596 return [] 597 return [IdlArgument(idl_name, argument_node) 598 for argument_node in node.GetChildren()] 599 600 601################################################################################ 602# Stringifiers 603################################################################################ 604 605class IdlStringifier(object): 606 def __init__(self, idl_name, node): 607 self.attribute = None 608 self.operation = None 609 self.extended_attributes = {} 610 self.idl_name = idl_name 611 612 for child in node.GetChildren(): 613 child_class = child.GetClass() 614 if child_class == 'Attribute': 615 self.attribute = IdlAttribute(idl_name, child) 616 elif child_class == 'Operation': 617 operation = IdlOperation(idl_name, child) 618 if operation.name: 619 self.operation = operation 620 elif child_class == 'ExtAttributes': 621 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child) 622 else: 623 raise ValueError('Unrecognized node class: %s' % child_class) 624 625 # Copy the stringifier's extended attributes (such as [Unforgable]) onto 626 # the underlying attribute or operation, if there is one. 627 if self.attribute or self.operation: 628 (self.attribute or self.operation).extended_attributes.update( 629 self.extended_attributes) 630 631 632################################################################################ 633# Extended attributes 634################################################################################ 635 636def ext_attributes_node_to_extended_attributes(idl_name, node): 637 """ 638 Returns: 639 Dictionary of {ExtAttributeName: ExtAttributeValue}. 640 Value is usually a string, with these exceptions: 641 Constructors: value is a list of Arguments nodes, corresponding to 642 possible signatures of the constructor. 643 CustomConstructors: value is a list of Arguments nodes, corresponding to 644 possible signatures of the custom constructor. 645 NamedConstructor: value is a Call node, corresponding to the single 646 signature of the named constructor. 647 SetWrapperReferenceTo: value is an Arguments node. 648 """ 649 # Primarily just make a dictionary from the children. 650 # The only complexity is handling various types of constructors: 651 # Constructors and Custom Constructors can have duplicate entries due to 652 # overloading, and thus are stored in temporary lists. 653 # However, Named Constructors cannot be overloaded, and thus do not have 654 # a list. 655 # FIXME: move Constructor logic into separate function, instead of modifying 656 # extended attributes in-place. 657 constructors = [] 658 custom_constructors = [] 659 extended_attributes = {} 660 661 def child_node(extended_attribute_node): 662 children = extended_attribute_node.GetChildren() 663 if not children: 664 return None 665 if len(children) > 1: 666 raise ValueError('ExtAttributes node with %s children, expected at most 1' % len(children)) 667 return children[0] 668 669 extended_attribute_node_list = node.GetChildren() 670 for extended_attribute_node in extended_attribute_node_list: 671 name = extended_attribute_node.GetName() 672 child = child_node(extended_attribute_node) 673 child_class = child and child.GetClass() 674 if name == 'Constructor': 675 if child_class and child_class != 'Arguments': 676 raise ValueError('Constructor only supports Arguments as child, but has child of class: %s' % child_class) 677 constructors.append(child) 678 elif name == 'CustomConstructor': 679 if child_class and child_class != 'Arguments': 680 raise ValueError('[CustomConstructor] only supports Arguments as child, but has child of class: %s' % child_class) 681 custom_constructors.append(child) 682 elif name == 'NamedConstructor': 683 if child_class and child_class != 'Call': 684 raise ValueError('[NamedConstructor] only supports Call as child, but has child of class: %s' % child_class) 685 extended_attributes[name] = child 686 elif name == 'SetWrapperReferenceTo': 687 if not child: 688 raise ValueError('[SetWrapperReferenceTo] requires a child, but has none.') 689 if child_class != 'Arguments': 690 raise ValueError('[SetWrapperReferenceTo] only supports Arguments as child, but has child of class: %s' % child_class) 691 extended_attributes[name] = arguments_node_to_arguments(idl_name, child) 692 elif child: 693 raise ValueError('ExtAttributes node with unexpected children: %s' % name) 694 else: 695 value = extended_attribute_node.GetProperty('VALUE') 696 extended_attributes[name] = value 697 698 # Store constructors and custom constructors in special list attributes, 699 # which are deleted later. Note plural in key. 700 if constructors: 701 extended_attributes['Constructors'] = constructors 702 if custom_constructors: 703 extended_attributes['CustomConstructors'] = custom_constructors 704 705 return extended_attributes 706 707 708def extended_attributes_to_constructors(idl_name, extended_attributes): 709 """Returns constructors and custom_constructors (lists of IdlOperations). 710 711 Auxiliary function for IdlInterface.__init__. 712 """ 713 714 constructor_list = extended_attributes.get('Constructors', []) 715 constructors = [ 716 IdlOperation.constructor_from_arguments_node('Constructor', idl_name, arguments_node) 717 for arguments_node in constructor_list] 718 719 custom_constructor_list = extended_attributes.get('CustomConstructors', []) 720 custom_constructors = [ 721 IdlOperation.constructor_from_arguments_node('CustomConstructor', idl_name, arguments_node) 722 for arguments_node in custom_constructor_list] 723 724 if 'NamedConstructor' in extended_attributes: 725 # FIXME: support overloaded named constructors, and make homogeneous 726 name = 'NamedConstructor' 727 call_node = extended_attributes['NamedConstructor'] 728 extended_attributes['NamedConstructor'] = call_node.GetName() 729 children = call_node.GetChildren() 730 if len(children) != 1: 731 raise ValueError('NamedConstructor node expects 1 child, got %s.' % len(children)) 732 arguments_node = children[0] 733 named_constructor = IdlOperation.constructor_from_arguments_node('NamedConstructor', idl_name, arguments_node) 734 # FIXME: should return named_constructor separately; appended for Perl 735 constructors.append(named_constructor) 736 737 return constructors, custom_constructors 738 739 740def clear_constructor_attributes(extended_attributes): 741 # Deletes Constructor*s* (plural), sets Constructor (singular) 742 if 'Constructors' in extended_attributes: 743 del extended_attributes['Constructors'] 744 extended_attributes['Constructor'] = None 745 if 'CustomConstructors' in extended_attributes: 746 del extended_attributes['CustomConstructors'] 747 extended_attributes['CustomConstructor'] = None 748 749 750################################################################################ 751# Types 752################################################################################ 753 754def type_node_to_type(node): 755 children = node.GetChildren() 756 if len(children) < 1 or len(children) > 2: 757 raise ValueError('Type node expects 1 or 2 children (type + optional array []), got %s (multi-dimensional arrays are not supported).' % len(children)) 758 759 base_type = type_node_inner_to_type(children[0]) 760 761 if node.GetProperty('NULLABLE'): 762 base_type = IdlNullableType(base_type) 763 764 if len(children) == 2: 765 array_node = children[1] 766 array_node_class = array_node.GetClass() 767 if array_node_class != 'Array': 768 raise ValueError('Expected Array node as TypeSuffix, got %s node.' % array_node_class) 769 array_type = IdlArrayType(base_type) 770 if array_node.GetProperty('NULLABLE'): 771 return IdlNullableType(array_type) 772 return array_type 773 774 return base_type 775 776 777def type_node_inner_to_type(node): 778 node_class = node.GetClass() 779 # Note Type*r*ef, not Typedef, meaning the type is an identifier, thus 780 # either a typedef shorthand (but not a Typedef declaration itself) or an 781 # interface type. We do not distinguish these, and just use the type name. 782 if node_class in ['PrimitiveType', 'Typeref']: 783 # unrestricted syntax: unrestricted double | unrestricted float 784 is_unrestricted = node.GetProperty('UNRESTRICTED') or False 785 return IdlType(node.GetName(), is_unrestricted=is_unrestricted) 786 elif node_class == 'Any': 787 return IdlType('any') 788 elif node_class == 'Sequence': 789 return sequence_node_to_type(node) 790 elif node_class == 'UnionType': 791 return union_type_node_to_idl_union_type(node) 792 raise ValueError('Unrecognized node class: %s' % node_class) 793 794 795def sequence_node_to_type(node): 796 children = node.GetChildren() 797 if len(children) != 1: 798 raise ValueError('Sequence node expects exactly 1 child, got %s' % len(children)) 799 sequence_child = children[0] 800 sequence_child_class = sequence_child.GetClass() 801 if sequence_child_class != 'Type': 802 raise ValueError('Unrecognized node class: %s' % sequence_child_class) 803 element_type = type_node_to_type(sequence_child) 804 sequence_type = IdlSequenceType(element_type) 805 if node.GetProperty('NULLABLE'): 806 return IdlNullableType(sequence_type) 807 return sequence_type 808 809 810def typedef_node_to_type(node): 811 children = node.GetChildren() 812 if len(children) != 1: 813 raise ValueError('Typedef node with %s children, expected 1' % len(children)) 814 child = children[0] 815 child_class = child.GetClass() 816 if child_class != 'Type': 817 raise ValueError('Unrecognized node class: %s' % child_class) 818 return type_node_to_type(child) 819 820 821def union_type_node_to_idl_union_type(node): 822 member_types = [type_node_to_type(member_type_node) 823 for member_type_node in node.GetChildren()] 824 return IdlUnionType(member_types) 825