mirror of
https://github.com/dart-lang/sdk
synced 2024-10-02 10:19:17 +00:00
63a57a76ce
This reverts commit f8ff12008e
.
This CL is a revert of the initial revert and should be landed once
Flutter changes have been merged to handle the changes here.
Change-Id: I300a5efd776bf2a596743971f4e15ad62da77f5a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/153368
Commit-Queue: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
1159 lines
45 KiB
Python
1159 lines
45 KiB
Python
#!/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
|