| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/python | |
| 2 # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
| 3 # for details. All rights reserved. Use of this source code is governed by a | |
| 4 # BSD-style license that can be found in the LICENSE file. | |
| 5 | |
| 6 import os | |
| 7 import sys | |
| 8 | |
| 9 | |
| 10 class IDLNode(object): | |
| 11 """Base class for all IDL elements. | |
| 12 IDLNode may contain various child nodes, and have properties. Examples | |
| 13 of IDLNode are interfaces, interface members, function arguments, | |
| 14 etc. | |
| 15 """ | |
| 16 | |
| 17 def __init__(self, ast): | |
| 18 """Initializes an IDLNode from a PegParser AST output.""" | |
| 19 self.id = self._find_first(ast, 'Id') if ast is not None else None | |
| 20 | |
| 21 def __repr__(self): | |
| 22 """Generates string of the form <class id extra extra ... 0x12345678>.""" | |
| 23 extras = self._extra_repr() | |
| 24 if isinstance(extras, list): | |
| 25 extras = ' '.join([str(e) for e in extras]) | |
| 26 try: | |
| 27 if self.id: | |
| 28 return '<%s %s 0x%x>' % ( | |
| 29 type(self).__name__, | |
| 30 ('%s %s' % (self.id, extras)).strip(), | |
| 31 hash(self)) | |
| 32 return '<%s %s 0x%x>' % ( | |
| 33 type(self).__name__, | |
| 34 extras, | |
| 35 hash(self)) | |
| 36 except Exception, e: | |
| 37 return "can't convert to string: %s" % e | |
| 38 | |
| 39 def _extra_repr(self): | |
| 40 """Returns string of extra info for __repr__().""" | |
| 41 return '' | |
| 42 | |
| 43 def __cmp__(self, other): | |
| 44 """Override default compare operation. | |
| 45 IDLNodes are equal if all their properties are equal.""" | |
| 46 if other is None or not isinstance(other, IDLNode): | |
| 47 return 1 | |
| 48 return self.__dict__.__cmp__(other.__dict__) | |
| 49 | |
| 50 def reset_id(self, newId): | |
| 51 """Reset the id of the Node. This is typically done during a normalization | |
| 52 phase (e.g., "DOMWindow" -> "Window").""" | |
| 53 self.id = newId | |
| 54 | |
| 55 def all(self, type_filter=None): | |
| 56 """Returns a list containing this node and all it child nodes | |
| 57 (recursive). | |
| 58 | |
| 59 Args: | |
| 60 type_filter -- can be used to limit the results to a specific | |
| 61 node type (e.g. IDLOperation). | |
| 62 """ | |
| 63 res = [] | |
| 64 if type_filter is None or isinstance(self, type_filter): | |
| 65 res.append(self) | |
| 66 for v in self._all_subnodes(): | |
| 67 if isinstance(v, IDLNode): | |
| 68 res.extend(v.all(type_filter)) | |
| 69 elif isinstance(v, list): | |
| 70 for item in v: | |
| 71 if isinstance(item, IDLNode): | |
| 72 res.extend(item.all(type_filter)) | |
| 73 return res | |
| 74 | |
| 75 def _all_subnodes(self): | |
| 76 """Accessor used by all() to find subnodes.""" | |
| 77 return self.__dict__.values() | |
| 78 | |
| 79 def to_dict(self): | |
| 80 """Converts the IDLNode and its children into a dictionary. | |
| 81 This method is useful mostly for debugging and pretty printing. | |
| 82 """ | |
| 83 res = {} | |
| 84 for (k, v) in self.__dict__.items(): | |
| 85 if v == None or v == False or v == [] or v == {}: | |
| 86 # Skip empty/false members. | |
| 87 continue | |
| 88 elif isinstance(v, IDLDictNode) and not len(v): | |
| 89 # Skip empty dict sub-nodes. | |
| 90 continue | |
| 91 elif isinstance(v, list): | |
| 92 # Convert lists: | |
| 93 new_v = [] | |
| 94 for sub_node in v: | |
| 95 if isinstance(sub_node, IDLNode): | |
| 96 # Convert sub-node: | |
| 97 new_v.append(sub_node.to_dict()) | |
| 98 else: | |
| 99 new_v.append(sub_node) | |
| 100 v = new_v | |
| 101 elif isinstance(v, IDLNode): | |
| 102 # Convert sub-node: | |
| 103 v = v.to_dict() | |
| 104 res[k] = v | |
| 105 return res | |
| 106 | |
| 107 def _find_all(self, ast, label, max_results=sys.maxint): | |
| 108 """Searches the AST for tuples with a given label. The PegParser | |
| 109 output is composed of lists and tuples, where the tuple 1st argument | |
| 110 is a label. If ast root is a list, will search recursively inside each | |
| 111 member in the list. | |
| 112 | |
| 113 Args: | |
| 114 ast -- the AST to search. | |
| 115 label -- the label to look for. | |
| 116 res -- results are put into this list. | |
| 117 max_results -- maximum number of results. | |
| 118 """ | |
| 119 res = [] | |
| 120 if max_results <= 0: | |
| 121 return res | |
| 122 | |
| 123 if isinstance(ast, list): | |
| 124 for childAst in ast: | |
| 125 sub_res = self._find_all(childAst, label, | |
| 126 max_results - len(res)) | |
| 127 res.extend(sub_res) | |
| 128 elif isinstance(ast, tuple): | |
| 129 (nodeLabel, value) = ast | |
| 130 if nodeLabel == label: | |
| 131 res.append(value) | |
| 132 return res | |
| 133 | |
| 134 def _find_first(self, ast, label): | |
| 135 """Convenience method for _find_all(..., max_results=1). | |
| 136 Returns a single element instead of a list, or None if nothing | |
| 137 is found.""" | |
| 138 res = self._find_all(ast, label, max_results=1) | |
| 139 if len(res): | |
| 140 return res[0] | |
| 141 return None | |
| 142 | |
| 143 def _has(self, ast, label): | |
| 144 """Returns true if an element with the given label is | |
| 145 in the AST by searching for it.""" | |
| 146 return len(self._find_all(ast, label, max_results=1)) == 1 | |
| 147 | |
| 148 def _convert_all(self, ast, label, idlnode_ctor): | |
| 149 """Converts AST elements into IDLNode elements. | |
| 150 Uses _find_all to find elements with a given label and converts | |
| 151 them into IDLNodes with a given constructor. | |
| 152 Returns: | |
| 153 A list of the converted nodes. | |
| 154 Args: | |
| 155 ast -- the ast element to start a search at. | |
| 156 label -- the element label to look for. | |
| 157 idlnode_ctor -- a constructor function of one of the IDLNode | |
| 158 sub-classes. | |
| 159 """ | |
| 160 res = [] | |
| 161 found = self._find_all(ast, label) | |
| 162 if not found: | |
| 163 return res | |
| 164 if not isinstance(found, list): | |
| 165 raise RuntimeError("Expected list but %s found" % type(found)) | |
| 166 for childAst in found: | |
| 167 converted = idlnode_ctor(childAst) | |
| 168 res.append(converted) | |
| 169 return res | |
| 170 | |
| 171 def _convert_first(self, ast, label, idlnode_ctor): | |
| 172 """Like _convert_all, but only converts the first found results.""" | |
| 173 childAst = self._find_first(ast, label) | |
| 174 if not childAst: | |
| 175 return None | |
| 176 return idlnode_ctor(childAst) | |
| 177 | |
| 178 def _convert_ext_attrs(self, ast): | |
| 179 """Helper method for uniform conversion of extended attributes.""" | |
| 180 self.ext_attrs = IDLExtAttrs(ast) | |
| 181 | |
| 182 def _convert_annotations(self, ast): | |
| 183 """Helper method for uniform conversion of annotations.""" | |
| 184 self.annotations = IDLAnnotations(ast) | |
| 185 | |
| 186 | |
| 187 class IDLDictNode(IDLNode): | |
| 188 """Base class for dictionary-like IDL nodes such as extended attributes | |
| 189 and annotations. The base class implements various dict interfaces.""" | |
| 190 | |
| 191 def __init__(self, ast): | |
| 192 IDLNode.__init__(self, None) | |
| 193 if ast is not None and isinstance(ast, dict): | |
| 194 self.__map = ast | |
| 195 else: | |
| 196 self.__map = {} | |
| 197 | |
| 198 def __len__(self): | |
| 199 return len(self.__map) | |
| 200 | |
| 201 def __getitem__(self, key): | |
| 202 return self.__map[key] | |
| 203 | |
| 204 def __setitem__(self, key, value): | |
| 205 self.__map[key] = value | |
| 206 | |
| 207 def __delitem__(self, key): | |
| 208 del self.__map[key] | |
| 209 | |
| 210 def __contains__(self, key): | |
| 211 return key in self.__map | |
| 212 | |
| 213 def __iter__(self): | |
| 214 return self.__map.__iter__() | |
| 215 | |
| 216 def get(self, key, default=None): | |
| 217 return self.__map.get(key, default) | |
| 218 | |
| 219 def items(self): | |
| 220 return self.__map.items() | |
| 221 | |
| 222 def keys(self): | |
| 223 return self.__map.keys() | |
| 224 | |
| 225 def values(self): | |
| 226 return self.__map.values() | |
| 227 | |
| 228 def clear(self): | |
| 229 self.__map = {} | |
| 230 | |
| 231 def to_dict(self): | |
| 232 """Overrides the default IDLNode.to_dict behavior. | |
| 233 The IDLDictNode members are copied into a new dictionary, and | |
| 234 IDLNode members are recursively converted into dicts as well. | |
| 235 """ | |
| 236 res = {} | |
| 237 for (k, v) in self.__map.items(): | |
| 238 if isinstance(v, IDLNode): | |
| 239 v = v.to_dict() | |
| 240 res[k] = v | |
| 241 return res | |
| 242 | |
| 243 def _all_subnodes(self): | |
| 244 # Usually an IDLDictNode does not contain further IDLNodes. | |
| 245 return [] | |
| 246 | |
| 247 | |
| 248 class IDLFile(IDLNode): | |
| 249 """IDLFile is the top-level node in each IDL file. It may contain interfaces."
"" | |
| 250 | |
| 251 def __init__(self, ast, filename=None): | |
| 252 IDLNode.__init__(self, ast) | |
| 253 self.filename = filename | |
| 254 self.interfaces = self._convert_all(ast, 'Interface', IDLInterface) | |
| 255 modules = self._convert_all(ast, 'Module', IDLModule) | |
| 256 self.implementsStatements = self._convert_all(ast, 'ImplStmt', | |
| 257 IDLImplementsStatement) | |
| 258 for module in modules: | |
| 259 self.interfaces.extend(module.interfaces) | |
| 260 self.implementsStatements.extend(module.implementsStatements) | |
| 261 | |
| 262 | |
| 263 class IDLModule(IDLNode): | |
| 264 """IDLModule has an id, and may contain interfaces, type defs and | |
| 265 implements statements.""" | |
| 266 def __init__(self, ast): | |
| 267 IDLNode.__init__(self, ast) | |
| 268 self._convert_ext_attrs(ast) | |
| 269 self._convert_annotations(ast) | |
| 270 self.interfaces = self._convert_all(ast, 'Interface', IDLInterface) | |
| 271 self.typeDefs = self._convert_all(ast, 'TypeDef', IDLTypeDef) | |
| 272 self.implementsStatements = self._convert_all(ast, 'ImplStmt', | |
| 273 IDLImplementsStatement) | |
| 274 | |
| 275 | |
| 276 class IDLExtAttrs(IDLDictNode): | |
| 277 """IDLExtAttrs is an IDLDictNode that stores IDL Extended Attributes. | |
| 278 Modules, interfaces, members and arguments can all own IDLExtAttrs.""" | |
| 279 def __init__(self, ast=None): | |
| 280 IDLDictNode.__init__(self, None) | |
| 281 if not ast: | |
| 282 return | |
| 283 ext_attrs_ast = self._find_first(ast, 'ExtAttrs') | |
| 284 if not ext_attrs_ast: | |
| 285 return | |
| 286 for ext_attr in self._find_all(ext_attrs_ast, 'ExtAttr'): | |
| 287 name = self._find_first(ext_attr, 'Id') | |
| 288 value = self._find_first(ext_attr, 'ExtAttrValue') | |
| 289 | |
| 290 func_value = self._find_first(value, 'ExtAttrFunctionValue') | |
| 291 if func_value: | |
| 292 # E.g. NamedConstructor=Audio(in [Optional] DOMString src) | |
| 293 self[name] = IDLExtAttrFunctionValue( | |
| 294 func_value, | |
| 295 self._find_first(func_value, 'ExtAttrArgList')) | |
| 296 continue | |
| 297 | |
| 298 ctor_args = not value and self._find_first(ext_attr, 'ExtAttrArgList') | |
| 299 if ctor_args: | |
| 300 # E.g. Constructor(Element host) | |
| 301 self[name] = IDLExtAttrFunctionValue(None, ctor_args) | |
| 302 continue | |
| 303 | |
| 304 self[name] = value | |
| 305 | |
| 306 def _all_subnodes(self): | |
| 307 # Extended attributes may contain IDLNodes, e.g. IDLExtAttrFunctionValue | |
| 308 return self.values() | |
| 309 | |
| 310 | |
| 311 class IDLExtAttrFunctionValue(IDLNode): | |
| 312 """IDLExtAttrFunctionValue.""" | |
| 313 def __init__(self, func_value_ast, arg_list_ast): | |
| 314 IDLNode.__init__(self, func_value_ast) | |
| 315 self.arguments = self._convert_all(arg_list_ast, 'Argument', IDLArgument) | |
| 316 | |
| 317 | |
| 318 class IDLType(IDLNode): | |
| 319 """IDLType is used to describe constants, attributes and operations' | |
| 320 return and input types. IDLType matches AST labels such as ScopedName, | |
| 321 StringType, VoidType, IntegerType, etc.""" | |
| 322 | |
| 323 def __init__(self, ast): | |
| 324 IDLNode.__init__(self, ast) | |
| 325 # Search for a 'ScopedName' or any label ending with 'Type'. | |
| 326 if isinstance(ast, list): | |
| 327 self.id = self._find_first(ast, 'ScopedName') | |
| 328 if not self.id: | |
| 329 # FIXME: use regexp search instead | |
| 330 def findType(ast): | |
| 331 for label, childAst in ast: | |
| 332 if label.endswith('Type'): | |
| 333 type = self._label_to_type(label, ast) | |
| 334 if type != 'sequence': | |
| 335 return type | |
| 336 type_ast = self._find_first(childAst, 'Type') | |
| 337 if not type_ast: | |
| 338 return type | |
| 339 return 'sequence<%s>' % findType(type_ast) | |
| 340 raise Exception('No type declaration found in %s' % ast) | |
| 341 self.id = findType(ast) | |
| 342 array_modifiers = self._find_first(ast, 'ArrayModifiers') | |
| 343 if array_modifiers: | |
| 344 self.id += array_modifiers | |
| 345 elif isinstance(ast, tuple): | |
| 346 (label, value) = ast | |
| 347 if label == 'ScopedName': | |
| 348 self.id = value | |
| 349 else: | |
| 350 self.id = self._label_to_type(label, ast) | |
| 351 elif isinstance(ast, str): | |
| 352 self.id = ast | |
| 353 if not self.id: | |
| 354 raise SyntaxError('Could not parse type %s' % (ast)) | |
| 355 | |
| 356 def _label_to_type(self, label, ast): | |
| 357 if label == 'LongLongType': | |
| 358 label = 'long long' | |
| 359 elif label.endswith('Type'): | |
| 360 # Omit 'Type' suffix and lowercase the rest. | |
| 361 label = '%s%s' % (label[0].lower(), label[1:-4]) | |
| 362 | |
| 363 # Add unsigned qualifier. | |
| 364 if self._has(ast, 'Unsigned'): | |
| 365 label = 'unsigned %s' % label | |
| 366 return label | |
| 367 | |
| 368 | |
| 369 class IDLTypeDef(IDLNode): | |
| 370 """IDLNode for 'typedef [type] [id]' declarations.""" | |
| 371 def __init__(self, ast): | |
| 372 IDLNode.__init__(self, ast) | |
| 373 self._convert_annotations(ast) | |
| 374 self.type = self._convert_first(ast, 'Type', IDLType) | |
| 375 | |
| 376 | |
| 377 class IDLInterface(IDLNode): | |
| 378 """IDLInterface node contains operations, attributes, constants, | |
| 379 as well as parent references.""" | |
| 380 | |
| 381 def __init__(self, ast): | |
| 382 IDLNode.__init__(self, ast) | |
| 383 self._convert_ext_attrs(ast) | |
| 384 self._convert_annotations(ast) | |
| 385 self.parents = self._convert_all(ast, 'ParentInterface', | |
| 386 IDLParentInterface) | |
| 387 javascript_interface_name = self.ext_attrs.get('InterfaceName', self.id) | |
| 388 self.javascript_binding_name = javascript_interface_name | |
| 389 self.doc_js_name = javascript_interface_name | |
| 390 self.operations = self._convert_all(ast, 'Operation', | |
| 391 lambda ast: IDLOperation(ast, self.doc_js_name)) | |
| 392 self.attributes = self._convert_all(ast, 'Attribute', | |
| 393 lambda ast: IDLAttribute(ast, self.doc_js_name)) | |
| 394 self.constants = self._convert_all(ast, 'Const', | |
| 395 lambda ast: IDLConstant(ast, self.doc_js_name)) | |
| 396 self.is_supplemental = 'Supplemental' in self.ext_attrs | |
| 397 self.is_no_interface_object = 'NoInterfaceObject' in self.ext_attrs | |
| 398 self.is_fc_suppressed = 'Suppressed' in self.ext_attrs | |
| 399 | |
| 400 def reset_id(self, new_id): | |
| 401 """Reset the id of the Interface and corresponding the JS names.""" | |
| 402 if self.id != new_id: | |
| 403 self.id = new_id | |
| 404 self.doc_js_name = new_id | |
| 405 self.javascript_binding_name = new_id | |
| 406 for member in self.operations: | |
| 407 member.doc_js_interface_name = new_id | |
| 408 for member in self.attributes: | |
| 409 member.doc_js_interface_name = new_id | |
| 410 for member in self.constants: | |
| 411 member.doc_js_interface_name = new_id | |
| 412 | |
| 413 def has_attribute(self, candidate): | |
| 414 for attribute in self.attributes: | |
| 415 if (attribute.id == candidate.id and | |
| 416 attribute.is_read_only == candidate.is_read_only): | |
| 417 return True | |
| 418 return False | |
| 419 | |
| 420 | |
| 421 class IDLParentInterface(IDLNode): | |
| 422 """This IDLNode specialization is for 'Interface Child : Parent {}' | |
| 423 declarations.""" | |
| 424 def __init__(self, ast): | |
| 425 IDLNode.__init__(self, ast) | |
| 426 self._convert_annotations(ast) | |
| 427 self.type = self._convert_first(ast, 'InterfaceType', IDLType) | |
| 428 | |
| 429 | |
| 430 class IDLMember(IDLNode): | |
| 431 """A base class for constants, attributes and operations.""" | |
| 432 | |
| 433 def __init__(self, ast, doc_js_interface_name): | |
| 434 IDLNode.__init__(self, ast) | |
| 435 self.type = self._convert_first(ast, 'Type', IDLType) | |
| 436 self._convert_ext_attrs(ast) | |
| 437 self._convert_annotations(ast) | |
| 438 self.doc_js_interface_name = doc_js_interface_name | |
| 439 self.is_fc_suppressed = 'Suppressed' in self.ext_attrs | |
| 440 self.is_static = self._has(ast, 'Static') | |
| 441 | |
| 442 | |
| 443 class IDLOperation(IDLMember): | |
| 444 """IDLNode specialization for 'type name(args)' declarations.""" | |
| 445 def __init__(self, ast, doc_js_interface_name): | |
| 446 IDLMember.__init__(self, ast, doc_js_interface_name) | |
| 447 self.type = self._convert_first(ast, 'ReturnType', IDLType) | |
| 448 self.arguments = self._convert_all(ast, 'Argument', IDLArgument) | |
| 449 self.raises = self._convert_first(ast, 'Raises', IDLType) | |
| 450 self.specials = self._find_all(ast, 'Special') | |
| 451 self.is_stringifier = self._has(ast, 'Stringifier') | |
| 452 def _extra_repr(self): | |
| 453 return [self.arguments] | |
| 454 | |
| 455 | |
| 456 class IDLAttribute(IDLMember): | |
| 457 """IDLNode specialization for 'attribute type name' declarations.""" | |
| 458 def __init__(self, ast, doc_js_interface_name): | |
| 459 IDLMember.__init__(self, ast, doc_js_interface_name) | |
| 460 self.is_read_only = self._has(ast, 'ReadOnly') | |
| 461 # There are various ways to define exceptions for attributes: | |
| 462 self.raises = self._convert_first(ast, 'Raises', IDLType) | |
| 463 self.get_raises = self.raises \ | |
| 464 or self._convert_first(ast, 'GetRaises', IDLType) | |
| 465 self.set_raises = self.raises \ | |
| 466 or self._convert_first(ast, 'SetRaises', IDLType) | |
| 467 def _extra_repr(self): | |
| 468 extra = [] | |
| 469 if self.is_read_only: extra.append('readonly') | |
| 470 if self.raises: extra.append('raises') | |
| 471 return extra | |
| 472 | |
| 473 class IDLConstant(IDLMember): | |
| 474 """IDLNode specialization for 'const type name = value' declarations.""" | |
| 475 def __init__(self, ast, doc_js_interface_name): | |
| 476 IDLMember.__init__(self, ast, doc_js_interface_name) | |
| 477 self.value = self._find_first(ast, 'ConstExpr') | |
| 478 | |
| 479 | |
| 480 class IDLArgument(IDLNode): | |
| 481 """IDLNode specialization for operation arguments.""" | |
| 482 def __init__(self, ast): | |
| 483 IDLNode.__init__(self, ast) | |
| 484 self.type = self._convert_first(ast, 'Type', IDLType) | |
| 485 self._convert_ext_attrs(ast) | |
| 486 | |
| 487 def __repr__(self): | |
| 488 return '<IDLArgument(type = %s, id = %s)>' % (self.type, self.id) | |
| 489 | |
| 490 | |
| 491 class IDLImplementsStatement(IDLNode): | |
| 492 """IDLNode specialization for 'X implements Y' declarations.""" | |
| 493 def __init__(self, ast): | |
| 494 IDLNode.__init__(self, ast) | |
| 495 self.implementor = self._convert_first(ast, 'ImplStmtImplementor', | |
| 496 IDLType) | |
| 497 self.implemented = self._convert_first(ast, 'ImplStmtImplemented', | |
| 498 IDLType) | |
| 499 | |
| 500 | |
| 501 class IDLAnnotations(IDLDictNode): | |
| 502 """IDLDictNode specialization for a list of FremontCut annotations.""" | |
| 503 def __init__(self, ast=None): | |
| 504 IDLDictNode.__init__(self, ast) | |
| 505 self.id = None | |
| 506 if not ast: | |
| 507 return | |
| 508 for annotation in self._find_all(ast, 'Annotation'): | |
| 509 name = self._find_first(annotation, 'Id') | |
| 510 value = IDLAnnotation(annotation) | |
| 511 self[name] = value | |
| 512 | |
| 513 | |
| 514 class IDLAnnotation(IDLDictNode): | |
| 515 """IDLDictNode specialization for one annotation.""" | |
| 516 def __init__(self, ast=None): | |
| 517 IDLDictNode.__init__(self, ast) | |
| 518 self.id = None | |
| 519 if not ast: | |
| 520 return | |
| 521 for arg in self._find_all(ast, 'AnnotationArg'): | |
| 522 name = self._find_first(arg, 'Id') | |
| 523 value = self._find_first(arg, 'AnnotationArgValue') | |
| 524 self[name] = value | |
| OLD | NEW |