blob: 8be47969c492c50e65072e1d687cac9c29c05717 [file] [log] [blame]
#!/usr/bin/env python3
# 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 generator import MultitypeSortKey
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):
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 ==
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,
def __init__(self, ast, id=None):
"""Initializes an IDLNode from a PegParser AST output."""
if ast: = self._find_first(ast, 'Id') if ast is not None else None
# Support synthesized IDLNode created w/o an AST (e.g., setlike support). = 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])
return '<%s %s 0x%x>' % (type(self).__name__,
('%s %s' % (, extras)).strip(),
return '<%s %s 0x%x>' % (type(self).__name__, extras, hash(self))
except Exception as e:
return "can't convert to string: %s" % e
def _extra_repr(self):
"""Returns string of extra info for __repr__()."""
return ''
def __eq__(self, other):
"""Override default equals operation.
IDLNodes are equal if all their properties are equal."""
if other is None or not isinstance(other, IDLNode):
return 1
return self.__dict__.__eq__(other.__dict__)
def __hash__(self):
"""Define default hashing behavior.
In order to comply with a == b => hash(a) == hash(b), we recursively iterate
self.__dict__ and convert all objects to hashable objects."""
return self.to_hash()
def reset_id(self, newId):
"""Reset the id of the Node. This is typically done during a normalization
phase (e.g., "DOMWindow" -> "Window").""" = newId
def all(self, type_filter=None):
"""Returns a list containing this node and all it child nodes
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):
for v in self._all_subnodes():
if isinstance(v, IDLNode):
elif isinstance(v, list):
for item in v:
if isinstance(item, IDLNode):
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.
elif isinstance(v, IDLDictNode) and not len(v):
# Skip empty dict sub-nodes.
elif isinstance(v, list):
# Convert lists:
new_v = []
for sub_node in v:
if isinstance(sub_node, IDLNode):
# Convert sub-node:
v = new_v
elif isinstance(v, IDLNode):
# Convert sub-node:
v = v.to_dict()
res[k] = v
return res
def to_hash(self):
return hash(self._to_hashable(self))
def _to_hashable(self, obj):
# By default, lists and dicts are not hashable, and user-defined objects
# are unordered. In order to make a consistent hash for a given object,
# this converts unhashable types and sorts properties.
if isinstance(obj, list):
# Convert lists to tuples.
new_obj = []
for item in obj:
return tuple(new_obj)
elif isinstance(obj, dict):
# Convert dicts to frozensets of tuples.
new_obj = set()
# Sort to ensure fixed order.
for (k2, v2) in sorted(obj.items(), key=MultitypeSortKey):
new_obj.add((self._to_hashable(k2), self._to_hashable(v2)))
return frozenset(new_obj)
elif hasattr(obj, '__dict__'):
items = []
# Sort properties to ensure fixed order.
for (k, v) in sorted(obj.__dict__.items(), key=MultitypeSortKey):
items.append((k, self._to_hashable(v)))
return tuple(items)
return obj
def _find_all(self, ast, label, max_results=sys.maxsize):
"""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.
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')
sub_res = self._find_all(childAst, label,
max_results - len(res))
elif isinstance(ast, tuple):
(nodeLabel, value) = ast
if nodeLabel == label:
# 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]
elif isinstance(field_value, list):
for item in field_value:
elif label == 'ParentInterface' or label == 'InterfaceType':
# Fetch the AST for the parent interface.
parent_idlnode = new_asts[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',
'', # 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.
A list of the converted nodes.
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
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)
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
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'):
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',
for callback_function in callback_functions:
for annotation in callback_function.annotations:
callback = callback_function.annotations[annotation]
cb_interface = IDLInterface(None,
cb_interface.ext_attrs['Callback'] = len(callback.arguments)
op = IDLOperation(None,, "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]
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(
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(
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(
implement_statement = self._createImplementsStatement(
implementor, implemented_name)
elif in dependency.get_interfaces_info():
interface_info = dependency.get_interfaces_info()[interface.
implements = []
if 'implements_interfaces' in interface_info:
implements = interface_info['implements_interfaces']
if not (blink_interface.is_partial) and len(implements) > 0:
implementor = new_asts[].interfaces.get(
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 = []
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):
elif typedef_type.idl_type.base_type == 'Dictionary':
dictionary = IDLDictionary(typedef_type, True)
self.enums = self._convert_all(ast, 'Enum', IDLEnum)
def _createImplementsStatement(self, implementor, implemented_name):
implemented = new_asts[implemented_name].interfaces.get(
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.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()[]
# TODO(terry): Same handling for implementsStatements as in IDLFile?
self.implementsStatements = interface_info[
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:
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 == 'NamedConstructor':
constructor_name = ast.extended_attributes[
constructor_name = None
func_value = IDLExtAttrFunctionValue(
constructor_name, constructor.arguments, True)
if name == 'Constructor':
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.
self[name] = value
ext_attrs_ast = self._find_first(ast, 'ExtAttrs')
if not ext_attrs_ast:
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)
func_value = self._find_first(value, 'ExtAttrFunctionValue')
if func_value:
# E.g. NamedConstructor=Audio(optional DOMString src)
self[name] = IDLExtAttrFunctionValue(
self._find_first(func_value, 'ExtAttrArgList'))
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):
def __init__(self, func_value_ast, arg_list_ast, is_blink=False):
IDLNode.__init__(self, func_value_ast)
if is_blink:
# Blink path = func_value_ast # func_value_ast is the function name for Blink.
self.arguments = []
for argument in arg_list_ast:
self.arguments = self._convert_all(arg_list_ast, 'Argument',
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
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).
self.nullable = self._has(ast, 'Nullable')
# Search for a 'ScopedName' or any label ending with 'Type'.
if isinstance(ast, list): = self._find_first(ast, 'ScopedName')
if not
# 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) = findType(ast)
# TODO(terry): Remove array_modifiers id has [] appended, keep for old
# parsing.
array_modifiers = self._find_first(ast, 'ArrayModifiers')
if array_modifiers: += array_modifiers
elif isinstance(ast, tuple):
(label, value) = ast
if label == 'ScopedName': = value
else: = self._label_to_type(label, ast)
elif isinstance(ast, str): = 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 ( in _unions_to_any):
# TODO(terry): For union types use any otherwise type is unionType is
# not found and is removed during merging. = 'any'
type_name = str(ast)
# TODO(terry): For now don't handle unrestricted types see
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) = type_name
# IdlUnionType
if ast.is_union_type:
if not ( in _unions_to_any):
# TODO(terry): For union types use any otherwise type is unionType is
# not found and is removed during merging. = '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
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)
if not (isinstance(ast, list)) and ast.__module__ == "idl_definitions":
# Blink AST
self.values = ast.values
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.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.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 =
if (typedefDictionary):
# Dictionary is a typedef to a union.
class IDLDictionaryMembers(IDLDictNode):
"""IDLDictionaryMembers specialization for a list of FremontCut dictionary values."""
def __init__(self, ast=None, js_name=None):
IDLDictNode.__init__(self, ast) = None
if not ast:
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,
""" 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)
syn_op.type.nullable = result_nullable
for argument in arguments:
arg = IDLArgument(None, argument[1])
arg.type = argument[0]
arg.optional = argument[2] if len(argument) > 2 else False
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' %
set_op = generate_operation(, 'void', 'forEach',
[[IDLType(None, callback_name), 'callback'],
[IDLType(None, 'any'), 'thisArg', True]])
set_op = generate_operation(, 'boolean', 'has',
[[IDLType(None, set_like.value_type.base_type), 'arg']])
if not set_like.is_read_only:
# Issue #45676: `add` can return null on Firefox, so this should be
# typed nullable.
add_result_nullable = True
set_op = generate_operation(,, 'add',
[[IDLType(None, set_like.value_type.base_type), 'arg']],
set_op = generate_operation(, 'boolean', 'delete',
[[IDLType(None, set_like.value_type.base_type), 'arg']])
set_op = generate_operation(, 'void', 'clear', [])
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: = id
self.parents = self._convert_all(ast, 'ParentInterface',
javascript_interface_name = self.ext_attrs.get('InterfaceName',
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 = [
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.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 != new_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 ( == and
attribute.is_read_only == candidate.is_read_only):
return True
return False
class IDLParentInterface(IDLNode):
"""This IDLNode specialization is for 'Interface Child : Parent {}'
def __init__(self, ast):
IDLNode.__init__(self, 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)
# 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
self.type = self._convert_first(ast, 'Type', IDLType)
self.type = resolveTypedef(self.type)
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 = []
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 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); = '__propertyQuery__'
elif self.ext_attrs.get('ImplementedAs'): = self.ext_attrs.get('ImplementedAs')
else: = '__getter__'
elif self.specials == ['setter']: = '__setter__'
# Special case: if it's a setter, ignore 'declared' return type
self.type = IDLType([('VoidType', None)])
elif self.specials == ['deleter']: = '__delete__'
raise Exception('Cannot handle %s: operation has no id' % ast)
if len(self.arguments) >= 1 and ( in _operation_suffix_map
) and not self.ext_attrs.get('ImplementedAs'):
arg = self.arguments[0]
operation_category = 'Named' if == 'DOMString' else 'Indexed'
'ImplementedAs', 'anonymous%s%s' %
(operation_category, _operation_suffix_map[]))
def __repr__(self):
return '<IDLOperation(id = %s)>' % (
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)
# 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
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')
# TODO(vsm): Recover this from the type instead.
if 'Callback' in
self.ext_attrs['Callback'] = None
def __repr__(self):
return '<IDLArgument(type = %s, id = %s)>' % (self.type,
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',
self.implemented = self._convert_first(ast, 'ImplStmtImplemented',
class IDLAnnotations(IDLDictNode):
"""IDLDictNode specialization for a list of FremontCut annotations."""
def __init__(self, ast=None):
IDLDictNode.__init__(self, ast) = None
if not ast:
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) = None
if not ast:
for arg in self._find_all(ast, 'AnnotationArg'):
name = self._find_first(arg, 'Id')
value = self._find_first(arg, 'AnnotationArgValue')
self[name] = value