Commit 99e66d37 authored by Yuki Shiino's avatar Yuki Shiino Committed by Commit Bot

IDL compiler: Support multi-layered extended attributes

Adds IdlType.effective_annotations, which supports multi-layered
extended attributes.  For example, given the following IDL
fragments,

  typedef [ExtAttr1] long NewLong;
  void f([ExtAttr2] NewLong arg);

arg.idl_type.effective_annotations returns [ExtAttr1, ExtAttr2]
while arg.idl_type.extended_attributes returns [ExtAttr1] only.

c.f. https://crbug.com/1058762

Bug: 839389
Change-Id: I49cdd82dfa0b8c3468610ffce6458eec963d9148
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2096408Reviewed-by: default avatarHitoshi Yoshida <peria@chromium.org>
Commit-Queue: Yuki Shiino <yukishiino@chromium.org>
Cr-Commit-Position: refs/heads/master@{#749556}
parent 573a3a4c
...@@ -597,9 +597,10 @@ class IdlCompiler(object): ...@@ -597,9 +597,10 @@ class IdlCompiler(object):
idl_def = StubUserDefinedType(ref.identifier) idl_def = StubUserDefinedType(ref.identifier)
if isinstance(idl_def, UserDefinedType): if isinstance(idl_def, UserDefinedType):
idl_type = self._idl_type_factory.definition_type( idl_type = self._idl_type_factory.definition_type(
user_defined_type=idl_def) reference_type=ref, user_defined_type=idl_def)
elif isinstance(idl_def, Typedef): elif isinstance(idl_def, Typedef):
idl_type = self._idl_type_factory.typedef_type(typedef=idl_def) idl_type = self._idl_type_factory.typedef_type(
reference_type=ref, typedef=idl_def)
else: else:
assert False assert False
ref.set_target_object(idl_type) ref.set_target_object(idl_type)
......
...@@ -48,13 +48,8 @@ class IdlTypeFactory(object): ...@@ -48,13 +48,8 @@ class IdlTypeFactory(object):
def __init__(self): def __init__(self):
self._idl_types = [] self._idl_types = []
# Factory to initialize instances of ReferenceType. # Factory to initialize instances of ReferenceType.
attrs_to_be_proxied = (
set(RefById.get_all_attributes(IdlType)).difference(
# attributes not to be proxied
set(('debug_info', 'extended_attributes', 'is_optional',
'optionality'))))
self._ref_by_id_factory = RefByIdFactory( self._ref_by_id_factory = RefByIdFactory(
target_attrs_with_priority=attrs_to_be_proxied) target_attrs_with_priority=RefById.get_all_attributes(IdlType))
# |_is_frozen| is initially False and you can create new instances of # |_is_frozen| is initially False and you can create new instances of
# IdlType. The first invocation of |for_each| freezes the factory and # IdlType. The first invocation of |for_each| freezes the factory and
# you can no longer create a new instance of IdlType. # you can no longer create a new instance of IdlType.
...@@ -202,6 +197,12 @@ class IdlType(WithExtendedAttributes, WithDebugInfo): ...@@ -202,6 +197,12 @@ class IdlType(WithExtendedAttributes, WithDebugInfo):
https://heycam.github.io/webidl/#dfn-type-name https://heycam.github.io/webidl/#dfn-type-name
Note that a type name is not necessarily unique. Note that a type name is not necessarily unique.
""" """
return '{}{}'.format(
self.type_name_without_extended_attributes, ''.join(
sorted(self.effective_annotations.keys())))
@property
def type_name_without_extended_attributes(self):
raise exceptions.NotImplementedError() raise exceptions.NotImplementedError()
@property @property
...@@ -268,6 +269,23 @@ class IdlType(WithExtendedAttributes, WithDebugInfo): ...@@ -268,6 +269,23 @@ class IdlType(WithExtendedAttributes, WithDebugInfo):
return self._unwrap(switches) return self._unwrap(switches)
@property
def effective_annotations(self):
"""
Returns the extended attributes associated with this IDL type.
https://heycam.github.io/webidl/#idl-type-extended-attribute-associated-with
For example, given the following IDL fragments,
typedef [ExtAttr1] long NewLong;
void f([ExtAttr2] NewLong arg);
arg.idl_type.extended_attributes returns [ExtAttr2],
arg.idl_type.unwrap().extended_attributes returns [ExtAttr1], and
arg.idl_type.effective_annotations returns [ExtAttr1, ExtAttr2].
"""
return self.extended_attributes
@property @property
def does_include_nullable_type(self): def does_include_nullable_type(self):
""" """
...@@ -437,11 +455,6 @@ class IdlType(WithExtendedAttributes, WithDebugInfo): ...@@ -437,11 +455,6 @@ class IdlType(WithExtendedAttributes, WithDebugInfo):
""" """
return False return False
@property
def is_annotated(self):
"""Returns True if this is annotated."""
return bool(self.extended_attributes)
@property @property
def is_optional(self): def is_optional(self):
""" """
...@@ -547,11 +560,6 @@ class IdlType(WithExtendedAttributes, WithDebugInfo): ...@@ -547,11 +560,6 @@ class IdlType(WithExtendedAttributes, WithDebugInfo):
return '{}{}{}'.format(optional_form, ext_attr_form, return '{}{}{}'.format(optional_form, ext_attr_form,
syntactic_form_inner) syntactic_form_inner)
def _format_type_name(self, type_name_inner):
"""Helper function to implement |type_name|."""
return '{}{}'.format(type_name_inner, ''.join(
sorted(self.extended_attributes.keys())))
def _unwrap(self, switches): def _unwrap(self, switches):
return self return self
...@@ -615,10 +623,9 @@ class SimpleType(IdlType): ...@@ -615,10 +623,9 @@ class SimpleType(IdlType):
return self._format_syntactic_form(self._name) return self._format_syntactic_form(self._name)
@property @property
def type_name(self): def type_name_without_extended_attributes(self):
name = 'String' if self._name == 'DOMString' else self._name name = 'String' if self._name == 'DOMString' else self._name
return self._format_type_name( return NameStyleConverter(name).to_upper_camel_case()
NameStyleConverter(name).to_upper_camel_case())
@property @property
def keyword_typename(self): def keyword_typename(self):
...@@ -731,14 +738,14 @@ class DefinitionType(IdlType, WithIdentifier): ...@@ -731,14 +738,14 @@ class DefinitionType(IdlType, WithIdentifier):
TypedefType and UnionType respectively. TypedefType and UnionType respectively.
""" """
def __init__(self, def __init__(self, reference_type, user_defined_type, pass_key=None):
user_defined_type, assert isinstance(reference_type, ReferenceType)
debug_info=None,
pass_key=None):
assert isinstance(user_defined_type, UserDefinedType) assert isinstance(user_defined_type, UserDefinedType)
IdlType.__init__( IdlType.__init__(
self, self,
debug_info=debug_info, is_optional=reference_type.is_optional,
extended_attributes=reference_type.extended_attributes,
debug_info=reference_type.debug_info,
pass_key=pass_key) pass_key=pass_key)
WithIdentifier.__init__(self, user_defined_type.identifier) WithIdentifier.__init__(self, user_defined_type.identifier)
self._type_definition_object = user_defined_type self._type_definition_object = user_defined_type
...@@ -752,14 +759,10 @@ class DefinitionType(IdlType, WithIdentifier): ...@@ -752,14 +759,10 @@ class DefinitionType(IdlType, WithIdentifier):
@property @property
def syntactic_form(self): def syntactic_form(self):
assert not self.extended_attributes return self._format_syntactic_form(self.identifier)
assert not self.is_optional
return self.identifier
@property @property
def type_name(self): def type_name_without_extended_attributes(self):
assert not self.extended_attributes
assert not self.is_optional
return self.identifier return self.identifier
@property @property
...@@ -802,14 +805,14 @@ class TypedefType(IdlType, WithIdentifier): ...@@ -802,14 +805,14 @@ class TypedefType(IdlType, WithIdentifier):
can track down the typedef'ed type to |original_type|. can track down the typedef'ed type to |original_type|.
""" """
def __init__(self, def __init__(self, reference_type, typedef, pass_key=None):
typedef, assert isinstance(reference_type, ReferenceType)
debug_info=None,
pass_key=None):
assert isinstance(typedef, Typedef) assert isinstance(typedef, Typedef)
IdlType.__init__( IdlType.__init__(
self, self,
debug_info=debug_info, is_optional=reference_type.is_optional,
extended_attributes=reference_type.extended_attributes,
debug_info=reference_type.debug_info,
pass_key=pass_key) pass_key=pass_key)
WithIdentifier.__init__(self, typedef.identifier) WithIdentifier.__init__(self, typedef.identifier)
self._typedef = typedef self._typedef = typedef
...@@ -823,15 +826,11 @@ class TypedefType(IdlType, WithIdentifier): ...@@ -823,15 +826,11 @@ class TypedefType(IdlType, WithIdentifier):
@property @property
def syntactic_form(self): def syntactic_form(self):
assert not self.extended_attributes return self._format_syntactic_form(self.identifier)
assert not self.is_optional
return self.identifier
@property @property
def type_name(self): def type_name_without_extended_attributes(self):
assert not self.extended_attributes return self.original_type.type_name_without_extended_attributes
assert not self.is_optional
return self.original_type.type_name
def apply_to_all_composing_elements(self, callback): def apply_to_all_composing_elements(self, callback):
try: try:
...@@ -840,6 +839,16 @@ class TypedefType(IdlType, WithIdentifier): ...@@ -840,6 +839,16 @@ class TypedefType(IdlType, WithIdentifier):
return return
self.original_type.apply_to_all_composing_elements(callback) self.original_type.apply_to_all_composing_elements(callback)
@property
def effective_annotations(self):
original_annotations = self.original_type.effective_annotations
if not self.extended_attributes:
return original_annotations
if not original_annotations:
return self.extended_attributes
return ExtendedAttributes(
list(self.extended_attributes) + list(original_annotations))
@property @property
def does_include_nullable_type(self): def does_include_nullable_type(self):
return self.original_type.does_include_nullable_type return self.original_type.does_include_nullable_type
...@@ -920,9 +929,8 @@ class SequenceType(_ArrayLikeType): ...@@ -920,9 +929,8 @@ class SequenceType(_ArrayLikeType):
self.element_type.syntactic_form)) self.element_type.syntactic_form))
@property @property
def type_name(self): def type_name_without_extended_attributes(self):
return self._format_type_name('{}Sequence'.format( return '{}Sequence'.format(self.element_type.type_name)
self.element_type.type_name))
@property @property
def is_sequence(self): def is_sequence(self):
...@@ -952,9 +960,8 @@ class FrozenArrayType(_ArrayLikeType): ...@@ -952,9 +960,8 @@ class FrozenArrayType(_ArrayLikeType):
self.element_type.syntactic_form)) self.element_type.syntactic_form))
@property @property
def type_name(self): def type_name_without_extended_attributes(self):
return self._format_type_name('{}Array'.format( return '{}Array'.format(self.element_type.type_name)
self.element_type.type_name))
@property @property
def is_frozen_array(self): def is_frozen_array(self):
...@@ -981,10 +988,11 @@ class VariadicType(_ArrayLikeType): ...@@ -981,10 +988,11 @@ class VariadicType(_ArrayLikeType):
return '{}...'.format(self.element_type.syntactic_form) return '{}...'.format(self.element_type.syntactic_form)
@property @property
def type_name(self): def type_name_without_extended_attributes(self):
# Blink-specific expansion of type name # Blink-specific expansion of type name
# The type name of a variadic type is the concatenation of the type # The type name of a variadic type is the concatenation of the type
# name of the element type and the string "Variadic". # name of the element type and the string "Variadic".
assert not self.extended_attributes
return '{}Variadic'.format(self.element_type.type_name) return '{}Variadic'.format(self.element_type.type_name)
@property @property
...@@ -1031,9 +1039,9 @@ class RecordType(IdlType): ...@@ -1031,9 +1039,9 @@ class RecordType(IdlType):
self.key_type.syntactic_form, self.value_type.syntactic_form)) self.key_type.syntactic_form, self.value_type.syntactic_form))
@property @property
def type_name(self): def type_name_without_extended_attributes(self):
return self._format_type_name('{}{}Record'.format( return '{}{}Record'.format(self.key_type.type_name,
self.key_type.type_name, self.value_type.type_name)) self.value_type.type_name)
def apply_to_all_composing_elements(self, callback): def apply_to_all_composing_elements(self, callback):
try: try:
...@@ -1087,9 +1095,8 @@ class PromiseType(IdlType): ...@@ -1087,9 +1095,8 @@ class PromiseType(IdlType):
self.result_type.syntactic_form)) self.result_type.syntactic_form))
@property @property
def type_name(self): def type_name_without_extended_attributes(self):
return self._format_type_name('{}Promise'.format( return '{}Promise'.format(self.result_type.type_name)
self.result_type.type_name))
def apply_to_all_composing_elements(self, callback): def apply_to_all_composing_elements(self, callback):
try: try:
...@@ -1154,9 +1161,8 @@ class UnionType(IdlType): ...@@ -1154,9 +1161,8 @@ class UnionType(IdlType):
[member.syntactic_form for member in self.member_types]))) [member.syntactic_form for member in self.member_types])))
@property @property
def type_name(self): def type_name_without_extended_attributes(self):
return self._format_type_name('Or'.join( return 'Or'.join([member.type_name for member in self.member_types])
[member.type_name for member in self.member_types]))
def apply_to_all_composing_elements(self, callback): def apply_to_all_composing_elements(self, callback):
try: try:
...@@ -1243,18 +1249,16 @@ class NullableType(IdlType): ...@@ -1243,18 +1249,16 @@ class NullableType(IdlType):
return '{}?'.format(self.inner_type.syntactic_form) return '{}?'.format(self.inner_type.syntactic_form)
@property @property
def type_name(self): def type_name_without_extended_attributes(self):
assert not self.extended_attributes
# https://heycam.github.io/webidl/#idl-annotated-types # https://heycam.github.io/webidl/#idl-annotated-types
# Web IDL seems not supposing a case of [X] ([Y] Type)?, i.e. something # Web IDL seems not supposing a case of [X] ([Y] Type)?, i.e. something
# like [X] nullable<[Y] Type>, which should turn into "TypeYOrNullX". # like [X] nullable<[Y] Type>, which should turn into "TypeYOrNullX".
# #
# In case of '[Clamp] long?', it's interpreted as '([Clamp] long)?' but # In case of '[Clamp] long?', it's interpreted as '([Clamp] long)?' but
# the type name must be "LongOrNullClamp" instead of "LongClampOrNull". # the type name must be "LongOrNullClamp" instead of "LongClampOrNull".
name = self.inner_type.type_name assert not self.extended_attributes
ext_attrs = ''.join(sorted(self.inner_type.extended_attributes.keys())) return '{}OrNull'.format(
sep_index = len(name) - len(ext_attrs) self.inner_type.type_name_without_extended_attributes)
return '{}OrNull{}'.format(name[0:sep_index], name[sep_index:])
def apply_to_all_composing_elements(self, callback): def apply_to_all_composing_elements(self, callback):
try: try:
...@@ -1263,6 +1267,11 @@ class NullableType(IdlType): ...@@ -1263,6 +1267,11 @@ class NullableType(IdlType):
return return
self.inner_type.apply_to_all_composing_elements(callback) self.inner_type.apply_to_all_composing_elements(callback)
@property
def effective_annotations(self):
assert not self.extended_attributes
return self.inner_type.effective_annotations
@property @property
def does_include_nullable_type(self): def does_include_nullable_type(self):
return True return True
......
...@@ -54,7 +54,7 @@ class IdlTypesTest(unittest.TestCase): ...@@ -54,7 +54,7 @@ class IdlTypesTest(unittest.TestCase):
ext_attrs = ExtendedAttributes([ExtendedAttribute('Clamp')]) ext_attrs = ExtendedAttributes([ExtendedAttribute('Clamp')])
annotated_type = factory.simple_type( annotated_type = factory.simple_type(
'short', extended_attributes=ext_attrs) 'short', extended_attributes=ext_attrs)
self.assertTrue(annotated_type.is_annotated) self.assertTrue(annotated_type.extended_attributes)
self.assertTrue(annotated_type.is_numeric) self.assertTrue(annotated_type.is_numeric)
optional_type = factory.simple_type('DOMString', is_optional=True) optional_type = factory.simple_type('DOMString', is_optional=True)
...@@ -63,7 +63,7 @@ class IdlTypesTest(unittest.TestCase): ...@@ -63,7 +63,7 @@ class IdlTypesTest(unittest.TestCase):
annotated_optional = factory.simple_type( annotated_optional = factory.simple_type(
'long', is_optional=True, extended_attributes=ext_attrs) 'long', is_optional=True, extended_attributes=ext_attrs)
self.assertTrue(annotated_optional.is_annotated) self.assertTrue(annotated_optional.extended_attributes)
self.assertTrue(annotated_optional.is_optional) self.assertTrue(annotated_optional.is_optional)
self.assertTrue(annotated_optional.is_numeric) self.assertTrue(annotated_optional.is_numeric)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment