| #!/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 os |
| import sys |
| |
| import idl_definitions |
| from idl_types import IdlType, IdlNullableType, IdlUnionType, IdlArrayOrSequenceType |
| import dependency |
| |
| new_asts = {} |
| |
| # Report of union types mapped to any. |
| _unions_to_any = [] |
| |
| |
| def report_unions_to_any(): |
| global _unions_to_any |
| |
| warnings = [] |
| for union_id in sorted(_unions_to_any): |
| warnings.append('Union type %s is mapped to \'any\'' % union_id) |
| |
| return warnings |
| |
| |
| # Ugly but Chrome IDLs can reference typedefs in any IDL w/o an include. So we |
| # need to remember any typedef seen then alias any reference to a typedef. |
| _typeDefsFixup = [] |
| |
| |
| def _addTypedef(typedef): |
| _typeDefsFixup.append(typedef) |
| |
| |
| def resolveTypedef(type): |
| """ Given a type if it's a known typedef (only typedef's that aren't union) |
| are remembered for fixup. typedefs that are union type are mapped to |
| any so those we don't need to alias. typedefs referenced in the file |
| where the typedef was defined are automatically aliased to the real type. |
| This resolves typedef where the declaration is in another IDL file. |
| """ |
| for typedef in _typeDefsFixup: |
| if typedef.id == type.id: |
| return typedef.type |
| |
| return type |
| |
| |
| _operation_suffix_map = { |
| '__getter__': "Getter", |
| '__setter__': "Setter", |
| '__delete__': "Deleter", |
| } |
| |
| class IDLNode(object): |
| """Base class for all IDL elements. |
| IDLNode may contain various child nodes, and have properties. Examples |
| of IDLNode are interfaces, interface members, function arguments, |
| etc. |
| """ |
| |
| def __init__(self, ast, id=None): |
| """Initializes an IDLNode from a PegParser AST output.""" |
| if ast: |
| self.id = self._find_first(ast, 'Id') if ast is not None else None |
| else: |
| # Support synthesized IDLNode created w/o an AST (e.g., setlike support). |
| self.id = id |
| |
| 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 reset_id(self, newId): |
| """Reset the id of the Node. This is typically done during a normalization |
| phase (e.g., "DOMWindow" -> "Window").""" |
| self.id = newId |
| |
| 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: |
| if childAst and \ |
| not(isinstance(childAst, dict)) and \ |
| not(isinstance(childAst, str)) and \ |
| not(isinstance(childAst, tuple)) and \ |
| childAst.__module__ == "idl_definitions": |
| field_name = self._convert_label_to_field(label) |
| if hasattr(childAst, field_name): |
| field_value = getattr(childAst, field_name) |
| # It's an IdlType we need the string name of the type. |
| if field_name == 'idl_type': |
| field_value = getattr(field_value, 'base_type') |
| res.append(field_value) |
| else: |
| 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) |
| # TODO(terry): Seems bogus to check for so many things probably better to just |
| # pass in blink_compile and drive it off from that... |
| elif (ast and not (isinstance(ast, dict)) and |
| not (isinstance(ast, str)) and |
| (ast.__module__ == "idl_definitions" or |
| ast.__module__ == "idl_types")): |
| field_name = self._convert_label_to_field(label) |
| if hasattr(ast, field_name): |
| field_value = getattr(ast, field_name) |
| if field_value: |
| if label == 'Interface' or label == 'Enum' or label == "Dictionary": |
| for key in field_value: |
| value = field_value[key] |
| res.append(value) |
| elif isinstance(field_value, list): |
| for item in field_value: |
| res.append(item) |
| elif label == 'ParentInterface' or label == 'InterfaceType': |
| # Fetch the AST for the parent interface. |
| parent_idlnode = new_asts[field_value] |
| res.append(parent_idlnode.interfaces[field_value]) |
| else: |
| res.append(field_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 |
| |
| # Mapping from original AST tuple names to new AST field names idl_definitions.Idl*. |
| def _convert_label_to_field(self, label): |
| label_field = { |
| # Keys old AST names, Values Blink IdlInterface names. |
| 'ParentInterface': 'parent', |
| 'Id': 'name', |
| 'Interface': 'interfaces', |
| 'Callback_Function': 'callback_functions', |
| 'Callback': 'is_callback', |
| 'Partial': 'is_partial', |
| 'Operation': 'operations', |
| 'Attribute': 'attributes', |
| 'Const': 'constants', |
| 'Type': 'idl_type', |
| 'ExtAttrs': 'extended_attributes', |
| 'Special': 'specials', |
| 'ReturnType': 'idl_type', |
| 'Argument': 'arguments', |
| 'InterfaceType': 'name', |
| 'ConstExpr': 'value', |
| 'Static': 'is_static', |
| 'ReadOnly': 'is_read_only', |
| 'Optional': 'is_optional', |
| 'Nullable': 'is_nullable', |
| 'Enum': 'enumerations', |
| 'Annotation': |
| '', # TODO(terry): Ignore annotation used for database cache. |
| 'TypeDef': '', # typedef in an IDL are already resolved. |
| 'Dictionary': 'dictionaries', |
| 'Member': 'members', |
| 'Default': 'default_value', # Dictionary member default value |
| } |
| result = label_field.get(label) |
| if result != '' and not (result): |
| print 'FATAL ERROR: AST mapping name not found %s.' % label |
| return result if result else '' |
| |
| 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) |
| |
| def _convert_constants(self, ast, js_name): |
| """Helper method for uniform conversion of dictionary members.""" |
| self.members = IDLDictionaryMembers(ast, js_name) |
| |
| |
| 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 setdefault(self, key, value=None): |
| return self.__map.setdefault(key, value) |
| |
| 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 interfaces.""" |
| |
| DART_IDL = 'dart.idl' |
| |
| def __init__(self, ast, filename=None): |
| IDLNode.__init__(self, ast) |
| self.filename = filename |
| |
| filename_basename = os.path.basename(filename) |
| |
| # Report of union types mapped to any. |
| |
| # Remember all the typedefs before we start walking the AST. Some |
| # IDLs now have typedefs before interfaces. So we need to remember |
| # to resolve the typedefs. |
| self.typeDefs = self._convert_all(ast, 'TypeDef', IDLTypeDef) |
| for typedefName in ast.typedefs: |
| typedef_type = ast.typedefs[typedefName] |
| # Ignore unions and dictionaries for now we just want normal typedefs to resolve our arguments/types. |
| if not (isinstance(typedef_type.idl_type, IdlUnionType)) and not ( |
| typedef_type.idl_type.base_type == 'Dictionary'): |
| _addTypedef(IDLTypeDef(typedef_type)) |
| |
| self.interfaces = self._convert_all(ast, 'Interface', IDLInterface) |
| self.dictionaries = self._convert_all(ast, 'Dictionary', IDLDictionary) |
| |
| if len(ast.callback_functions) > 0: |
| callback_functions = self._convert_all(ast, 'Callback_Function', |
| IDLCallbackFunction) |
| for callback_function in callback_functions: |
| for annotation in callback_function.annotations: |
| callback = callback_function.annotations[annotation] |
| cb_interface = IDLInterface(None, callback.name) |
| cb_interface.ext_attrs['Callback'] = len(callback.arguments) |
| op = IDLOperation(None, cb_interface.id, "handleEvent") |
| op.type = IDLType(callback.idl_type) |
| op.type = resolveTypedef(op.type) |
| |
| if len(callback.arguments) > 0: |
| op.arguments = self._convert_all( |
| callback, 'Argument', IDLArgument) |
| |
| cb_interface.operations = [op] |
| self.interfaces.append(cb_interface) |
| |
| is_blink = not (isinstance( |
| ast, list)) and ast.__module__ == 'idl_definitions' |
| |
| if is_blink: |
| # implements is handled by the interface merging step (see the function |
| # merge_interface_dependencies). |
| for interface in self.interfaces: |
| blink_interface = ast.interfaces.get(interface.id) |
| if filename_basename == self.DART_IDL: |
| # Special handling for dart.idl we need to remember the interface, |
| # since we could have many (not one interface / file). Then build up |
| # the IDLImplementsStatement for any implements in dart.idl. |
| interface_info = dependency.get_interfaces_info( |
| )['__dart_idl___'] |
| |
| self.implementsStatements = [] |
| |
| implement_pairs = interface_info['implement_pairs'] |
| for implement_pair in implement_pairs: |
| interface_name = implement_pair[0] |
| implemented_name = implement_pair[1] |
| |
| implementor = new_asts[interface_name].interfaces.get( |
| interface_name) |
| implement_statement = self._createImplementsStatement( |
| implementor, implemented_name) |
| |
| self.implementsStatements.append(implement_statement) |
| elif interface.id in dependency.get_interfaces_info(): |
| interface_info = dependency.get_interfaces_info()[interface. |
| id] |
| |
| implements = interface_info[ |
| 'implements_interfaces'] if interface_info.has_key( |
| 'implements_interfaces') else [] |
| if not (blink_interface.is_partial) and len(implements) > 0: |
| implementor = new_asts[interface.id].interfaces.get( |
| interface.id) |
| |
| self.implementsStatements = [] |
| |
| # TODO(terry): Need to handle more than one implements. |
| for implemented_name in implements: |
| implement_statement = self._createImplementsStatement( |
| implementor, implemented_name) |
| self.implementsStatements.append( |
| implement_statement) |
| else: |
| self.implementsStatements = [] |
| else: |
| self.implementsStatements = self._convert_all( |
| ast, 'ImplStmt', IDLImplementsStatement) |
| |
| # Record typedefs that are unions. |
| for typedefName in ast.typedefs: |
| typedef_type = ast.typedefs[typedefName] |
| if isinstance(typedef_type.idl_type, IdlUnionType): |
| self.typeDefs.append(IDLTypeDef(typedef_type)) |
| elif typedef_type.idl_type.base_type == 'Dictionary': |
| dictionary = IDLDictionary(typedef_type, True) |
| self.dictionaries.append(dictionary) |
| |
| self.enums = self._convert_all(ast, 'Enum', IDLEnum) |
| |
| def _createImplementsStatement(self, implementor, implemented_name): |
| implemented = new_asts[implemented_name].interfaces.get( |
| implemented_name) |
| |
| implement_statement = IDLImplementsStatement(implemented) |
| |
| implement_statement.implementor = IDLType(implementor) |
| implement_statement.implemented = IDLType(implemented) |
| |
| return implement_statement |
| |
| |
| 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) |
| |
| is_blink = ast.__module__ == 'idl_definitions' |
| |
| # No reason to handle typedef they're already aliased in Blink's AST. |
| self.typeDefs = [] if is_blink else self._convert_all( |
| ast, 'TypeDef', IDLTypeDef) |
| |
| self.enums = self._convert_all(ast, 'Enum', IDLNode) |
| |
| if is_blink: |
| # implements is handled by the interface merging step (see the function |
| # merge_interface_dependencies). |
| for interface in self.interfaces: |
| interface_info = get_interfaces_info()[interface.id] |
| # TODO(terry): Same handling for implementsStatements as in IDLFile? |
| self.implementsStatements = interface_info[ |
| 'implements_interfaces'] |
| else: |
| 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 |
| if not (isinstance(ast, list)) and ast.__module__ == "idl_definitions": |
| # Pull out extended attributes from Blink AST. |
| for name, value in ast.extended_attributes.items(): |
| # TODO(terry): Handle constructors... |
| if name == 'NamedConstructor' or name == 'Constructor': |
| for constructor in ast.constructors: |
| if constructor.name == 'NamedConstructor': |
| constructor_name = ast.extended_attributes[ |
| 'NamedConstructor'] |
| else: |
| constructor_name = None |
| func_value = IDLExtAttrFunctionValue( |
| constructor_name, constructor.arguments, True) |
| if name == 'Constructor': |
| self.setdefault('Constructor', |
| []).append(func_value) |
| else: |
| self[name] = func_value |
| elif name == 'SetWrapperReferenceTo': |
| # NOTE: No need to process handling for GC wrapper. But if its a reference |
| # to another type via an IdlArgument we'd need to convert to IDLArgument |
| # otherwise the type might be a reference to another type and the circularity |
| # will break deep_copy which is done later to the interfaces in the |
| # database. If we every need SetWrapperReferenceTo then we'd need to |
| # convert IdlArgument to IDLArgument. |
| continue |
| else: |
| self[name] = value |
| else: |
| 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') |
| |
| if name == 'Constructor': |
| # There might be multiple constructor attributes, collect them |
| # as a list. Represent plain Constructor attribute |
| # (without any signature) as None. |
| assert value is None |
| func_value = None |
| ctor_args = self._find_first(ext_attr, 'ExtAttrArgList') |
| if ctor_args: |
| func_value = IDLExtAttrFunctionValue(None, ctor_args) |
| self.setdefault('Constructor', []).append(func_value) |
| continue |
| |
| func_value = self._find_first(value, 'ExtAttrFunctionValue') |
| if func_value: |
| # E.g. NamedConstructor=Audio(optional DOMString src) |
| self[name] = IDLExtAttrFunctionValue( |
| func_value, |
| self._find_first(func_value, 'ExtAttrArgList')) |
| continue |
| |
| self[name] = value |
| |
| def _all_subnodes(self): |
| # Extended attributes may contain IDLNodes, e.g. IDLExtAttrFunctionValue |
| return self.values() |
| |
| |
| # IDLExtAttrFunctionValue is used for constructors defined in the IDL. |
| class IDLExtAttrFunctionValue(IDLNode): |
| """IDLExtAttrFunctionValue.""" |
| |
| def __init__(self, func_value_ast, arg_list_ast, is_blink=False): |
| IDLNode.__init__(self, func_value_ast) |
| if is_blink: |
| # Blink path |
| self.id = func_value_ast # func_value_ast is the function name for Blink. |
| self.arguments = [] |
| for argument in arg_list_ast: |
| self.arguments.append(IDLArgument(argument)) |
| else: |
| 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. |
| NOTE: AST of None implies synthesize IDLType the id is passed in used by |
| setlike.""" |
| |
| def __init__(self, ast, id=None): |
| global _unions_to_any |
| |
| IDLNode.__init__(self, ast, id) |
| |
| if not ast: |
| # Support synthesized IDLType with no AST (e.g., setlike support). |
| return |
| |
| self.nullable = self._has(ast, 'Nullable') |
| # 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 |
| def findType(ast): |
| for label, childAst in ast: |
| if label.endswith('Type'): |
| type = self._label_to_type(label, ast) |
| if type != 'sequence': |
| return type |
| type_ast = self._find_first(childAst, 'Type') |
| if not type_ast: |
| return type |
| return 'sequence<%s>' % findType(type_ast) |
| raise Exception('No type declaration found in %s' % ast) |
| |
| self.id = findType(ast) |
| # TODO(terry): Remove array_modifiers id has [] appended, keep for old |
| # parsing. |
| array_modifiers = self._find_first(ast, 'ArrayModifiers') |
| if array_modifiers: |
| self.id += array_modifiers |
| 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 |
| # New blink handling. |
| elif ast.__module__ == "idl_types": |
| if isinstance(ast, IdlType) or isinstance(ast, IdlArrayOrSequenceType) or \ |
| isinstance(ast, IdlNullableType): |
| if isinstance(ast, |
| IdlNullableType) and ast.inner_type.is_union_type: |
| # Report of union types mapped to any. |
| if not (self.id in _unions_to_any): |
| _unions_to_any.append(self.id) |
| # TODO(terry): For union types use any otherwise type is unionType is |
| # not found and is removed during merging. |
| self.id = 'any' |
| else: |
| type_name = str(ast) |
| # TODO(terry): For now don't handle unrestricted types see |
| # https://code.google.com/p/chromium/issues/detail?id=354298 |
| type_name = type_name.replace('unrestricted ', '', 1) |
| |
| # TODO(terry): Handled USVString as a DOMString. |
| type_name = type_name.replace('USVString', 'DOMString', 1) |
| type_name = type_name.replace('HTMLString', 'DOMString', 1) |
| |
| # TODO(terry); WindowTimers setInterval/setTimeout overloads with a |
| # Function type - map to any until the IDL uses union. |
| type_name = type_name.replace('Function', 'any', 1) |
| |
| self.id = type_name |
| else: |
| # IdlUnionType |
| if ast.is_union_type: |
| if not (self.id in _unions_to_any): |
| _unions_to_any.append(self.id) |
| # TODO(terry): For union types use any otherwise type is unionType is |
| # not found and is removed during merging. |
| self.id = 'any' |
| # TODO(terry): Any union type e.g. 'type1 or type2 or type2', |
| # 'typedef (Type1 or Type2) UnionType' |
| # Is a problem we need to extend IDLType and IDLTypeDef to handle more |
| # than one type. |
| # |
| # Also for typedef's e.g., |
| # typedef (Type1 or Type2) UnionType |
| # should consider synthesizing a new interface (e.g., UnionType) that's |
| # both Type1 and Type2. |
| if not self.id: |
| print '>>>> __module__ %s' % ast.__module__ |
| raise SyntaxError('Could not parse type %s' % (ast)) |
| |
| def _label_to_type(self, label, ast): |
| 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 IDLEnum(IDLNode): |
| """IDLNode for 'enum [id] { [string]+ }'""" |
| |
| def __init__(self, ast): |
| IDLNode.__init__(self, ast) |
| self._convert_annotations(ast) |
| if not (isinstance(ast, list)) and ast.__module__ == "idl_definitions": |
| # Blink AST |
| self.values = ast.values |
| else: |
| self.values = self._find_all(ast, 'StringLiteral') |
| |
| # TODO(terry): Need to handle emitting of enums for dart:html |
| |
| |
| class IDLCallbackFunction(IDLNode): |
| """IDLNode for 'callback [type] [id]' declarations.""" |
| |
| def __init__(self, ast): |
| IDLNode.__init__(self, ast) |
| self._convert_annotations(ast) |
| self.type = self._convert_first(ast, 'Type', IDLType) |
| |
| |
| 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 IDLDictionary(IDLNode): |
| """IDLDictionary node contains members, |
| as well as parent references.""" |
| |
| def __init__(self, ast, typedefDictionary=False): |
| IDLNode.__init__(self, ast) |
| |
| self.javascript_binding_name = self.id |
| if (typedefDictionary): |
| # Dictionary is a typedef to a union. |
| self._convert_ext_attrs(None) |
| else: |
| self._convert_ext_attrs(ast) |
| self._convert_constants(ast, self.id) |
| |
| |
| class IDLDictionaryMembers(IDLDictNode): |
| """IDLDictionaryMembers specialization for a list of FremontCut dictionary values.""" |
| |
| def __init__(self, ast=None, js_name=None): |
| IDLDictNode.__init__(self, ast) |
| self.id = None |
| if not ast: |
| return |
| for member in self._find_all(ast, 'Member'): |
| name = self._find_first(member, 'Id') |
| value = IDLDictionaryMember(member, js_name) |
| self[name] = value |
| |
| |
| def generate_callback(interface_name, result_type, arguments): |
| syn_op = IDLOperation(None, interface_name, 'callback') |
| |
| syn_op.type = resolveTypedef(result_type) |
| syn_op.arguments = arguments |
| |
| return syn_op |
| |
| |
| def generate_operation(interface_name, result_type_name, oper_name, arguments): |
| """ Synthesize an IDLOperation with no AST used for support of setlike.""" |
| """ Arguments is a list of argument where each argument is: |
| [IDLType, argument_name, optional_boolean] """ |
| |
| syn_op = IDLOperation(None, interface_name, oper_name) |
| |
| syn_op.type = IDLType(None, result_type_name) |
| syn_op.type = resolveTypedef(syn_op.type) |
| |
| for argument in arguments: |
| arg = IDLArgument(None, argument[1]) |
| arg.type = argument[0] |
| arg.optional = argument[2] if len(argument) > 2 else False |
| syn_op.arguments.append(arg) |
| |
| return syn_op |
| |
| |
| def generate_setLike_operations_properties(interface, set_like): |
| """ |
| Need to create (in our database) a number of operations. This is a new IDL |
| syntax, the implied operations for a set now use setlike<T> where T is a known |
| type e.g., setlike<FontFace> setlike implies these operations are generated: |
| |
| void forEach(any callback, optional any thisArg); |
| boolean has(FontFace fontFace); |
| boolean has(FontFace fontFace); |
| |
| if setlike is not read-only these operations are generated: |
| |
| FontFaceSet add(FontFace value); |
| boolean delete(FontFace value); |
| void clear(); |
| """ |
| setlike_ops = [] |
| """ |
| Need to create a typedef for a function callback e.g., |
| a setlike will need a callback that has the proper args in FontFaceSet that is |
| three arguments, etc. |
| |
| typedef void FontFaceSetForEachCallback( |
| FontFace fontFace, FontFace fontFaceAgain, FontFaceSet set); |
| |
| void forEach(FontFaceSetForEachCallback callback, [Object thisArg]); |
| """ |
| callback_name = '%sForEachCallback' % interface.id |
| set_op = generate_operation(interface.id, 'void', 'forEach', |
| [[IDLType(None, callback_name), 'callback'], |
| [IDLType(None, 'any'), 'thisArg', True]]) |
| setlike_ops.append(set_op) |
| |
| set_op = generate_operation( |
| interface.id, 'boolean', 'has', |
| [[IDLType(None, set_like.value_type.base_type), 'arg']]) |
| setlike_ops.append(set_op) |
| |
| if not set_like.is_read_only: |
| set_op = generate_operation( |
| interface.id, interface.id, 'add', |
| [[IDLType(None, set_like.value_type.base_type), 'arg']]) |
| setlike_ops.append(set_op) |
| set_op = generate_operation( |
| interface.id, 'boolean', 'delete', |
| [[IDLType(None, set_like.value_type.base_type), 'arg']]) |
| setlike_ops.append(set_op) |
| set_op = generate_operation(interface.id, 'void', 'clear', []) |
| setlike_ops.append(set_op) |
| |
| return setlike_ops |
| |
| |
| class IDLInterface(IDLNode): |
| """IDLInterface node contains operations, attributes, constants, |
| as well as parent references.""" |
| |
| def __init__(self, ast, id=None): |
| IDLNode.__init__(self, ast) |
| |
| if id: |
| self.id = id |
| |
| self._convert_ext_attrs(ast) |
| self._convert_annotations(ast) |
| |
| self.parents = self._convert_all(ast, 'ParentInterface', |
| IDLParentInterface) |
| |
| javascript_interface_name = self.ext_attrs.get('InterfaceName', self.id) |
| self.javascript_binding_name = javascript_interface_name |
| self.doc_js_name = javascript_interface_name |
| |
| if not (self._find_first(ast, 'Callback') is None): |
| self.ext_attrs['Callback'] = None |
| if not (self._find_first(ast, 'Partial') is None): |
| self.is_supplemental = True |
| self.ext_attrs['DartSupplemental'] = None |
| self.isMaplike = False |
| self.isMaplike_ro = False |
| self.maplike_key_value = [None, None] |
| if ast is not None and ast.maplike is not None: |
| self.isMaplike = True |
| self.isMaplike_ro = ast.maplike.is_read_only |
| self.maplike_key_value = [ |
| IDLType(ast.maplike.key_type), |
| IDLType(ast.maplike.value_type) |
| ] |
| |
| self.operations = self._convert_all( |
| ast, 'Operation', lambda ast: IDLOperation(ast, self.doc_js_name)) |
| |
| if not (id) and ast.setlike: |
| setlike_ops = generate_setLike_operations_properties( |
| self, ast.setlike) |
| for op in setlike_ops: |
| self.operations.append(op) |
| |
| 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 = 'DartSupplemental' in self.ext_attrs |
| self.is_no_interface_object = 'NoInterfaceObject' in self.ext_attrs |
| # TODO(terry): Can eliminate Suppressed when we're only using blink parser. |
| self.is_fc_suppressed = 'Suppressed' in self.ext_attrs or \ |
| 'DartSuppress' in self.ext_attrs |
| |
| def reset_id(self, new_id): |
| """Reset the id of the Interface and corresponding the JS names.""" |
| if self.id != new_id: |
| self.id = new_id |
| self.doc_js_name = new_id |
| self.javascript_binding_name = new_id |
| for member in self.operations: |
| member.doc_js_interface_name = new_id |
| for member in self.attributes: |
| member.doc_js_interface_name = new_id |
| for member in self.constants: |
| member.doc_js_interface_name = new_id |
| |
| def has_attribute(self, candidate): |
| for attribute in self.attributes: |
| if (attribute.id == candidate.id and |
| attribute.is_read_only == candidate.is_read_only): |
| 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, member_id=None): |
| if ast: |
| IDLNode.__init__(self, ast) |
| else: |
| # The ast is None to support synthesizing an IDLMember, member_id is only |
| # used when ast is None. |
| IDLNode.__init__(self, ast, member_id) |
| self.type = None |
| self.doc_js_interface_name = doc_js_interface_name |
| return |
| |
| self.type = self._convert_first(ast, 'Type', IDLType) |
| self.type = resolveTypedef(self.type) |
| |
| self._convert_ext_attrs(ast) |
| self._convert_annotations(ast) |
| self.doc_js_interface_name = doc_js_interface_name |
| # TODO(terry): Can eliminate Suppressed when we're only using blink parser. |
| self.is_fc_suppressed = 'Suppressed' in self.ext_attrs or \ |
| 'DartSuppress' 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, id=None): |
| IDLMember.__init__(self, ast, doc_js_interface_name, id) |
| |
| if not ast: |
| # Synthesize an IDLOperation with no ast used for setlike. |
| self.ext_attrs = IDLExtAttrs() |
| self.annotations = IDLAnnotations() |
| self.is_fc_suppressed = False |
| self.specials = [] |
| self.is_static = False |
| self.arguments = [] |
| return |
| |
| self.type = self._convert_first(ast, 'ReturnType', IDLType) |
| self.type = resolveTypedef(self.type) |
| |
| self.arguments = self._convert_all(ast, 'Argument', IDLArgument) |
| self.specials = self._find_all(ast, 'Special') |
| # Special case: there are getters of the form |
| # getter <ReturnType>(args). For now force the name to be __getter__, |
| # but it should be operator[] later. |
| if self.id is None: |
| if self.specials == ['getter']: |
| if self.ext_attrs.get('Custom') == 'PropertyQuery': |
| # Handling __propertyQuery__ the extended attribute is: |
| # [Custom=PropertyQuery] getter boolean (DOMString name); |
| self.id = '__propertyQuery__' |
| elif self.ext_attrs.get('ImplementedAs'): |
| self.id = self.ext_attrs.get('ImplementedAs') |
| else: |
| self.id = '__getter__' |
| elif self.specials == ['setter']: |
| self.id = '__setter__' |
| # Special case: if it's a setter, ignore 'declared' return type |
| self.type = IDLType([('VoidType', None)]) |
| elif self.specials == ['deleter']: |
| self.id = '__delete__' |
| else: |
| raise Exception('Cannot handle %s: operation has no id' % ast) |
| |
| if len(self.arguments) >= 1 and ( |
| self.id in _operation_suffix_map |
| ) and not self.ext_attrs.get('ImplementedAs'): |
| arg = self.arguments[0] |
| operation_category = 'Named' if arg.type.id == 'DOMString' else 'Indexed' |
| self.ext_attrs.setdefault( |
| 'ImplementedAs', 'anonymous%s%s' % |
| (operation_category, _operation_suffix_map[self.id])) |
| |
| def __repr__(self): |
| return '<IDLOperation(id = %s)>' % (self.id) |
| |
| def _extra_repr(self): |
| return [self.arguments] |
| |
| def SameSignatureAs(self, operation): |
| if self.type != operation.type: |
| return False |
| return [a.type for a in self.arguments] == [ |
| a.type for a in operation.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: |
| |
| def _extra_repr(self): |
| extra = [] |
| if self.is_read_only: extra.append('readonly') |
| 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, id=None): |
| if ast: |
| IDLNode.__init__(self, ast) |
| else: |
| # Synthesize an IDLArgument with no ast used for setlike. |
| IDLNode.__init__(self, ast, id) |
| self.ext_attrs = IDLExtAttrs() |
| self.default_value = None |
| self.default_value_is_null = False |
| return |
| |
| self.default_value = None |
| self.default_value_is_null = False |
| # Handle the 'argType arg = default'. IDL syntax changed from |
| # [default=NullString]. |
| if not isinstance(ast, list): |
| if isinstance(ast.default_value, |
| idl_definitions.IdlLiteral) and ast.default_value: |
| self.default_value = ast.default_value.value |
| self.default_value_is_null = ast.default_value.is_null |
| elif 'Default' in ast.extended_attributes: |
| # Work around [Default=Undefined] for arguments - only look in the model's |
| # default_value |
| self.default_value = ast.extended_attributes.get('Default') |
| self.default_value_is_null = False |
| |
| self.type = self._convert_first(ast, 'Type', IDLType) |
| self.type = resolveTypedef(self.type) |
| |
| self.optional = self._has(ast, 'Optional') |
| self._convert_ext_attrs(ast) |
| # TODO(vsm): Recover this from the type instead. |
| if 'Callback' in self.type.id: |
| self.ext_attrs['Callback'] = None |
| |
| def __repr__(self): |
| return '<IDLArgument(type = %s, id = %s)>' % (self.type, self.id) |
| |
| |
| class IDLDictionaryMember(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) |
| default_value = self._find_first(ast, 'Default') |
| self.value = default_value.value if default_value else None |
| |
| |
| class IDLImplementsStatement(IDLNode): |
| """IDLNode specialization for 'IMPLEMENTOR implements IMPLEMENTED' declarations.""" |
| |
| def __init__(self, ast): |
| IDLNode.__init__(self, ast) |
| if isinstance(ast, list) or ast.__module__ != 'idl_definitions': |
| 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 |