# Copyright 2019 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from .argument import Argument
from .ast_group import AstGroup
from .async_iterator import AsyncIterator
from .attribute import Attribute
from .callback_function import CallbackFunction
from .callback_interface import CallbackInterface
from .composition_parts import Component
from .composition_parts import DebugInfo
from .composition_parts import Identifier
from .composition_parts import Location
from .constant import Constant
from .constructor import Constructor
from .dictionary import Dictionary
from .dictionary import DictionaryMember
from .enumeration import Enumeration
from .extended_attribute import ExtendedAttribute
from .extended_attribute import ExtendedAttributes
from .idl_type import IdlTypeFactory
from .includes import Includes
from .interface import AsyncIterable
from .interface import Interface
from .interface import Iterable
from .interface import Maplike
from .interface import Setlike
from .literal_constant import LiteralConstant
from .make_copy import make_copy
from .namespace import Namespace
from .operation import Operation
from .sync_iterator import SyncIterator
from .typedef import Typedef
def load_and_register_idl_definitions(filepaths, register_ir,
create_ref_to_idl_def, idl_type_factory):
"""
Reads ASTs from |filepaths| and builds IRs from ASTs.
Args:
filepaths: Paths to pickle files that store AST nodes.
register_ir: A callback function that registers the argument as an
IR.
create_ref_to_idl_def: A callback function that creates a reference
to an IDL definition from the given identifier.
idl_type_factory: All IdlType instances will be created through this
factory.
"""
assert callable(register_ir)
for filepath in filepaths:
asts = AstGroup.read_from_file(filepath)
builder = _IRBuilder(
component=Component(asts.component),
for_testing=asts.for_testing,
create_ref_to_idl_def=create_ref_to_idl_def,
idl_type_factory=idl_type_factory)
for file_node in asts:
assert file_node.GetClass() == 'File'
for top_level_node in file_node.GetChildren():
register_ir(builder.build_top_level_def(top_level_node))
class _IRBuilder(object):
def __init__(self, component, for_testing, create_ref_to_idl_def,
idl_type_factory):
"""
Args:
component: A Component to which the built IRs are associated.
for_testing: True if the IDL definitions are meant for testing
purpose only.
create_ref_to_idl_def: A callback function that creates a reference
to an IDL definition from the given identifier.
idl_type_factory: All IdlType instances will be created through this
factory.
"""
assert isinstance(component, Component)
assert isinstance(for_testing, bool)
assert callable(create_ref_to_idl_def)
assert isinstance(idl_type_factory, IdlTypeFactory)
self._component = component
self._for_testing = for_testing
self._create_ref_to_idl_def = create_ref_to_idl_def
self._idl_type_factory = idl_type_factory
def build_top_level_def(self, node):
build_functions = {
'Callback': self._build_callback_function,
'Dictionary': self._build_dictionary,
'Enum': self._build_enumeration,
'Includes': self._build_includes,
'Interface': self._build_interface,
'Namespace': self._build_namespace,
'Typedef': self._build_typedef,
}
ir = build_functions[node.GetClass()](node)
ir.code_generator_info.set_for_testing(self._for_testing)
return ir
# Builder functions for top-level definitions
def _build_interface(self, node):
if node.GetProperty('CALLBACK'):
return self._build_callback_interface(node)
identifier = Identifier(node.GetName())
child_nodes = list(node.GetChildren())
inherited = self._take_inheritance(child_nodes)
stringifier_members = self._take_stringifier(child_nodes)
async_iterable = self._take_async_iterable(
child_nodes, interface_identifier=identifier)
iterable = self._take_iterable(child_nodes,
interface_identifier=identifier)
maplike = self._take_maplike(
child_nodes, interface_identifier=identifier)
setlike = self._take_setlike(
child_nodes, interface_identifier=identifier)
extended_attributes = self._take_extended_attributes(child_nodes)
members = [
self._build_interface_member(
child, interface_identifier=identifier)
for child in child_nodes
]
if stringifier_members:
members.extend(filter(None, stringifier_members))
attributes = []
constants = []
constructors = []
operations = []
for member in members:
if isinstance(member, Attribute.IR):
attributes.append(member)
elif isinstance(member, Constant.IR):
constants.append(member)
elif isinstance(member, Constructor.IR):
constructors.append(member)
elif isinstance(member, Operation.IR):
operations.append(member)
else:
assert False
legacy_factory_functions = self._build_legacy_factory_function(node)
return Interface.IR(identifier=identifier,
is_partial=bool(node.GetProperty('PARTIAL')),
is_mixin=bool(node.GetProperty('MIXIN')),
inherited=inherited,
attributes=attributes,
constants=constants,
constructors=constructors,
legacy_factory_functions=legacy_factory_functions,
operations=operations,
async_iterable=async_iterable,
iterable=iterable,
maplike=maplike,
setlike=setlike,
extended_attributes=extended_attributes,
component=self._component,
debug_info=self._build_debug_info(node))
def _build_namespace(self, node):
child_nodes = list(node.GetChildren())
extended_attributes = self._take_extended_attributes(child_nodes)
members = list(map(self._build_interface_member, child_nodes))
attributes = []
constants = []
operations = []
for member in members:
if isinstance(member, Attribute.IR):
member.is_static = True
attributes.append(member)
elif isinstance(member, Constant.IR):
constants.append(member)
elif isinstance(member, Operation.IR):
member.is_static = True
operations.append(member)
else:
assert False
return Namespace.IR(
identifier=Identifier(node.GetName()),
is_partial=bool(node.GetProperty('PARTIAL')),
attributes=attributes,
constants=constants,
operations=operations,
extended_attributes=extended_attributes,
component=self._component,
debug_info=self._build_debug_info(node))
def _build_interface_member(self,
node,
fallback_extended_attributes=None,
interface_identifier=None):
def build_attribute(node):
child_nodes = list(node.GetChildren())
idl_type = self._take_type(child_nodes)
extended_attributes = self._take_extended_attributes(
child_nodes) or fallback_extended_attributes
assert not child_nodes
return Attribute.IR(
identifier=Identifier(node.GetName()),
idl_type=idl_type,
is_static=bool(node.GetProperty('STATIC')),
is_readonly=bool(node.GetProperty('READONLY')),
does_inherit_getter=bool(node.GetProperty('INHERIT')),
extended_attributes=extended_attributes,
component=self._component,
debug_info=self._build_debug_info(node))
def build_constant(node):
child_nodes = list(node.GetChildren())
value = self._take_constant_value(child_nodes)
extended_attributes = self._take_extended_attributes(
child_nodes) or fallback_extended_attributes
assert len(child_nodes) == 1, child_nodes[0].GetClass()
# idl_parser doesn't produce a 'Type' node for the type of a
# constant, hence we need to skip one level.
idl_type = self._build_type_internal(child_nodes)
return Constant.IR(
identifier=Identifier(node.GetName()),
idl_type=idl_type,
value=value,
extended_attributes=extended_attributes,
component=self._component,
debug_info=self._build_debug_info(node))
def build_constructor(node):
assert isinstance(interface_identifier, Identifier)
child_nodes = list(node.GetChildren())
arguments = self._take_arguments(child_nodes)
extended_attributes = self._take_extended_attributes(
child_nodes) or fallback_extended_attributes
assert not child_nodes
return_type = self._idl_type_factory.reference_type(
interface_identifier)
return Constructor.IR(
identifier=None,
arguments=arguments,
return_type=return_type,
extended_attributes=extended_attributes,
component=self._component,
debug_info=self._build_debug_info(node))
def build_operation(node):
child_nodes = list(node.GetChildren())
arguments = self._take_arguments(child_nodes)
return_type = self._take_type(child_nodes)
extended_attributes = self._take_extended_attributes(
child_nodes) or fallback_extended_attributes
assert not child_nodes
return Operation.IR(
identifier=Identifier(node.GetName()),
arguments=arguments,
return_type=return_type,
is_static=bool(node.GetProperty('STATIC')),
is_getter=bool(node.GetProperty('GETTER')),
is_setter=bool(node.GetProperty('SETTER')),
is_deleter=bool(node.GetProperty('DELETER')),
extended_attributes=extended_attributes,
component=self._component,
debug_info=self._build_debug_info(node))
build_functions = {
'Attribute': build_attribute,
'Const': build_constant,
'Constructor': build_constructor,
'Operation': build_operation,
}
return build_functions[node.GetClass()](node)
def _build_legacy_factory_function(self, node):
assert node.GetClass() == 'Interface'
legacy_factory_functions = []
for child in node.GetChildren():
if child.GetClass() == 'ExtAttributes':
interface_ext_attrs = child.GetChildren()
break
else:
return legacy_factory_functions
for ext_attr in interface_ext_attrs:
if ext_attr.GetName() != 'LegacyFactoryFunction':
continue
call_node = ext_attr.GetChildren()[0]
assert call_node.GetClass() == 'Call'
child_nodes = list(call_node.GetChildren())
arguments = self._take_arguments(child_nodes)
return_type = self._idl_type_factory.reference_type(
Identifier(node.GetName()))
assert not child_nodes
legacy_factory_functions.append(
Constructor.IR(identifier=Identifier(call_node.GetName()),
arguments=arguments,
return_type=return_type,
component=self._component,
debug_info=self._build_debug_info(node)))
return legacy_factory_functions
def _build_dictionary(self, node):
child_nodes = list(node.GetChildren())
inherited = self._take_inheritance(child_nodes)
extended_attributes = self._take_extended_attributes(child_nodes)
own_members = list(map(self._build_dictionary_member, child_nodes))
return Dictionary.IR(
identifier=Identifier(node.GetName()),
is_partial=bool(node.GetProperty('PARTIAL')),
inherited=inherited,
own_members=own_members,
extended_attributes=extended_attributes,
component=self._component,
debug_info=self._build_debug_info(node))
def _build_dictionary_member(self, node):
assert node.GetClass() == 'Key'
child_nodes = list(node.GetChildren())
is_required = bool(node.GetProperty('REQUIRED'))
idl_type = self._take_type(child_nodes, is_optional=(not is_required))
default_value = self._take_default_value(child_nodes)
extended_attributes = self._take_extended_attributes(child_nodes)
assert not child_nodes
return DictionaryMember.IR(
identifier=Identifier(node.GetName()),
idl_type=idl_type,
default_value=default_value,
extended_attributes=extended_attributes,
component=self._component,
debug_info=self._build_debug_info(node))
def _build_callback_interface(self, node):
assert node.GetProperty('CALLBACK')
child_nodes = list(node.GetChildren())
extended_attributes = self._take_extended_attributes(child_nodes)
members = list(map(self._build_interface_member, child_nodes))
constants = []
operations = []
for member in members:
if isinstance(member, Constant.IR):
constants.append(member)
elif isinstance(member, Operation.IR):
operations.append(member)
else:
assert False
return CallbackInterface.IR(
identifier=Identifier(node.GetName()),
constants=constants,
operations=operations,
extended_attributes=extended_attributes,
component=self._component,
debug_info=self._build_debug_info(node))
def _build_callback_function(self, node):
child_nodes = list(node.GetChildren())
arguments = self._take_arguments(child_nodes)
return_type = self._take_type(child_nodes)
extended_attributes = self._take_extended_attributes(child_nodes)
assert not child_nodes
return CallbackFunction.IR(
identifier=Identifier(node.GetName()),
arguments=arguments,
return_type=return_type,
extended_attributes=extended_attributes,
component=self._component,
debug_info=self._build_debug_info(node))
def _build_enumeration(self, node):
child_nodes = list(node.GetChildren())
extended_attributes = self._take_extended_attributes(child_nodes)
assert all(child.GetClass() == 'EnumItem' for child in child_nodes)
values = [child.GetName() for child in child_nodes]
return Enumeration.IR(
identifier=Identifier(node.GetName()),
values=values,
extended_attributes=extended_attributes,
component=self._component,
debug_info=self._build_debug_info(node))
def _build_typedef(self, node):
child_nodes = list(node.GetChildren())
idl_type = self._take_type(child_nodes)
assert not child_nodes
return Typedef.IR(
identifier=Identifier(node.GetName()),
idl_type=idl_type,
component=self._component,
debug_info=self._build_debug_info(node))
def _build_includes(self, node):
return Includes.IR(
interface_identifier=Identifier(node.GetName()),
mixin_identifier=Identifier(node.GetProperty('REFERENCE')),
component=self._component,
debug_info=self._build_debug_info(node))
# Helper functions sorted alphabetically
def _build_arguments(self, node):
def build_argument(node, index):
assert node.GetClass() == 'Argument'
child_nodes = list(node.GetChildren())
is_optional = bool(node.GetProperty('OPTIONAL'))
is_variadic = bool(self._take_is_variadic_argument(child_nodes))
# The parser may place extended attributes on arguments, but they
# should be applied to types.
extended_attributes = self._take_extended_attributes(child_nodes)
idl_type = self._take_type(
child_nodes,
is_optional=is_optional,
is_variadic=is_variadic,
extended_attributes=extended_attributes)
default_value = self._take_default_value(child_nodes)
assert not child_nodes
return Argument.IR(
identifier=Identifier(node.GetName()),
index=index,
idl_type=idl_type,
default_value=default_value)
assert node.GetClass() == 'Arguments'
return [
build_argument(node, i)
for i, node in enumerate(node.GetChildren())
]
def _build_async_iterable(self, node, interface_identifier):
assert node.GetClass() == 'AsyncIterable'
assert isinstance(interface_identifier, Identifier)
child_nodes = list(node.GetChildren())
arguments = self._take_arguments(child_nodes)
extended_attributes = self._take_extended_attributes(child_nodes)
types = list(map(self._build_type, child_nodes))
assert len(types) == 1 or len(types) == 2
iter_ops = self._create_async_iterable_operations(
node, interface_identifier, arguments, extended_attributes)
if len(types) == 1: # value iterator
key_type, value_type = (None, types[0])
iter_ops[Identifier('values')].is_async_iterator = True
operations = [iter_ops[Identifier('values')]]
else: # pair iterator
key_type, value_type = types
iter_ops[Identifier('entries')].is_async_iterator = True
operations = list(iter_ops.values())
return AsyncIterable.IR(key_type=key_type,
value_type=value_type,
operations=operations,
arguments=arguments,
extended_attributes=extended_attributes,
debug_info=self._build_debug_info(node))
def _build_constant_value(self, node):
assert node.GetClass() == 'Value'
return self._build_literal_constant(node)
def _build_debug_info(self, node):
return DebugInfo(
location=Location(
filepath=node.GetProperty('FILENAME'),
line_number=node.GetProperty('LINENO'),
position=node.GetProperty('POSITION')))
def _build_default_value(self, node):
assert node.GetClass() == 'Default'
return self._build_literal_constant(node)
def _build_extended_attributes(self, node):
def build_extended_attribute(node):
key = node.GetName()
values = node.GetProperty('VALUE', default=None)
arguments = None
name = None
child_nodes = node.GetChildren()
if child_nodes:
assert len(child_nodes) == 1
child = child_nodes[0]
if child.GetClass() == 'Arguments':
arguments = list(
map(build_extattr_argument, child.GetChildren()))
elif child.GetClass() == 'Call':
assert len(child.GetChildren()) == 1
grand_child = child.GetChildren()[0]
assert grand_child.GetClass() == 'Arguments'
# ExtendedAttribute is not designed to represent an
# operation, especially a complicated argument list.
# Discard the arguments.
arguments = ()
name = child.GetName()
else:
assert False
return ExtendedAttribute(
key=key, values=values, arguments=arguments, name=name)
def build_extattr_argument(node):
assert node.GetClass() == 'Argument'
child_nodes = node.GetChildren()
assert len(child_nodes) == 1
assert child_nodes[0].GetClass() == 'Type'
type_node = child_nodes[0]
type_children = type_node.GetChildren()
assert len(type_children) == 1
return (type_children[0].GetName(), node.GetName())
assert node.GetClass() == 'ExtAttributes'
return ExtendedAttributes(
list(
filter(None, map(build_extended_attribute,
node.GetChildren()))))
def _build_inheritance(self, node):
assert node.GetClass() == 'Inherit'
return self._create_ref_to_idl_def(
Identifier(node.GetName()), self._build_debug_info(node))
def _build_is_variadic_argument(self, node):
# idl_parser produces the following tree to indicate an argument is
# variadic.
# Arguments
# := [Argument, Argument, ...]
# Argument
# := [Type, Argument(Name='...')] # Argument inside Argument
assert node.GetClass() == 'Argument'
assert node.GetName() == '...'
return True
def _build_iterable(self, node, interface_identifier):
assert node.GetClass() == 'Iterable'
assert isinstance(interface_identifier, Identifier)
child_nodes = list(node.GetChildren())
extended_attributes = self._take_extended_attributes(child_nodes)
types = list(map(self._build_type, child_nodes))
assert len(types) == 1 or len(types) == 2
if len(types) == 1: # value iterator
key_type, value_type = (None, types[0])
operations = None
else: # pair iterator
key_type, value_type = types
iter_ops = self._create_iterable_operations(
node, interface_identifier, extended_attributes)
iter_ops[Identifier('entries')].is_iterator = True
operations = list(iter_ops.values())
return Iterable.IR(key_type=key_type,
value_type=value_type,
operations=operations,
extended_attributes=extended_attributes,
debug_info=self._build_debug_info(node))
def _build_literal_constant(self, node):
assert not node.GetChildren()
type_token = node.GetProperty('TYPE')
value_token = node.GetProperty('VALUE')
debug_info = self._build_debug_info(node)
factory = self._idl_type_factory
if type_token == 'NULL':
idl_type = factory.nullable_type(
inner_type=factory.simple_type(
name='any', debug_info=debug_info),
debug_info=debug_info)
assert value_token == 'NULL'
value = None
literal = 'null'
elif type_token == 'boolean':
idl_type = factory.simple_type(
name='boolean', debug_info=debug_info)
assert isinstance(value_token, bool)
value = value_token
literal = 'true' if value else 'false'
elif type_token == 'integer':
idl_type = factory.simple_type(name='long', debug_info=debug_info)
assert isinstance(value_token, str)
value = int(value_token, base=0)
literal = value_token
elif type_token == 'float':
idl_type = factory.simple_type(
name='double', debug_info=debug_info)
assert isinstance(value_token, str)
value = float(value_token)
literal = value_token
elif type_token == 'DOMString':
idl_type = factory.simple_type(
name='DOMString', debug_info=debug_info)
assert isinstance(value_token, str)
value = value_token
literal = '"{}"'.format(value)
elif type_token == 'sequence':
idl_type = factory.sequence_type(
element_type=factory.simple_type(
name='any', debug_info=debug_info),
debug_info=debug_info)
assert value_token == '[]'
value = []
literal = '[]'
elif type_token == 'dictionary':
idl_type = factory.simple_type(
name='object', debug_info=debug_info)
assert value_token == '{}'
value = dict()
literal = '{}'
else:
assert False, "Unknown literal type: {}".format(type_token)
return LiteralConstant(idl_type=idl_type, value=value, literal=literal)
def _build_maplike(self, node, interface_identifier):
assert node.GetClass() == 'Maplike'
assert isinstance(interface_identifier, Identifier)
types = list(map(self._build_type, node.GetChildren()))
assert len(types) == 2
key_type, value_type = types
is_readonly = bool(node.GetProperty('READONLY'))
attributes = [
self._create_attribute(
Identifier('size'),
'unsigned long',
is_readonly=True,
node=node),
]
iter_map = self._create_iterable_operations(node, interface_identifier)
iter_map[Identifier('entries')].is_iterator = True
iter_ops = list(iter_map.values())
read_ops = [
self._create_operation(Identifier('get'),
arguments=self._create_arguments([
(Identifier('key'), key_type),
]),
return_type='any',
extended_attributes={
'CallWith': 'ScriptState',
'RaisesException': None,
'ImplementedAs': 'getForBinding',
},
node=node),
self._create_operation(Identifier('has'),
arguments=self._create_arguments([
(Identifier('key'), key_type),
]),
return_type='boolean',
extended_attributes={
'CallWith': 'ScriptState',
'RaisesException': None,
'ImplementedAs': 'hasForBinding',
},
node=node),
]
write_ops = [
self._create_operation(
Identifier('set'),
arguments=self._create_arguments([
(Identifier('key'), key_type),
(Identifier('value'), value_type),
]),
return_type=interface_identifier,
extended_attributes={
'CallWith': 'ScriptState',
'RaisesException': None,
'ImplementedAs': 'setForBinding',
},
node=node),
self._create_operation(
Identifier('delete'),
arguments=self._create_arguments([
(Identifier('key'), key_type),
]),
return_type='boolean',
extended_attributes={
'CallWith': 'ScriptState',
'RaisesException': None,
'ImplementedAs': 'deleteForBinding',
},
node=node),
self._create_operation(
Identifier('clear'),
extended_attributes={
'CallWith': 'ScriptState',
'RaisesException': None,
'ImplementedAs': 'clearForBinding',
},
node=node),
]
for op in write_ops:
op.is_optionally_defined = True
if is_readonly:
operations = iter_ops + read_ops
else:
operations = iter_ops + read_ops + write_ops
return Maplike.IR(
key_type=key_type,
value_type=value_type,
is_readonly=is_readonly,
attributes=attributes,
operations=operations,
debug_info=self._build_debug_info(node))
def _build_setlike(self, node, interface_identifier):
assert node.GetClass() == 'Setlike'
assert isinstance(interface_identifier, Identifier)
types = list(map(self._build_type, node.GetChildren()))
assert len(types) == 1
value_type = types[0]
is_readonly = bool(node.GetProperty('READONLY'))
attributes = [
self._create_attribute(
Identifier('size'),
'unsigned long',
is_readonly=True,
node=node),
]
iter_map = self._create_iterable_operations(node, interface_identifier)
iter_map[Identifier('values')].is_iterator = True
iter_ops = list(iter_map.values())
read_ops = [
self._create_operation(
Identifier('has'),
arguments=self._create_arguments([
(Identifier('value'), value_type),
]),
return_type='boolean',
extended_attributes={
'CallWith': 'ScriptState',
'RaisesException': None,
'ImplementedAs': 'hasForBinding',
},
node=node),
]
write_ops = [
self._create_operation(
Identifier('add'),
arguments=self._create_arguments([
(Identifier('value'), value_type),
]),
return_type=interface_identifier,
extended_attributes={
'CallWith': 'ScriptState',
'RaisesException': None,
'ImplementedAs': 'addForBinding',
},
node=node),
self._create_operation(
Identifier('delete'),
arguments=self._create_arguments([
(Identifier('value'), value_type),
]),
return_type='boolean',
extended_attributes={
'CallWith': 'ScriptState',
'RaisesException': None,
'ImplementedAs': 'deleteForBinding',
},
node=node),
self._create_operation(
Identifier('clear'),
extended_attributes={
'CallWith': 'ScriptState',
'RaisesException': None,
'ImplementedAs': 'clearForBinding',
},
node=node),
]
for op in write_ops:
op.is_optionally_defined = True
if is_readonly:
operations = iter_ops + read_ops
else:
operations = iter_ops + read_ops + write_ops
return Setlike.IR(
value_type=value_type,
is_readonly=is_readonly,
attributes=attributes,
operations=operations,
debug_info=self._build_debug_info(node))
def _build_stringifier(self, node):
# There are three forms of stringifier declaration;
# a. [ExtAttrs] stringifier;
# b. [ExtAttrs] stringifier DOMString foo();
# c. [ExtAttrs] stringifier attribute DOMString bar;
# and we apply [ExtAttrs] to an operation in cases a and b, or to an
# attribute in case c.
assert node.GetClass() == 'Stringifier'
child_nodes = node.GetChildren()
extended_attributes = self._take_extended_attributes(child_nodes)
assert len(child_nodes) <= 1
member = None
if len(child_nodes) == 1:
member = self._build_interface_member(
child_nodes[0],
fallback_extended_attributes=extended_attributes)
extended_attributes = None
operation = member if isinstance(member, Operation.IR) else None
attribute = member if isinstance(member, Attribute.IR) else None
if operation is None:
return_type = self._idl_type_factory.simple_type(
name='DOMString', debug_info=self._build_debug_info(node))
operation = Operation.IR(
identifier=Identifier(''),
arguments=[],
return_type=return_type,
extended_attributes=extended_attributes,
component=self._component,
debug_info=self._build_debug_info(node))
operation.is_stringifier = True
if attribute:
operation.stringifier_attribute = attribute.identifier
return (operation, attribute)
else:
return (operation, )
def _build_type(self,
node,
is_optional=False,
is_variadic=False,
extended_attributes=None):
assert node.GetClass() == 'Type'
assert not (is_optional and is_variadic)
idl_type = self._build_type_internal(
node.GetChildren(),
is_optional=is_optional,
extended_attributes=extended_attributes)
if node.GetProperty('NULLABLE'):
idl_type = self._idl_type_factory.nullable_type(
idl_type,
is_optional=is_optional,
debug_info=self._build_debug_info(node))
if is_variadic:
idl_type = self._idl_type_factory.variadic_type(
idl_type, debug_info=self._build_debug_info(node))
return idl_type
def _build_type_internal(self,
nodes,
is_optional=False,
extended_attributes=None):
"""
Args:
nodes: The child nodes of a 'Type' node.
"""
def build_frozen_array_type(node, extended_attributes):
assert len(node.GetChildren()) == 1
return self._idl_type_factory.frozen_array_type(
element_type=self._build_type(node.GetChildren()[0]),
is_optional=is_optional,
extended_attributes=extended_attributes,
debug_info=self._build_debug_info(node))
def build_observable_array_type(node, extended_attributes):
assert len(node.GetChildren()) == 1
return self._idl_type_factory.observable_array_type(
element_type=self._build_type(node.GetChildren()[0]),
is_optional=is_optional,
extended_attributes=extended_attributes,
debug_info=self._build_debug_info(node))
def build_promise_type(node, extended_attributes):
assert len(node.GetChildren()) == 1
return self._idl_type_factory.promise_type(
result_type=self._build_type(node.GetChildren()[0]),
is_optional=is_optional,
extended_attributes=extended_attributes,
debug_info=self._build_debug_info(node))
def build_union_type(node, extended_attributes):
return self._idl_type_factory.union_type(
member_types=list(map(self._build_type, node.GetChildren())),
is_optional=is_optional,
extended_attributes=extended_attributes,
debug_info=self._build_debug_info(node))
def build_record_type(node, extended_attributes):
key_node, value_node = node.GetChildren()
return self._idl_type_factory.record_type(
# idl_parser doesn't produce a 'Type' node for the key type,
# hence we need to skip one level.
key_type=self._build_type_internal([key_node]),
value_type=self._build_type(value_node),
is_optional=is_optional,
extended_attributes=extended_attributes,
debug_info=self._build_debug_info(node))
def build_reference_type(node, extended_attributes):
return self._idl_type_factory.reference_type(
Identifier(node.GetName()),
is_optional=is_optional,
extended_attributes=extended_attributes,
debug_info=self._build_debug_info(node))
def build_sequence_type(node, extended_attributes):
return self._idl_type_factory.sequence_type(
element_type=self._build_type(node.GetChildren()[0]),
is_optional=is_optional,
extended_attributes=extended_attributes,
debug_info=self._build_debug_info(node))
def build_simple_type(node, extended_attributes):
name = node.GetName()
if name is None:
assert node.GetClass() in ('Any', 'Undefined')
name = node.GetClass().lower()
if node.GetProperty('UNRESTRICTED'):
name = 'unrestricted {}'.format(name)
return self._idl_type_factory.simple_type(
name=name,
is_optional=is_optional,
extended_attributes=extended_attributes,
debug_info=self._build_debug_info(node))
type_nodes = list(nodes)
ext_attrs1 = extended_attributes
ext_attrs2 = self._take_extended_attributes(type_nodes)
if ext_attrs1 and ext_attrs2:
extended_attributes = ExtendedAttributes(
list(ext_attrs1) + list(ext_attrs2))
else:
extended_attributes = ext_attrs1 or ext_attrs2
assert len(type_nodes) == 1
body_node = type_nodes[0]
buffer_source_types = set([
'ArrayBuffer',
'ArrayBufferView', # Blink-specific ArrayBufferView definition
'DataView',
'Int8Array',
'Int16Array',
'Int32Array',
'BigInt64Array',
'Uint8Array',
'Uint16Array',
'Uint32Array',
'BigUint64Array',
'Uint8ClampedArray',
'Float32Array',
'Float64Array',
])
if body_node.GetName() in buffer_source_types:
return build_simple_type(
body_node, extended_attributes=extended_attributes)
build_functions = {
'Any': build_simple_type,
'FrozenArray': build_frozen_array_type,
'ObservableArray': build_observable_array_type,
'PrimitiveType': build_simple_type,
'Promise': build_promise_type,
'Record': build_record_type,
'Sequence': build_sequence_type,
'StringType': build_simple_type,
'Typeref': build_reference_type,
'Undefined': build_simple_type,
'UnionType': build_union_type,
}
return build_functions[body_node.GetClass()](
body_node, extended_attributes=extended_attributes)
def _create_arguments(self, args):
"""
Constructs a list of new instances of Argument.
Args:
args: A list of argument parameters. Each argument parameter is
a list of argument identifier, type name in str, and optional
default value in str.
"""
assert isinstance(args, (list, tuple))
arguments = []
index = 0
for arg in args:
assert isinstance(arg, (list, tuple))
assert len(arg) == 2 or len(arg) == 3
identifier = arg[0]
if isinstance(arg[1], str):
idl_type = self._create_type(
arg[1], is_optional=(len(arg) == 3))
else:
idl_type = arg[1]
default_value = None
if len(arg) == 3:
default_value = self._create_literal_constant(arg[2])
arguments.append(
Argument.IR(
identifier,
index=index,
idl_type=idl_type,
default_value=default_value))
index += 1
return arguments
def _create_async_iterable_operations(self, node, interface_identifier,
arguments, extended_attributes):
"""
Constructs a set of async iterable operations.
https://webidl.spec.whatwg.org/#define-the-asynchronous-iteration-methods
"""
def make_ext_attrs(key_values):
return ExtendedAttributes(
list(extended_attributes or []) +
list(self._create_extended_attributes(key_values)))
return {
Identifier('entries'):
self._create_operation(
Identifier('entries'),
arguments=make_copy(arguments),
return_type=AsyncIterator.identifier_for(interface_identifier),
extended_attributes=make_ext_attrs({
'CallWith':
'ScriptState',
'RaisesException':
None,
'ImplementedAs':
'entriesForBinding',
}),
node=node),
Identifier('keys'):
self._create_operation(
Identifier('keys'),
arguments=make_copy(arguments),
return_type=AsyncIterator.identifier_for(interface_identifier),
extended_attributes=make_ext_attrs({
'CallWith':
'ScriptState',
'RaisesException':
None,
'ImplementedAs':
'keysForBinding',
}),
node=node),
Identifier('values'):
self._create_operation(
Identifier('values'),
arguments=make_copy(arguments),
return_type=AsyncIterator.identifier_for(interface_identifier),
extended_attributes=make_ext_attrs({
'CallWith':
'ScriptState',
'RaisesException':
None,
'ImplementedAs':
'valuesForBinding',
}),
node=node),
}
def _create_attribute(self,
identifier,
idl_type,
is_readonly=False,
extended_attributes=None,
node=None):
"""Constructs a new Attribute.IR from simple parameters."""
if isinstance(idl_type, str):
idl_type = self._create_type(idl_type)
if isinstance(extended_attributes, dict):
extended_attributes = self._create_extended_attributes(
extended_attributes)
debug_info = self._build_debug_info(node) if node else None
return Attribute.IR(
identifier,
idl_type=idl_type,
is_readonly=is_readonly,
extended_attributes=extended_attributes,
component=self._component,
debug_info=debug_info)
def _create_extended_attributes(self, key_values):
"""
Constructs a new ExtendedAttributes from a dict of key and values.
"""
assert isinstance(key_values, dict)
return ExtendedAttributes([
ExtendedAttribute(key=key, values=values)
for key, values in key_values.items()
])
def _create_iterable_operations(self,
node,
interface_identifier,
extended_attributes=None):
"""
Constructs a set of iterable operations.
https://webidl.spec.whatwg.org/#define-the-iteration-methods
"""
def make_ext_attrs(key_values):
return ExtendedAttributes(
list(extended_attributes or []) +
list(self._create_extended_attributes(key_values)))
return {
Identifier('forEach'):
self._create_operation(Identifier('forEach'),
arguments=self._create_arguments([
(Identifier('callback'),
Identifier('ForEachIteratorCallback')),
(Identifier('thisArg'), 'any', 'null'),
]),
extended_attributes=make_ext_attrs({
'CallWith':
('ScriptState', 'ThisValue'),
'RaisesException':
None,
'ImplementedAs':
'forEachForBinding',
}),
node=node),
Identifier('entries'):
self._create_operation(
Identifier('entries'),
return_type=SyncIterator.identifier_for(interface_identifier),
extended_attributes=make_ext_attrs({
'CallWith':
'ScriptState',
'RaisesException':
None,
'ImplementedAs':
'entriesForBinding',
}),
node=node),
Identifier('keys'):
self._create_operation(
Identifier('keys'),
return_type=SyncIterator.identifier_for(interface_identifier),
extended_attributes=make_ext_attrs({
'CallWith':
'ScriptState',
'RaisesException':
None,
'ImplementedAs':
'keysForBinding',
}),
node=node),
Identifier('values'):
self._create_operation(
Identifier('values'),
return_type=SyncIterator.identifier_for(interface_identifier),
extended_attributes=make_ext_attrs({
'CallWith':
'ScriptState',
'RaisesException':
None,
'ImplementedAs':
'valuesForBinding',
}),
node=node),
}
def _create_literal_constant(self, token):
factory = self._idl_type_factory
if token == 'null':
return LiteralConstant(
idl_type=factory.nullable_type(
inner_type=factory.simple_type(name='any')),
value=None,
literal='null')
else:
assert False
def _create_operation(self,
identifier,
arguments=None,
return_type=None,
extended_attributes=None,
node=None):
"""Constructs a new Operation.IR from simple parameters."""
if not return_type:
return_type = self._create_type('undefined')
elif isinstance(return_type, str):
return_type = self._create_type(return_type)
if isinstance(extended_attributes, dict):
extended_attributes = self._create_extended_attributes(
extended_attributes)
debug_info = self._build_debug_info(node) if node else None
return Operation.IR(
identifier,
arguments=(arguments or []),
return_type=return_type,
extended_attributes=extended_attributes,
component=self._component,
debug_info=debug_info)
def _create_type(self, keyword_or_identifier, **kwargs):
"""Constructs a new IdlType from a type keyword or identifier."""
name = keyword_or_identifier
if isinstance(name, Identifier):
return self._idl_type_factory.reference_type(name, **kwargs)
elif isinstance(name, str):
return self._idl_type_factory.simple_type(name, **kwargs)
else:
assert False
def _take_and_build(self, node_class, build_func, node_list, **kwargs):
"""
Takes a node of |node_class| from |node_list| if any, and then builds
and returns an IR. The processed node is removed from |node_list|.
Returns None if not found.
"""
for node in node_list:
if node.GetClass() == node_class:
node_list.remove(node)
return build_func(node, **kwargs)
return None
def _take_arguments(self, node_list):
return self._take_and_build('Arguments', self._build_arguments,
node_list)
def _take_async_iterable(self, node_list, **kwargs):
return self._take_and_build('AsyncIterable',
self._build_async_iterable, node_list,
**kwargs)
def _take_constant_value(self, node_list):
return self._take_and_build('Value', self._build_constant_value,
node_list)
def _take_default_value(self, node_list):
return self._take_and_build('Default', self._build_default_value,
node_list)
def _take_extended_attributes(self, node_list):
return self._take_and_build('ExtAttributes',
self._build_extended_attributes, node_list)
def _take_inheritance(self, node_list):
return self._take_and_build('Inherit', self._build_inheritance,
node_list)
def _take_is_variadic_argument(self, node_list):
return self._take_and_build(
'Argument', self._build_is_variadic_argument, node_list)
def _take_iterable(self, node_list, **kwargs):
return self._take_and_build('Iterable', self._build_iterable,
node_list, **kwargs)
def _take_maplike(self, node_list, **kwargs):
return self._take_and_build('Maplike', self._build_maplike, node_list,
**kwargs)
def _take_setlike(self, node_list, **kwargs):
return self._take_and_build('Setlike', self._build_setlike, node_list,
**kwargs)
def _take_stringifier(self, node_list):
return self._take_and_build('Stringifier', self._build_stringifier,
node_list)
def _take_type(self, node_list, **kwargs):
return self._take_and_build('Type', self._build_type, node_list,
**kwargs)