|  | #!/usr/bin/python | 
|  | # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file | 
|  | # for details. All rights reserved. Use of this source code is governed by a | 
|  | # BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | import sys | 
|  |  | 
|  |  | 
|  | class IDLNode(object): | 
|  | """Base class for all IDL elements. | 
|  | IDLNode may contain various child nodes, and have properties. Examples | 
|  | of IDLNode are modules, interfaces, interface members, function arguments, | 
|  | etc. | 
|  | """ | 
|  |  | 
|  | def __init__(self, ast): | 
|  | """Initializes an IDLNode from a PegParser AST output.""" | 
|  | self.id = self._find_first(ast, 'Id') if ast is not None else None | 
|  |  | 
|  | def __repr__(self): | 
|  | """Generates string of the form <class id extra extra ... 0x12345678>.""" | 
|  | extras = self._extra_repr() | 
|  | if isinstance(extras, list): | 
|  | extras = ' '.join([str(e) for e in extras]) | 
|  | try: | 
|  | if self.id: | 
|  | return '<%s %s 0x%x>' % ( | 
|  | type(self).__name__, | 
|  | ('%s %s' % (self.id, extras)).strip(), | 
|  | hash(self)) | 
|  | return '<%s %s 0x%x>' % ( | 
|  | type(self).__name__, | 
|  | extras, | 
|  | hash(self)) | 
|  | except Exception, e: | 
|  | return "can't convert to string: %s" % e | 
|  |  | 
|  | def _extra_repr(self): | 
|  | """Returns string of extra info for __repr__().""" | 
|  | return '' | 
|  |  | 
|  | def __cmp__(self, other): | 
|  | """Override default compare operation. | 
|  | IDLNodes are equal if all their properties are equal.""" | 
|  | if other is None or not isinstance(other, IDLNode): | 
|  | return 1 | 
|  | return self.__dict__.__cmp__(other.__dict__) | 
|  |  | 
|  | def all(self, type_filter=None): | 
|  | """Returns a list containing this node and all it child nodes | 
|  | (recursive). | 
|  |  | 
|  | Args: | 
|  | type_filter -- can be used to limit the results to a specific | 
|  | node type (e.g. IDLOperation). | 
|  | """ | 
|  | res = [] | 
|  | if type_filter is None or isinstance(self, type_filter): | 
|  | res.append(self) | 
|  | for v in self._all_subnodes(): | 
|  | if isinstance(v, IDLNode): | 
|  | res.extend(v.all(type_filter)) | 
|  | elif isinstance(v, list): | 
|  | for item in v: | 
|  | if isinstance(item, IDLNode): | 
|  | res.extend(item.all(type_filter)) | 
|  | return res | 
|  |  | 
|  | def _all_subnodes(self): | 
|  | """Accessor used by all() to find subnodes.""" | 
|  | return self.__dict__.values() | 
|  |  | 
|  | def to_dict(self): | 
|  | """Converts the IDLNode and its children into a dictionary. | 
|  | This method is useful mostly for debugging and pretty printing. | 
|  | """ | 
|  | res = {} | 
|  | for (k, v) in self.__dict__.items(): | 
|  | if v == None or v == False or v == [] or v == {}: | 
|  | # Skip empty/false members. | 
|  | continue | 
|  | elif isinstance(v, IDLDictNode) and not len(v): | 
|  | # Skip empty dict sub-nodes. | 
|  | continue | 
|  | elif isinstance(v, list): | 
|  | # Convert lists: | 
|  | new_v = [] | 
|  | for sub_node in v: | 
|  | if isinstance(sub_node, IDLNode): | 
|  | # Convert sub-node: | 
|  | new_v.append(sub_node.to_dict()) | 
|  | else: | 
|  | new_v.append(sub_node) | 
|  | v = new_v | 
|  | elif isinstance(v, IDLNode): | 
|  | # Convert sub-node: | 
|  | v = v.to_dict() | 
|  | res[k] = v | 
|  | return res | 
|  |  | 
|  | def _find_all(self, ast, label, max_results=sys.maxint): | 
|  | """Searches the AST for tuples with a given label. The PegParser | 
|  | output is composed of lists and tuples, where the tuple 1st argument | 
|  | is a label. If ast root is a list, will search recursively inside each | 
|  | member in the list. | 
|  |  | 
|  | Args: | 
|  | ast -- the AST to search. | 
|  | label -- the label to look for. | 
|  | res -- results are put into this list. | 
|  | max_results -- maximum number of results. | 
|  | """ | 
|  | res = [] | 
|  | if max_results <= 0: | 
|  | return res | 
|  |  | 
|  | if isinstance(ast, list): | 
|  | for childAst in ast: | 
|  | sub_res = self._find_all(childAst, label, | 
|  | max_results - len(res)) | 
|  | res.extend(sub_res) | 
|  | elif isinstance(ast, tuple): | 
|  | (nodeLabel, value) = ast | 
|  | if nodeLabel == label: | 
|  | res.append(value) | 
|  | return res | 
|  |  | 
|  | def _find_first(self, ast, label): | 
|  | """Convenience method for _find_all(..., max_results=1). | 
|  | Returns a single element instead of a list, or None if nothing | 
|  | is found.""" | 
|  | res = self._find_all(ast, label, max_results=1) | 
|  | if len(res): | 
|  | return res[0] | 
|  | return None | 
|  |  | 
|  | def _has(self, ast, label): | 
|  | """Returns true if an element with the given label is | 
|  | in the AST by searching for it.""" | 
|  | return len(self._find_all(ast, label, max_results=1)) == 1 | 
|  |  | 
|  | def _convert_all(self, ast, label, idlnode_ctor): | 
|  | """Converts AST elements into IDLNode elements. | 
|  | Uses _find_all to find elements with a given label and converts | 
|  | them into IDLNodes with a given constructor. | 
|  | Returns: | 
|  | A list of the converted nodes. | 
|  | Args: | 
|  | ast -- the ast element to start a search at. | 
|  | label -- the element label to look for. | 
|  | idlnode_ctor -- a constructor function of one of the IDLNode | 
|  | sub-classes. | 
|  | """ | 
|  | res = [] | 
|  | found = self._find_all(ast, label) | 
|  | if not found: | 
|  | return res | 
|  | if not isinstance(found, list): | 
|  | raise RuntimeError("Expected list but %s found" % type(found)) | 
|  | for childAst in found: | 
|  | converted = idlnode_ctor(childAst) | 
|  | res.append(converted) | 
|  | return res | 
|  |  | 
|  | def _convert_first(self, ast, label, idlnode_ctor): | 
|  | """Like _convert_all, but only converts the first found results.""" | 
|  | childAst = self._find_first(ast, label) | 
|  | if not childAst: | 
|  | return None | 
|  | return idlnode_ctor(childAst) | 
|  |  | 
|  | def _convert_ext_attrs(self, ast): | 
|  | """Helper method for uniform conversion of extended attributes.""" | 
|  | self.ext_attrs = IDLExtAttrs(ast) | 
|  |  | 
|  | def _convert_annotations(self, ast): | 
|  | """Helper method for uniform conversion of annotations.""" | 
|  | self.annotations = IDLAnnotations(ast) | 
|  |  | 
|  |  | 
|  | class IDLDictNode(IDLNode): | 
|  | """Base class for dictionary-like IDL nodes such as extended attributes | 
|  | and annotations. The base class implements various dict interfaces.""" | 
|  |  | 
|  | def __init__(self, ast): | 
|  | IDLNode.__init__(self, None) | 
|  | if ast is not None and isinstance(ast, dict): | 
|  | self.__map = ast | 
|  | else: | 
|  | self.__map = {} | 
|  |  | 
|  | def __len__(self): | 
|  | return len(self.__map) | 
|  |  | 
|  | def __getitem__(self, key): | 
|  | return self.__map[key] | 
|  |  | 
|  | def __setitem__(self, key, value): | 
|  | self.__map[key] = value | 
|  |  | 
|  | def __delitem__(self, key): | 
|  | del self.__map[key] | 
|  |  | 
|  | def __contains__(self, key): | 
|  | return key in self.__map | 
|  |  | 
|  | def __iter__(self): | 
|  | return self.__map.__iter__() | 
|  |  | 
|  | def get(self, key, default=None): | 
|  | return self.__map.get(key, default) | 
|  |  | 
|  | def items(self): | 
|  | return self.__map.items() | 
|  |  | 
|  | def keys(self): | 
|  | return self.__map.keys() | 
|  |  | 
|  | def values(self): | 
|  | return self.__map.values() | 
|  |  | 
|  | def clear(self): | 
|  | self.__map = {} | 
|  |  | 
|  | def to_dict(self): | 
|  | """Overrides the default IDLNode.to_dict behavior. | 
|  | The IDLDictNode members are copied into a new dictionary, and | 
|  | IDLNode members are recursively converted into dicts as well. | 
|  | """ | 
|  | res = {} | 
|  | for (k, v) in self.__map.items(): | 
|  | if isinstance(v, IDLNode): | 
|  | v = v.to_dict() | 
|  | res[k] = v | 
|  | return res | 
|  |  | 
|  | def _all_subnodes(self): | 
|  | # Usually an IDLDictNode does not contain further IDLNodes. | 
|  | return [] | 
|  |  | 
|  |  | 
|  | class IDLFile(IDLNode): | 
|  | """IDLFile is the top-level node in each IDL file. It may contain | 
|  | modules or interfaces.""" | 
|  |  | 
|  | def __init__(self, ast, filename=None): | 
|  | IDLNode.__init__(self, ast) | 
|  | self.filename = filename | 
|  | self.modules = self._convert_all(ast, 'Module', IDLModule) | 
|  | self.interfaces = self._convert_all(ast, 'Interface', IDLInterface) | 
|  |  | 
|  |  | 
|  | class IDLModule(IDLNode): | 
|  | """IDLModule has an id, and may contain interfaces, type defs and | 
|  | implements statements.""" | 
|  | def __init__(self, ast): | 
|  | IDLNode.__init__(self, ast) | 
|  | self._convert_ext_attrs(ast) | 
|  | self._convert_annotations(ast) | 
|  | self.interfaces = self._convert_all(ast, 'Interface', IDLInterface) | 
|  | self.typeDefs = self._convert_all(ast, 'TypeDef', IDLTypeDef) | 
|  | self.implementsStatements = self._convert_all(ast, 'ImplStmt', | 
|  | IDLImplementsStatement) | 
|  |  | 
|  |  | 
|  | class IDLExtAttrs(IDLDictNode): | 
|  | """IDLExtAttrs is an IDLDictNode that stores IDL Extended Attributes. | 
|  | Modules, interfaces, members and arguments can all own IDLExtAttrs.""" | 
|  | def __init__(self, ast=None): | 
|  | IDLDictNode.__init__(self, None) | 
|  | if not ast: | 
|  | return | 
|  | ext_attrs_ast = self._find_first(ast, 'ExtAttrs') | 
|  | if not ext_attrs_ast: | 
|  | return | 
|  | for ext_attr in self._find_all(ext_attrs_ast, 'ExtAttr'): | 
|  | name = self._find_first(ext_attr, 'Id') | 
|  | value = self._find_first(ext_attr, 'ExtAttrValue') | 
|  |  | 
|  | func_value = self._find_first(value, 'ExtAttrFunctionValue') | 
|  | if func_value: | 
|  | # E.g. NamedConstructor=Audio(in [Optional] DOMString src) | 
|  | self[name] = IDLExtAttrFunctionValue( | 
|  | func_value, | 
|  | self._find_first(func_value, 'ExtAttrArgList')) | 
|  | continue | 
|  |  | 
|  | ctor_args = not value and self._find_first(ext_attr, 'ExtAttrArgList') | 
|  | if ctor_args: | 
|  | # E.g. Constructor(Element host) | 
|  | self[name] = IDLExtAttrFunctionValue(None, ctor_args) | 
|  | continue | 
|  |  | 
|  | self[name] = value | 
|  |  | 
|  | def _all_subnodes(self): | 
|  | # Extended attributes may contain IDLNodes, e.g. IDLExtAttrFunctionValue | 
|  | return self.values() | 
|  |  | 
|  |  | 
|  | class IDLExtAttrFunctionValue(IDLNode): | 
|  | """IDLExtAttrFunctionValue.""" | 
|  | def __init__(self, func_value_ast, arg_list_ast): | 
|  | IDLNode.__init__(self, func_value_ast) | 
|  | self.arguments = self._convert_all(arg_list_ast, 'Argument', IDLArgument) | 
|  |  | 
|  |  | 
|  | class IDLType(IDLNode): | 
|  | """IDLType is used to describe constants, attributes and operations' | 
|  | return and input types. IDLType matches AST labels such as ScopedName, | 
|  | StringType, VoidType, IntegerType, etc.""" | 
|  |  | 
|  | def __init__(self, ast): | 
|  | IDLNode.__init__(self, ast) | 
|  | # Search for a 'ScopedName' or any label ending with 'Type'. | 
|  | if isinstance(ast, list): | 
|  | self.id = self._find_first(ast, 'ScopedName') | 
|  | if not self.id: | 
|  | # FIXME: use regexp search instead | 
|  | for childAst in ast: | 
|  | (label, childAst) = childAst | 
|  | if label.endswith('Type'): | 
|  | self.id = self._label_to_type(label, ast) | 
|  | break | 
|  | elif isinstance(ast, tuple): | 
|  | (label, value) = ast | 
|  | if label == 'ScopedName': | 
|  | self.id = value | 
|  | else: | 
|  | self.id = self._label_to_type(label, ast) | 
|  | elif isinstance(ast, str): | 
|  | self.id = ast | 
|  | if not self.id: | 
|  | raise SyntaxError('Could not parse type %s' % (ast)) | 
|  |  | 
|  | def _label_to_type(self, label, ast): | 
|  | if label == 'AnyArrayType': | 
|  | return 'any[]' | 
|  | if label == 'DOMStringArrayType': | 
|  | return 'DOMString[]' | 
|  | if label == 'LongLongType': | 
|  | label = 'long long' | 
|  | elif label.endswith('Type'): | 
|  | # Omit 'Type' suffix and lowercase the rest. | 
|  | label = '%s%s' % (label[0].lower(), label[1:-4]) | 
|  |  | 
|  | # Add unsigned qualifier. | 
|  | if self._has(ast, 'Unsigned'): | 
|  | label = 'unsigned %s' % label | 
|  | return label | 
|  |  | 
|  |  | 
|  | class IDLTypeDef(IDLNode): | 
|  | """IDLNode for 'typedef [type] [id]' declarations.""" | 
|  | def __init__(self, ast): | 
|  | IDLNode.__init__(self, ast) | 
|  | self._convert_annotations(ast) | 
|  | self.type = self._convert_first(ast, 'Type', IDLType) | 
|  |  | 
|  |  | 
|  | class IDLInterface(IDLNode): | 
|  | """IDLInterface node contains operations, attributes, constants, | 
|  | as well as parent references.""" | 
|  |  | 
|  | def __init__(self, ast): | 
|  | IDLNode.__init__(self, ast) | 
|  | self._convert_ext_attrs(ast) | 
|  | self._convert_annotations(ast) | 
|  | self.parents = self._convert_all(ast, 'ParentInterface', | 
|  | IDLParentInterface) | 
|  | self.javascript_binding_name = self.id | 
|  | self.doc_js_name = self.id | 
|  | self.operations = self._convert_all(ast, 'Operation', | 
|  | lambda ast: IDLOperation(ast, self.doc_js_name)) | 
|  | self.attributes = self._convert_all(ast, 'Attribute', | 
|  | lambda ast: IDLAttribute(ast, self.doc_js_name)) | 
|  | self.constants = self._convert_all(ast, 'Const', | 
|  | lambda ast: IDLConstant(ast, self.doc_js_name)) | 
|  | self.is_supplemental = 'Supplemental' in self.ext_attrs | 
|  | self.is_no_interface_object = 'NoInterfaceObject' in self.ext_attrs | 
|  | self.is_fc_suppressed = 'Suppressed' in self.ext_attrs | 
|  |  | 
|  | def has_attribute(self, candidate): | 
|  | for attribute in self.attributes: | 
|  | if (attribute.id == candidate.id and | 
|  | attribute.is_fc_getter == candidate.is_fc_getter and | 
|  | attribute.is_fc_setter == candidate.is_fc_setter): | 
|  | return True | 
|  | return False | 
|  |  | 
|  |  | 
|  | class IDLParentInterface(IDLNode): | 
|  | """This IDLNode specialization is for 'Interface Child : Parent {}' | 
|  | declarations.""" | 
|  | def __init__(self, ast): | 
|  | IDLNode.__init__(self, ast) | 
|  | self._convert_annotations(ast) | 
|  | self.type = self._convert_first(ast, 'InterfaceType', IDLType) | 
|  |  | 
|  |  | 
|  | class IDLMember(IDLNode): | 
|  | """A base class for constants, attributes and operations.""" | 
|  |  | 
|  | def __init__(self, ast, doc_js_interface_name): | 
|  | IDLNode.__init__(self, ast) | 
|  | self.type = self._convert_first(ast, 'Type', IDLType) | 
|  | self._convert_ext_attrs(ast) | 
|  | self._convert_annotations(ast) | 
|  | self.doc_js_interface_name = doc_js_interface_name | 
|  | self.is_fc_suppressed = 'Suppressed' in self.ext_attrs | 
|  | self.is_static = self._has(ast, 'Static') | 
|  |  | 
|  |  | 
|  | class IDLOperation(IDLMember): | 
|  | """IDLNode specialization for 'type name(args)' declarations.""" | 
|  | def __init__(self, ast, doc_js_interface_name): | 
|  | IDLMember.__init__(self, ast, doc_js_interface_name) | 
|  | self.type = self._convert_first(ast, 'ReturnType', IDLType) | 
|  | self.arguments = self._convert_all(ast, 'Argument', IDLArgument) | 
|  | self.raises = self._convert_first(ast, 'Raises', IDLType) | 
|  | self.specials = self._find_all(ast, 'Special') | 
|  | self.is_stringifier = self._has(ast, 'Stringifier') | 
|  | def _extra_repr(self): | 
|  | return [self.arguments] | 
|  |  | 
|  |  | 
|  | class IDLAttribute(IDLMember): | 
|  | """IDLNode specialization for 'attribute type name' declarations.""" | 
|  | def __init__(self, ast, doc_js_interface_name): | 
|  | IDLMember.__init__(self, ast, doc_js_interface_name) | 
|  | self.is_read_only = self._has(ast, 'ReadOnly') | 
|  | # There are various ways to define exceptions for attributes: | 
|  | self.raises = self._convert_first(ast, 'Raises', IDLType) | 
|  | self.get_raises = self.raises \ | 
|  | or self._convert_first(ast, 'GetRaises', IDLType) | 
|  | self.set_raises = self.raises \ | 
|  | or self._convert_first(ast, 'SetRaises', IDLType) | 
|  | # FremontCut IDL syntax defines getters and setters separately: | 
|  | self.is_fc_getter = self._has(ast, 'AttrGetter') | 
|  | self.is_fc_setter = self._has(ast, 'AttrSetter') | 
|  | def _extra_repr(self): | 
|  | extra = [] | 
|  | if self.is_fc_getter: extra.append('get') | 
|  | if self.is_fc_setter: extra.append('set') | 
|  | if self.is_read_only: extra.append('readonly') | 
|  | if self.raises: extra.append('raises') | 
|  | return extra | 
|  |  | 
|  | class IDLConstant(IDLMember): | 
|  | """IDLNode specialization for 'const type name = value' declarations.""" | 
|  | def __init__(self, ast, doc_js_interface_name): | 
|  | IDLMember.__init__(self, ast, doc_js_interface_name) | 
|  | self.value = self._find_first(ast, 'ConstExpr') | 
|  |  | 
|  |  | 
|  | class IDLArgument(IDLNode): | 
|  | """IDLNode specialization for operation arguments.""" | 
|  | def __init__(self, ast): | 
|  | IDLNode.__init__(self, ast) | 
|  | self.type = self._convert_first(ast, 'Type', IDLType) | 
|  | self._convert_ext_attrs(ast) | 
|  | # WebKit and Web IDL differ in how Optional is declared: | 
|  | self.is_optional = self._has(ast, 'Optional') \ | 
|  | or ('Optional' in self.ext_attrs) | 
|  |  | 
|  |  | 
|  | class IDLImplementsStatement(IDLNode): | 
|  | """IDLNode specialization for 'X implements Y' declarations.""" | 
|  | def __init__(self, ast): | 
|  | IDLNode.__init__(self, ast) | 
|  | self.implementor = self._convert_first(ast, 'ImplStmtImplementor', | 
|  | IDLType) | 
|  | self.implemented = self._convert_first(ast, 'ImplStmtImplemented', | 
|  | IDLType) | 
|  |  | 
|  |  | 
|  | class IDLAnnotations(IDLDictNode): | 
|  | """IDLDictNode specialization for a list of FremontCut annotations.""" | 
|  | def __init__(self, ast=None): | 
|  | IDLDictNode.__init__(self, ast) | 
|  | self.id = None | 
|  | if not ast: | 
|  | return | 
|  | for annotation in self._find_all(ast, 'Annotation'): | 
|  | name = self._find_first(annotation, 'Id') | 
|  | value = IDLAnnotation(annotation) | 
|  | self[name] = value | 
|  |  | 
|  |  | 
|  | class IDLAnnotation(IDLDictNode): | 
|  | """IDLDictNode specialization for one annotation.""" | 
|  | def __init__(self, ast=None): | 
|  | IDLDictNode.__init__(self, ast) | 
|  | self.id = None | 
|  | if not ast: | 
|  | return | 
|  | for arg in self._find_all(ast, 'AnnotationArg'): | 
|  | name = self._find_first(arg, 'Id') | 
|  | value = self._find_first(arg, 'AnnotationArgValue') | 
|  | self[name] = value |