blob: bc2aab7fef7b7138cd82c7463f9ef4890e3861ad [file] [log] [blame]
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from .code_generator_info import CodeGeneratorInfoMutable
from .composition_parts import Identifier
from .composition_parts import WithCodeGeneratorInfo
from .composition_parts import WithComponent
from .composition_parts import WithDebugInfo
from .composition_parts import WithIdentifier
from .idl_type import IdlType
from .typedef import Typedef
# NewUnion is a tentative name and will be renamed to Union.
class NewUnion(WithIdentifier, WithCodeGeneratorInfo, WithComponent,
WithDebugInfo):
"""
Union class makes a group of union types with the same flattened member
types and the same result whether it includes a nullable type or not.
For example, the following union types will be grouped into one Union
instance.
(A? or B or C), (A or B? or C), ((A or B) or C?), (A or (B or C?)), ...
All these unions have the same set of flattened member types (A, B, C) and
include a nullable type.
However, all following union types will be grouped into separate Union
instances.
(A or B), ([X] A or B), ([Y] A or B)
IdlType(A), IdlType([X] A), and IdlType([Y] A) are all distinguished from
each other as they behave differently. Bindings code generators are
expected to define an implementation class for each Union instance.
"""
_null_token = "Null"
@classmethod
def unique_token(cls, union_type):
"""
Returns an unique token per a set of union types that are considered
as the same group.
"""
assert union_type.is_union
token_pieces = []
def collect_token_pieces(idl_type):
idl_type = idl_type.unwrap()
if idl_type.is_union:
for member_type in idl_type.member_types:
collect_token_pieces(member_type)
else:
# Typename is not guaranteed to be unique, however it's
# unlikely that a conflict happens.
token_pieces.append(
idl_type.type_name_with_extended_attribute_key_values)
collect_token_pieces(union_type)
token_pieces.sort()
if union_type.does_include_nullable_type:
token_pieces.append(cls._null_token)
return tuple(token_pieces)
class IR(object):
# Note that Union.IR is, despite of its name, very different from other
# IDL definitions' IR classes. This class is not meant to be stored in
# IRMap nor managed with 'compilation phase'.
def __init__(self, token, union_types):
assert all(union_type.is_union for union_type in union_types)
self.token = token
self._member_set = set(token)
self.union_types = union_types
self.typedefs = []
self.sub_union_irs = []
self.public_object = None
def __lt__(self, other):
if len(self.token) == len(other.token):
return self.token < other.token
else:
return len(self.token) < len(other.token)
def contains(self, other):
assert isinstance(other, NewUnion.IR)
return (self.token != other.token
and self._member_set.issuperset(other._member_set))
def __init__(self, ir):
assert isinstance(ir, NewUnion.IR)
assert ir.public_object is None
identifier = Identifier('Union_{}'.format('_'.join(ir.token)))
union_type = ir.union_types[0]
flattened_member_types = union_type.flattened_member_types
does_include_nullable_type = union_type.does_include_nullable_type
does_include_nullable_or_dict = (
union_type.does_include_nullable_or_dict)
typedef_members = set()
union_members = set()
for union_type in ir.union_types:
assert union_type.flattened_member_types == flattened_member_types
assert (union_type.does_include_nullable_type ==
does_include_nullable_type)
for member_type in union_type.member_types:
if member_type.is_typedef:
typedef_members.add(member_type.typedef_object)
for sub_union_ir in ir.sub_union_irs:
assert isinstance(sub_union_ir.public_object, NewUnion)
typedef_members.update(sub_union_ir.typedefs)
union_members.add(sub_union_ir.public_object)
components = set()
for_testing = [False]
def collect_primary_component(idl_type):
type_definition_object = idl_type.type_definition_object
if type_definition_object and type_definition_object.components:
components.add(type_definition_object.components[0])
if (type_definition_object and
type_definition_object.code_generator_info.for_testing):
for_testing[0] = True
for idl_type in flattened_member_types:
idl_type.apply_to_all_composing_elements(collect_primary_component)
code_generator_info = CodeGeneratorInfoMutable()
code_generator_info.set_for_testing(for_testing[0])
WithIdentifier.__init__(self, identifier)
WithCodeGeneratorInfo.__init__(self,
code_generator_info,
readonly=True)
WithComponent.__init__(self, sorted(components), readonly=True)
WithDebugInfo.__init__(self)
sort_key_typename = lambda idl_type: (
idl_type.type_name_with_extended_attribute_key_values)
sort_key_identifier = lambda x: x.identifier
self._idl_types = tuple(ir.union_types)
self._member_tokens = ir.token
self._flattened_member_types = tuple(
sorted(flattened_member_types, key=sort_key_typename))
self._does_include_nullable_type = does_include_nullable_type
self._does_include_nullable_or_dict = does_include_nullable_or_dict
self._typedef_members = tuple(
sorted(typedef_members, key=sort_key_identifier))
self._union_members = tuple(
sorted(union_members, key=sort_key_identifier))
self._aliasing_typedefs = tuple(
sorted(ir.typedefs, key=sort_key_identifier))
ir.public_object = self
for idl_type in self._idl_types:
idl_type.set_new_union_definition_object(self)
@property
def idl_types(self):
"""Returns a list of IdlTypes which this object represents."""
return self._idl_types
@property
def member_tokens(self):
"""Returns a list of unique names of union member types."""
return self._member_tokens
@property
def flattened_member_types(self):
"""
Returns the same list of flattened member types as
IdlType.flattened_member_types.
"""
return self._flattened_member_types
@property
def does_include_nullable_type(self):
"""
Returns True if any of member type is nullable or a member union
includes a nullable type.
"""
return self._does_include_nullable_type
@property
def does_include_nullable_or_dict(self):
"""
Returns True if this type includes a nullable type or a dictionary
type.
"""
return self._does_include_nullable_or_dict
@property
def typedef_members(self):
"""
Returns a list of typedef types which are direct members of union types
which this object represents.
Given the following union types,
(AT or B), (A or BT) where typedef A AT, and typedef B BT
typedef_members returns a list of IdlType(AT) and IdlType(BT).
"""
return self._typedef_members
@property
def union_members(self):
"""
Returns a list of union types which are direct members of union types
which this object represents.
Given the following union types,
((A or B) or C), (A or (B or C))
union_members returns a list of IdlType(A or B) and IdlType(B or C).
"""
return self._union_members
@property
def aliasing_typedefs(self):
"""
Returns a list of typedef types which are aliases to union types which
this object represents.
Given the following typedef definitions,
typedef ((A or B) or C) T1;
typedef (A or (B or C)) T2;
aliasing_typedefs returns a list of IdlType(T1) and IdlType(T2).
"""
return self._aliasing_typedefs
class BackwardCompatibleUnion(WithIdentifier, WithCodeGeneratorInfo,
WithComponent, WithDebugInfo):
"""
Union class makes a group of union types with the same flattened member
types and the same result whether it includes a nullable type or not.
For example, the following union types will be grouped into one Union
instance.
(A? or B or C), (A or B? or C), ((A or B) or C?), (A or (B or C?)), ...
All these unions have the same set of flattened member types (A, B, C) and
include a nullable type.
However, all following union types will be grouped into separate Union
instances.
(A or B), ([X] A or B), ([Y] A or B)
IdlType(A), IdlType([X] A), and IdlType([Y] A) are all distinguished from
each other as they behave differently. Bindings code generators are
expected to define an implementation class for each Union instance.
"""
def __init__(self, union_types, typedef_backrefs):
"""
Args:
union_types: Union types of which this object consists. All types
in |union_types| must have the same flattened_member_types and
the same value of does_include_nullable_type.
typedef_backrefs: Typedef instances whose typedef'ed type is this
union.
"""
assert isinstance(union_types, (list, tuple))
assert len(union_types) > 0
assert all(
isinstance(union_type, IdlType) and union_type.is_union
for union_type in union_types)
assert isinstance(typedef_backrefs, (list, tuple))
assert all(
isinstance(typedef, Typedef) for typedef in typedef_backrefs)
flattened_members = union_types[0].flattened_member_types
does_include_nullable_type = union_types[0].does_include_nullable_type
nullable_members = set()
typedef_members = set()
union_members = set()
for union_type in union_types:
assert union_type.flattened_member_types == flattened_members
assert (union_type.does_include_nullable_type ==
does_include_nullable_type)
union_type.set_union_definition_object(self)
for direct_member in union_type.member_types:
if direct_member.is_nullable:
nullable_members.add(direct_member)
if direct_member.is_typedef:
typedef_members.add(direct_member)
if direct_member.is_union:
union_members.add(direct_member)
sort_key = lambda x: x.syntactic_form
components = set()
def collect_primary_component(idl_type):
type_definition_object = idl_type.type_definition_object
if type_definition_object and type_definition_object.components:
components.add(type_definition_object.components[0])
for idl_type in flattened_members:
idl_type.apply_to_all_composing_elements(collect_primary_component)
# Make this union type look defined in 'modules' if the union type is
# used in 'modules' in order to keep the backward compatibility with
# the old bindings generator.
is_defined_in_core = False
is_defined_in_modules = False
for idl_type in union_types:
filepath = idl_type.debug_info.location.filepath
if filepath.startswith('third_party/blink/renderer/core/'):
is_defined_in_core = True
if filepath.startswith('third_party/blink/renderer/modules/'):
is_defined_in_modules = True
if not is_defined_in_core and is_defined_in_modules:
from .composition_parts import Component
components.add(Component('modules'))
# TODO(peria, yukishiino): Produce unique union names. Trying to
# produce the names compatible to the old bindings generator for the
# time being.
#
# type_names = sorted(
# [idl_type.type_name for idl_type in flattened_members])
def backward_compatible_member_name(idl_type):
name = idl_type.unwrap().type_name
if name == 'StringTreatNullAs':
return 'StringTreatNullAsEmptyString'
else:
return name
identifier = Identifier('Or'.join([
backward_compatible_member_name(idl_type)
for idl_type in union_types[0].member_types
]))
WithIdentifier.__init__(self, identifier)
WithCodeGeneratorInfo.__init__(self, readonly=True)
WithComponent.__init__(self, sorted(components), readonly=True)
WithDebugInfo.__init__(self)
# Sort improves reproducibility.
self._flattened_members = tuple(
sorted(flattened_members, key=sort_key))
self._does_include_nullable_type = does_include_nullable_type
self._nullable_members = tuple(sorted(nullable_members, key=sort_key))
self._typedef_members = tuple(sorted(typedef_members, key=sort_key))
self._union_members = tuple(sorted(union_members, key=sort_key))
self._typedef_backrefs = tuple(typedef_backrefs)
@property
def flattened_member_types(self):
"""
Returns the same list of flattened member types as
IdlType.flattened_member_types.
"""
return self._flattened_members
@property
def does_include_nullable_type(self):
"""
Returns True if any of member type is nullable or a member union
includes a nullable type.
"""
return self._does_include_nullable_type
@property
def nullable_member_types(self):
"""
Returns a list of nullable types which are direct members of union types
of which this object consists.
Given the following unions,
(A? or B or C), (A or B? or C), (A or B or CN) where typedef C? CN;
nullable_member_types returns a list of IdlType(A?) and IdlType(B?).
"""
return self._nullable_members
@property
def typedef_member_types(self):
"""
Returns a list of typedef types which are direct members of union types
of which this object consists.
Given the following unions,
(AT or B), (A or BT) where typedef A AT, and typedef B BT;
typedef_member_types returns a list of IdlType(AT) and IdlType(BT).
"""
return self._typedef_members
@property
def union_member_types(self):
"""
Returns a list of union types which are direct members of union types of
which this object consists.
Given the following unions,
((A or B) or C), (A or (B or C))
union_member_types returns a list of IdlType(A or B) and
IdlType(B or C).
"""
return self._union_members
@property
def aliasing_typedefs(self):
"""
Returns a list of typedefs which are aliases to this union type.
"""
return self._typedef_backrefs
Union = BackwardCompatibleUnion