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 """Builds an IdlDefinitions object from an AST (produced by blink_idl_parser).""" 30 31 import os 32 33 from idl_definitions import IdlDefinitions, IdlInterface, IdlException, IdlOperation, IdlCallbackFunction, IdlArgument, IdlAttribute, IdlConstant, IdlEnum, IdlTypedef, IdlUnionType 34 35 SPECIAL_KEYWORD_LIST = ['GETTER', 'SETTER', 'DELETER'] 36 37 38 def build_idl_definitions_from_ast(node): 39 if node is None: 40 return None 41 node_class = node.GetClass() 42 if node_class != 'File': 43 raise ValueError('Unrecognized node class: %s' % node_class) 44 return file_node_to_idl_definitions(node) 45 46 47 def file_node_to_idl_definitions(node): 48 callback_functions = {} 49 enumerations = {} 50 exceptions = {} 51 interfaces = {} 52 typedefs = {} 53 54 # FIXME: only needed for Perl, remove later 55 file_name = os.path.abspath(node.GetName()) 56 57 children = node.GetChildren() 58 for child in children: 59 child_class = child.GetClass() 60 if child_class == 'Interface': 61 interface = interface_node_to_idl_interface(child) 62 interfaces[interface.name] = interface 63 elif child_class == 'Exception': 64 exception = exception_node_to_idl_exception(child) 65 exceptions[exception.name] = exception 66 elif child_class == 'Typedef': 67 type_name = child.GetName() 68 typedefs[type_name] = typedef_node_to_idl_typedef(child) 69 elif child_class == 'Enum': 70 enumeration = enum_node_to_idl_enum(child) 71 enumerations[enumeration.name] = enumeration 72 elif child_class == 'Callback': 73 callback_function = callback_node_to_idl_callback_function(child) 74 callback_functions[callback_function.name] = callback_function 75 elif child_class == 'Implements': 76 # Implements is handled at the interface merging step 77 pass 78 else: 79 raise ValueError('Unrecognized node class: %s' % child_class) 80 81 return IdlDefinitions(callback_functions=callback_functions, enumerations=enumerations, exceptions=exceptions, file_name=file_name, interfaces=interfaces, typedefs=typedefs) 82 83 # Constructors for Interface definitions and interface members 84 85 86 def interface_node_to_idl_interface(node): 87 attributes = [] 88 constants = [] 89 constructors = None 90 custom_constructors = None 91 extended_attributes = None 92 operations = [] 93 is_callback = node.GetProperty('CALLBACK') or False 94 # FIXME: uppercase 'Partial' in base IDL parser 95 is_partial = node.GetProperty('Partial') or False 96 name = node.GetName() 97 parent = None 98 99 children = node.GetChildren() 100 for child in children: 101 child_class = child.GetClass() 102 if child_class == 'Attribute': 103 attribute = attribute_node_to_idl_attribute(child) 104 # FIXME: This is a hack to support [CustomConstructor] for 105 # window.HTMLImageElement. Remove the hack. 106 clear_constructor_attributes(attribute.extended_attributes) 107 attributes.append(attribute) 108 elif child_class == 'Const': 109 constants.append(constant_node_to_idl_constant(child)) 110 elif child_class == 'ExtAttributes': 111 extended_attributes = ext_attributes_node_to_extended_attributes(child) 112 constructors, custom_constructors = extended_attributes_to_constructors(extended_attributes) 113 clear_constructor_attributes(extended_attributes) 114 elif child_class == 'Operation': 115 operations.append(operation_node_to_idl_operation(child)) 116 elif child_class == 'Inherit': 117 parent = child.GetName() 118 else: 119 raise ValueError('Unrecognized node class: %s' % child_class) 120 121 return IdlInterface(name=name, attributes=attributes, constants=constants, constructors=constructors, custom_constructors=custom_constructors, extended_attributes=extended_attributes, operations=operations, is_callback=is_callback, is_partial=is_partial, parent=parent) 122 123 124 def attribute_node_to_idl_attribute(node): 125 data_type = None 126 extended_attributes = {} 127 is_nullable = False 128 is_read_only = node.GetProperty('READONLY') or False 129 is_static = node.GetProperty('STATIC') or False 130 name = node.GetName() 131 132 children = node.GetChildren() 133 for child in children: 134 child_class = child.GetClass() 135 if child_class == 'Type': 136 data_type = type_node_to_type(child) 137 is_nullable = child.GetProperty('NULLABLE') or False 138 elif child_class == 'ExtAttributes': 139 extended_attributes = ext_attributes_node_to_extended_attributes(child) 140 else: 141 raise ValueError('Unrecognized node class: %s' % child_class) 142 143 return IdlAttribute(data_type=data_type, extended_attributes=extended_attributes, is_nullable=is_nullable, is_read_only=is_read_only, is_static=is_static, name=name) 144 145 146 def constant_node_to_idl_constant(node): 147 name = node.GetName() 148 149 children = node.GetChildren() 150 num_children = len(children) 151 if num_children < 2 or num_children > 3: 152 raise ValueError('Expected 2 or 3 children, got %s' % num_children) 153 154 type_node = children[0] 155 # ConstType is more limited than Type, so subtree is smaller and we don't 156 # use the full type_node_to_type function. 157 data_type = type_node_inner_to_type(type_node) 158 159 value_node = children[1] 160 value_node_class = value_node.GetClass() 161 if value_node_class != 'Value': 162 raise ValueError('Expected Value node, got %s' % value_node_class) 163 value = value_node.GetName() 164 165 extended_attributes = None 166 if num_children == 3: 167 ext_attributes_node = children[2] 168 extended_attributes = ext_attributes_node_to_extended_attributes(ext_attributes_node) 169 170 return IdlConstant(data_type=data_type, extended_attributes=extended_attributes, name=name, value=value) 171 172 173 def operation_node_to_idl_operation(node): 174 name = node.GetName() 175 # FIXME: AST should use None internally 176 if name == '_unnamed_': 177 name = None 178 179 is_static = node.GetProperty('STATIC') or False 180 specials = [] 181 property_dictionary = node.GetProperties() 182 for special_keyword in SPECIAL_KEYWORD_LIST: 183 if special_keyword in property_dictionary: 184 specials.append(special_keyword.lower()) 185 186 extended_attributes = None 187 arguments = [] 188 return_type = None 189 children = node.GetChildren() 190 for child in children: 191 child_class = child.GetClass() 192 if child_class == 'Arguments': 193 arguments = arguments_node_to_arguments(child) 194 elif child_class == 'Type': 195 return_type = type_node_to_type(child) 196 elif child_class == 'ExtAttributes': 197 extended_attributes = ext_attributes_node_to_extended_attributes(child) 198 else: 199 raise ValueError('Unrecognized node class: %s' % child_class) 200 201 return IdlOperation(name=name, data_type=return_type, extended_attributes=extended_attributes, is_static=is_static, arguments=arguments, specials=specials) 202 203 204 def arguments_node_to_arguments(node): 205 # [Constructor] and [CustomConstructor] without arguments (the bare form) 206 # have None instead of an arguments node, but have the same meaning as using 207 # an empty argument list, [Constructor()], so special-case this. 208 # http://www.w3.org/TR/WebIDL/#Constructor 209 if node is None: 210 return [] 211 arguments = [] 212 argument_node_list = node.GetChildren() 213 for argument_node in argument_node_list: 214 arguments.append(argument_node_to_idl_argument(argument_node)) 215 return arguments 216 217 218 def argument_node_to_idl_argument(node): 219 name = node.GetName() 220 221 data_type = None 222 extended_attributes = {} 223 # FIXME: Boolean values are inconsistent due to Perl compatibility. 224 # Make all default to False once Perl removed. 225 is_nullable = False 226 is_optional = node.GetProperty('OPTIONAL') 227 is_variadic = None 228 children = node.GetChildren() 229 for child in children: 230 child_class = child.GetClass() 231 if child_class == 'Type': 232 data_type = type_node_to_type(child) 233 is_nullable = child.GetProperty('NULLABLE') 234 elif child_class == 'ExtAttributes': 235 extended_attributes = ext_attributes_node_to_extended_attributes(child) 236 elif child_class == 'Argument': 237 child_name = child.GetName() 238 if child_name != '...': 239 raise ValueError('Unrecognized Argument node; expected "...", got "%s"' % child_name) 240 is_variadic = child.GetProperty('ELLIPSIS') or False 241 else: 242 raise ValueError('Unrecognized node class: %s' % child_class) 243 244 return IdlArgument(name=name, data_type=data_type, extended_attributes=extended_attributes, is_nullable=is_nullable, is_optional=is_optional, is_variadic=is_variadic) 245 246 # Constructors for for non-interface definitions 247 248 249 def callback_node_to_idl_callback_function(node): 250 name = node.GetName() 251 children = node.GetChildren() 252 num_children = len(children) 253 if num_children != 2: 254 raise ValueError('Expected 2 children, got %s' % num_children) 255 256 type_node = children[0] 257 data_type = type_node_to_type(type_node) 258 259 arguments_node = children[1] 260 arguments_node_class = arguments_node.GetClass() 261 if arguments_node_class != 'Arguments': 262 raise ValueError('Expected Value node, got %s' % arguments_node_class) 263 arguments = arguments_node_to_arguments(arguments_node) 264 265 return IdlCallbackFunction(name=name, data_type=data_type, arguments=arguments) 266 267 268 def enum_node_to_idl_enum(node): 269 name = node.GetName() 270 values = [] 271 for child in node.GetChildren(): 272 values.append(child.GetName()) 273 return IdlEnum(name=name, values=values) 274 275 276 def exception_operation_node_to_idl_operation(node): 277 # Needed to handle one case in DOMException.idl: 278 # // Override in a Mozilla compatible format 279 # [NotEnumerable] DOMString toString(); 280 # FIXME: can we remove this? replace with a stringifier? 281 extended_attributes = {} 282 name = node.GetName() 283 children = node.GetChildren() 284 if len(children) < 1 or len(children) > 2: 285 raise ValueError('ExceptionOperation node with %s children, expected 1 or 2' % len(children)) 286 287 type_node = children[0] 288 return_type = type_node_to_type(type_node) 289 290 if len(children) > 1: 291 ext_attributes_node = children[1] 292 extended_attributes = ext_attributes_node_to_extended_attributes(ext_attributes_node) 293 294 return IdlOperation(name=name, data_type=return_type, extended_attributes=extended_attributes) 295 296 297 def exception_node_to_idl_exception(node): 298 # Exceptions are similar to Interfaces, but simpler 299 attributes = [] 300 constants = [] 301 extended_attributes = None 302 operations = [] 303 name = node.GetName() 304 305 children = node.GetChildren() 306 for child in children: 307 child_class = child.GetClass() 308 if child_class == 'Attribute': 309 attribute = attribute_node_to_idl_attribute(child) 310 attributes.append(attribute) 311 elif child_class == 'Const': 312 constants.append(constant_node_to_idl_constant(child)) 313 elif child_class == 'ExtAttributes': 314 extended_attributes = ext_attributes_node_to_extended_attributes(child) 315 elif child_class == 'ExceptionOperation': 316 operations.append(exception_operation_node_to_idl_operation(child)) 317 else: 318 raise ValueError('Unrecognized node class: %s' % child_class) 319 320 return IdlException(name=name, attributes=attributes, constants=constants, extended_attributes=extended_attributes, operations=operations) 321 322 323 def typedef_node_to_idl_typedef(node): 324 data_type = None 325 extended_attributes = None 326 327 children = node.GetChildren() 328 for child in children: 329 child_class = child.GetClass() 330 if child_class == 'Type': 331 data_type = type_node_to_type(child) 332 elif child_class == 'ExtAttributes': 333 extended_attributes = ext_attributes_node_to_extended_attributes(child) 334 raise ValueError('Extended attributes in a typedef are untested!') 335 else: 336 raise ValueError('Unrecognized node class: %s' % child_class) 337 338 return IdlTypedef(data_type=data_type, extended_attributes=extended_attributes) 339 340 # Extended attributes 341 342 343 def ext_attributes_node_to_extended_attributes(node): 344 """ 345 Returns: 346 Dictionary of {ExtAttributeName: ExtAttributeValue}. 347 Value is usually a string, with three exceptions: 348 Constructors: value is a list of Arguments nodes, corresponding to 349 possibly signatures of the constructor. 350 CustomConstructors: value is a list of Arguments nodes, corresponding to 351 possibly signatures of the custom constructor. 352 NamedConstructor: value is a Call node, corresponding to the single 353 signature of the named constructor. 354 """ 355 # Primarily just make a dictionary from the children. 356 # The only complexity is handling various types of constructors: 357 # Constructors and Custom Constructors can have duplicate entries due to 358 # overloading, and thus are stored in temporary lists. 359 # However, Named Constructors cannot be overloaded, and thus do not have 360 # a list. 361 # FIXME: Add overloading for Named Constructors and remove custom bindings 362 # for HTMLImageElement 363 constructors = [] 364 custom_constructors = [] 365 extended_attributes = {} 366 367 attribute_list = node.GetChildren() 368 for attribute in attribute_list: 369 name = attribute.GetName() 370 children = attribute.GetChildren() 371 if name in ['Constructor', 'CustomConstructor', 'NamedConstructor']: 372 child = None 373 child_class = None 374 if children: 375 if len(children) > 1: 376 raise ValueError('ExtAttributes node with %s children, expected at most 1' % len(children)) 377 child = children[0] 378 child_class = child.GetClass() 379 if name == 'Constructor': 380 if child_class and child_class != 'Arguments': 381 raise ValueError('Constructor only supports Arguments as child, but has child of class: %s' % child_class) 382 constructors.append(child) 383 elif name == 'CustomConstructor': 384 if child_class and child_class != 'Arguments': 385 raise ValueError('Custom Constructor only supports Arguments as child, but has child of class: %s' % child_class) 386 custom_constructors.append(child) 387 else: # name == 'NamedConstructor' 388 if child_class and child_class != 'Call': 389 raise ValueError('Named Constructor only supports Call as child, but has child of class: %s' % child_class) 390 extended_attributes[name] = child 391 elif children: 392 raise ValueError('Non-constructor ExtAttributes node with children: %s' % name) 393 else: 394 value = attribute.GetProperty('VALUE') 395 extended_attributes[name] = value 396 397 # Store constructors and custom constructors in special list attributes, 398 # which are deleted later. Note plural in key. 399 if constructors: 400 extended_attributes['Constructors'] = constructors 401 if custom_constructors: 402 extended_attributes['CustomConstructors'] = custom_constructors 403 404 return extended_attributes 405 406 407 def extended_attributes_to_constructors(extended_attributes): 408 """Returns constructors and custom_constructors (lists of IdlOperations). 409 410 Auxiliary function for interface_node_to_idl_interface. 411 """ 412 constructors = [] 413 custom_constructors = [] 414 if 'Constructors' in extended_attributes: 415 constructor_list = extended_attributes['Constructors'] 416 # If not overloaded, have index 0, otherwise index from 1 417 overloaded_index = 0 if len(constructor_list) == 1 else 1 418 for arguments_node in constructor_list: 419 name = 'Constructor' 420 arguments = arguments_node_to_arguments(arguments_node) 421 constructor = IdlOperation(name=name, extended_attributes=extended_attributes, overloaded_index=overloaded_index, arguments=arguments) 422 constructors.append(constructor) 423 overloaded_index += 1 424 425 # Prefix 'CallWith' and 'RaisesException' with 'Constructor' 426 # FIXME: Change extended attributes to include prefix explicitly. 427 if 'CallWith' in extended_attributes: 428 extended_attributes['ConstructorCallWith'] = extended_attributes['CallWith'] 429 del extended_attributes['CallWith'] 430 if 'RaisesException' in extended_attributes: 431 extended_attributes['ConstructorRaisesException'] = extended_attributes['RaisesException'] 432 del extended_attributes['RaisesException'] 433 434 if 'CustomConstructors' in extended_attributes: 435 custom_constructor_list = extended_attributes['CustomConstructors'] 436 # If not overloaded, have index 0, otherwise index from 1 437 overloaded_index = 0 if len(custom_constructor_list) == 1 else 1 438 for arguments_node in custom_constructor_list: 439 name = 'CustomConstructor' 440 arguments = arguments_node_to_arguments(arguments_node) 441 custom_constructor = IdlOperation(name=name, extended_attributes=extended_attributes, overloaded_index=overloaded_index, arguments=arguments) 442 custom_constructors.append(custom_constructor) 443 overloaded_index += 1 444 445 if 'NamedConstructor' in extended_attributes: 446 name = 'NamedConstructor' 447 call_node = extended_attributes['NamedConstructor'] 448 extended_attributes['NamedConstructor'] = call_node.GetName() 449 overloaded_index = None # named constructors are not overloaded 450 children = call_node.GetChildren() 451 if len(children) != 1: 452 raise ValueError('NamedConstructor node expects 1 child, got %s.' % len(children)) 453 arguments_node = children[0] 454 arguments = arguments_node_to_arguments(arguments_node) 455 named_constructor = IdlOperation(name=name, extended_attributes=extended_attributes, overloaded_index=overloaded_index, arguments=arguments) 456 constructors.append(named_constructor) 457 458 return constructors, custom_constructors 459 460 461 def clear_constructor_attributes(extended_attributes): 462 # Deletes Constructor*s* (plural), sets Constructor (singular) 463 if 'Constructors' in extended_attributes: 464 del extended_attributes['Constructors'] 465 extended_attributes['Constructor'] = None 466 if 'CustomConstructors' in extended_attributes: 467 del extended_attributes['CustomConstructors'] 468 extended_attributes['CustomConstructor'] = None 469 470 471 # Types 472 473 474 def type_node_to_type(node): 475 children = node.GetChildren() 476 if len(children) < 1 or len(children) > 2: 477 raise ValueError('Type node expects 1 or 2 children (type + optional array []), got %s (multi-dimensional arrays are not supported).' % len(children)) 478 479 type_node_child = children[0] 480 data_type = type_node_inner_to_type(type_node_child) 481 482 if len(children) == 2: 483 array_node = children[1] 484 array_node_class = array_node.GetClass() 485 if array_node_class != 'Array': 486 raise ValueError('Expected Array node as TypeSuffix, got %s node.' % array_node_class) 487 data_type += '[]' 488 489 return data_type 490 491 492 def type_node_inner_to_type(node): 493 node_class = node.GetClass() 494 # Note Type*r*ef, not Typedef, meaning the type is an identifier, thus 495 # either a typedef shorthand (but not a Typedef declaration itself) or an 496 # interface type. We do not distinguish these, and just use the type name. 497 if node_class in ['PrimitiveType', 'Typeref']: 498 return node.GetName() 499 elif node_class == 'Any': 500 return 'any' 501 elif node_class == 'Sequence': 502 return sequence_node_to_type(node) 503 elif node_class == 'UnionType': 504 return union_type_node_to_idl_union_type(node) 505 raise ValueError('Unrecognized node class: %s' % node_class) 506 507 508 def sequence_node_to_type(node): 509 children = node.GetChildren() 510 if len(children) != 1: 511 raise ValueError('Sequence node expects exactly 1 child, got %s' % len(children)) 512 sequence_child = children[0] 513 sequence_child_class = sequence_child.GetClass() 514 if sequence_child_class != 'Type': 515 raise ValueError('Unrecognized node class: %s' % sequence_child_class) 516 sequence_type = type_node_to_type(sequence_child) 517 return 'sequence<%s>' % sequence_type 518 519 520 def union_type_node_to_idl_union_type(node): 521 union_member_types = [] 522 for member_type_node in node.GetChildren(): 523 member_type = type_node_to_type(member_type_node) 524 union_member_types.append(member_type) 525 return IdlUnionType(union_member_types=union_member_types) 526