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