| OLD | NEW |
| (Empty) |
| 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 | |
| 31 Classes are primarily constructors, which build an IdlDefinitions object | |
| 32 (and various contained objects) from an AST (produced by blink_idl_parser). | |
| 33 | |
| 34 This is in two steps: | |
| 35 * Constructors walk the AST, creating objects. | |
| 36 * Typedef resolution. | |
| 37 | |
| 38 Typedefs are all resolved here, and not stored in IR. | |
| 39 | |
| 40 Typedef resolution uses some auxiliary classes and OOP techniques to make this | |
| 41 a generic call, via the resolve_typedefs() method. | |
| 42 | |
| 43 Class hierarchy (mostly containment, '<' for inheritance): | |
| 44 | |
| 45 IdlDefinitions | |
| 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 | |
| 58 TypedObject :: mixin for typedef resolution | |
| 59 | |
| 60 Design doc: http://www.chromium.org/developers/design-documents/idl-compiler | |
| 61 """ | |
| 62 | |
| 63 import abc | |
| 64 | |
| 65 from idl_types import IdlType, IdlUnionType, IdlArrayType, IdlSequenceType, IdlN
ullableType | |
| 66 | |
| 67 SPECIAL_KEYWORD_LIST = ['GETTER', 'SETTER', 'DELETER'] | |
| 68 STANDARD_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 | |
| 78 class 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 | |
| 102 class 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_funct
ion | |
| 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 | |
| 183 class 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 | |
| 209 class 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 | |
| 230 class 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 | |
| 254 class 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 | |
| 268 class 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_attri
butes)) | |
| 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 | |
| 338 class 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_attri
butes(idl_name, child) | |
| 364 elif child_class == 'ExceptionOperation': | |
| 365 self.operations.append(IdlOperation.from_exception_operation_nod
e(idl_name, child)) | |
| 366 else: | |
| 367 raise ValueError('Unrecognized node class: %s' % child_class) | |
| 368 | |
| 369 | |
| 370 ################################################################################ | |
| 371 # Attributes | |
| 372 ################################################################################ | |
| 373 | |
| 374 class 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_attri
butes(idl_name, child) | |
| 391 else: | |
| 392 raise ValueError('Unrecognized node class: %s' % child_class) | |
| 393 | |
| 394 | |
| 395 ################################################################################ | |
| 396 # Constants | |
| 397 ################################################################################ | |
| 398 | |
| 399 class 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_attribute
s(idl_name, ext_attributes_node) | |
| 427 else: | |
| 428 self.extended_attributes = {} | |
| 429 | |
| 430 | |
| 431 ################################################################################ | |
| 432 # Literals | |
| 433 ################################################################################ | |
| 434 | |
| 435 class 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 | |
| 453 class 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 | |
| 463 def 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 | |
| 488 class 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_attri
butes(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_attr
ibutes(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 | |
| 562 class 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_attri
butes(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 | |
| 590 def 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 | |
| 605 class 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_attri
butes(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 | |
| 636 def 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 m
ost 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 Argument
s as child, but has child of class: %s' % child_class) | |
| 691 extended_attributes[name] = arguments_node_to_arguments(idl_name, ch
ild) | |
| 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 | |
| 708 def 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, ar
guments_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_na
me, 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('NamedC
onstructor', 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 | |
| 740 def 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 | |
| 754 def 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 arr
ay []), 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 | |
| 777 def 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 elif node_class == 'Promise': | |
| 793 return IdlType('Promise') | |
| 794 raise ValueError('Unrecognized node class: %s' % node_class) | |
| 795 | |
| 796 | |
| 797 def sequence_node_to_type(node): | |
| 798 children = node.GetChildren() | |
| 799 if len(children) != 1: | |
| 800 raise ValueError('Sequence node expects exactly 1 child, got %s' % len(c
hildren)) | |
| 801 sequence_child = children[0] | |
| 802 sequence_child_class = sequence_child.GetClass() | |
| 803 if sequence_child_class != 'Type': | |
| 804 raise ValueError('Unrecognized node class: %s' % sequence_child_class) | |
| 805 element_type = type_node_to_type(sequence_child) | |
| 806 sequence_type = IdlSequenceType(element_type) | |
| 807 if node.GetProperty('NULLABLE'): | |
| 808 return IdlNullableType(sequence_type) | |
| 809 return sequence_type | |
| 810 | |
| 811 | |
| 812 def typedef_node_to_type(node): | |
| 813 children = node.GetChildren() | |
| 814 if len(children) != 1: | |
| 815 raise ValueError('Typedef node with %s children, expected 1' % len(child
ren)) | |
| 816 child = children[0] | |
| 817 child_class = child.GetClass() | |
| 818 if child_class != 'Type': | |
| 819 raise ValueError('Unrecognized node class: %s' % child_class) | |
| 820 return type_node_to_type(child) | |
| 821 | |
| 822 | |
| 823 def union_type_node_to_idl_union_type(node): | |
| 824 member_types = [type_node_to_type(member_type_node) | |
| 825 for member_type_node in node.GetChildren()] | |
| 826 return IdlUnionType(member_types) | |
| OLD | NEW |