# 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 .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
class Union(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, Union.IR)
return (self.token != other.token
and self._member_set.issuperset(other._member_set))
def __init__(self, ir):
assert isinstance(ir, Union.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, Union)
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
@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