summaryrefslogtreecommitdiff
path: root/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py
diff options
context:
space:
mode:
Diffstat (limited to 'utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py')
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py1635
1 files changed, 1635 insertions, 0 deletions
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py
new file mode 100644
index 00000000..8547ff64
--- /dev/null
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py
@@ -0,0 +1,1635 @@
+# Copyright 2013 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.
+
+# This module's classes provide an interface to mojo modules. Modules are
+# collections of interfaces and structs to be used by mojo ipc clients and
+# servers.
+#
+# A simple interface would be created this way:
+# module = mojom.generate.module.Module('Foo')
+# interface = module.AddInterface('Bar')
+# method = interface.AddMethod('Tat', 0)
+# method.AddParameter('baz', 0, mojom.INT32)
+
+import pickle
+
+# We use our own version of __repr__ when displaying the AST, as the
+# AST currently doesn't capture which nodes are reference (e.g. to
+# types) and which nodes are definitions. This allows us to e.g. print
+# the definition of a struct when it's defined inside a module, but
+# only print its name when it's referenced in e.g. a method parameter.
+def Repr(obj, as_ref=True):
+ """A version of __repr__ that can distinguish references.
+
+ Sometimes we like to print an object's full representation
+ (e.g. with its fields) and sometimes we just want to reference an
+ object that was printed in full elsewhere. This function allows us
+ to make that distinction.
+
+ Args:
+ obj: The object whose string representation we compute.
+ as_ref: If True, use the short reference representation.
+
+ Returns:
+ A str representation of |obj|.
+ """
+ if hasattr(obj, 'Repr'):
+ return obj.Repr(as_ref=as_ref)
+ # Since we cannot implement Repr for existing container types, we
+ # handle them here.
+ elif isinstance(obj, list):
+ if not obj:
+ return '[]'
+ else:
+ return ('[\n%s\n]' % (',\n'.join(
+ ' %s' % Repr(elem, as_ref).replace('\n', '\n ')
+ for elem in obj)))
+ elif isinstance(obj, dict):
+ if not obj:
+ return '{}'
+ else:
+ return ('{\n%s\n}' % (',\n'.join(
+ ' %s: %s' % (Repr(key, as_ref).replace('\n', '\n '),
+ Repr(val, as_ref).replace('\n', '\n '))
+ for key, val in obj.items())))
+ else:
+ return repr(obj)
+
+
+def GenericRepr(obj, names):
+ """Compute generic Repr for |obj| based on the attributes in |names|.
+
+ Args:
+ obj: The object to compute a Repr for.
+ names: A dict from attribute names to include, to booleans
+ specifying whether those attributes should be shown as
+ references or not.
+
+ Returns:
+ A str representation of |obj|.
+ """
+
+ def ReprIndent(name, as_ref):
+ return ' %s=%s' % (name, Repr(getattr(obj, name), as_ref).replace(
+ '\n', '\n '))
+
+ return '%s(\n%s\n)' % (obj.__class__.__name__, ',\n'.join(
+ ReprIndent(name, as_ref) for (name, as_ref) in names.items()))
+
+
+class Kind(object):
+ """Kind represents a type (e.g. int8, string).
+
+ Attributes:
+ spec: A string uniquely identifying the type. May be None.
+ module: {Module} The defining module. Set to None for built-in types.
+ parent_kind: The enclosing type. For example, an enum defined
+ inside an interface has that interface as its parent. May be None.
+ """
+
+ def __init__(self, spec=None, module=None):
+ self.spec = spec
+ self.module = module
+ self.parent_kind = None
+
+ def Repr(self, as_ref=True):
+ # pylint: disable=unused-argument
+ return '<%s spec=%r>' % (self.__class__.__name__, self.spec)
+
+ def __repr__(self):
+ # Gives us a decent __repr__ for all kinds.
+ return self.Repr()
+
+ def __eq__(self, rhs):
+ # pylint: disable=unidiomatic-typecheck
+ return (type(self) == type(rhs)
+ and (self.spec, self.parent_kind) == (rhs.spec, rhs.parent_kind))
+
+ def __hash__(self):
+ # TODO(crbug.com/1060471): Remove this and other __hash__ methods on Kind
+ # and its subclasses. This is to support existing generator code which uses
+ # some primitive Kinds as dict keys. The default hash (object identity)
+ # breaks these dicts when a pickled Module instance is unpickled and used
+ # during a subsequent run of the parser.
+ return hash((self.spec, self.parent_kind))
+
+
+class ReferenceKind(Kind):
+ """ReferenceKind represents pointer and handle types.
+
+ A type is nullable if null (for pointer types) or invalid handle (for handle
+ types) is a legal value for the type.
+
+ Attributes:
+ is_nullable: True if the type is nullable.
+ """
+
+ def __init__(self, spec=None, is_nullable=False, module=None):
+ assert spec is None or is_nullable == spec.startswith('?')
+ Kind.__init__(self, spec, module)
+ self.is_nullable = is_nullable
+ self.shared_definition = {}
+
+ def Repr(self, as_ref=True):
+ return '<%s spec=%r is_nullable=%r>' % (self.__class__.__name__, self.spec,
+ self.is_nullable)
+
+ def MakeNullableKind(self):
+ assert not self.is_nullable
+
+ if self == STRING:
+ return NULLABLE_STRING
+ if self == HANDLE:
+ return NULLABLE_HANDLE
+ if self == DCPIPE:
+ return NULLABLE_DCPIPE
+ if self == DPPIPE:
+ return NULLABLE_DPPIPE
+ if self == MSGPIPE:
+ return NULLABLE_MSGPIPE
+ if self == SHAREDBUFFER:
+ return NULLABLE_SHAREDBUFFER
+ if self == PLATFORMHANDLE:
+ return NULLABLE_PLATFORMHANDLE
+
+ nullable_kind = type(self)()
+ nullable_kind.shared_definition = self.shared_definition
+ if self.spec is not None:
+ nullable_kind.spec = '?' + self.spec
+ nullable_kind.is_nullable = True
+ nullable_kind.parent_kind = self.parent_kind
+ nullable_kind.module = self.module
+
+ return nullable_kind
+
+ @classmethod
+ def AddSharedProperty(cls, name):
+ """Adds a property |name| to |cls|, which accesses the corresponding item in
+ |shared_definition|.
+
+ The reason of adding such indirection is to enable sharing definition
+ between a reference kind and its nullable variation. For example:
+ a = Struct('test_struct_1')
+ b = a.MakeNullableKind()
+ a.name = 'test_struct_2'
+ print(b.name) # Outputs 'test_struct_2'.
+ """
+
+ def Get(self):
+ try:
+ return self.shared_definition[name]
+ except KeyError: # Must raise AttributeError if property doesn't exist.
+ raise AttributeError
+
+ def Set(self, value):
+ self.shared_definition[name] = value
+
+ setattr(cls, name, property(Get, Set))
+
+ def __eq__(self, rhs):
+ return (isinstance(rhs, ReferenceKind)
+ and super(ReferenceKind, self).__eq__(rhs)
+ and self.is_nullable == rhs.is_nullable)
+
+ def __hash__(self):
+ return hash((super(ReferenceKind, self).__hash__(), self.is_nullable))
+
+
+# Initialize the set of primitive types. These can be accessed by clients.
+BOOL = Kind('b')
+INT8 = Kind('i8')
+INT16 = Kind('i16')
+INT32 = Kind('i32')
+INT64 = Kind('i64')
+UINT8 = Kind('u8')
+UINT16 = Kind('u16')
+UINT32 = Kind('u32')
+UINT64 = Kind('u64')
+FLOAT = Kind('f')
+DOUBLE = Kind('d')
+STRING = ReferenceKind('s')
+HANDLE = ReferenceKind('h')
+DCPIPE = ReferenceKind('h:d:c')
+DPPIPE = ReferenceKind('h:d:p')
+MSGPIPE = ReferenceKind('h:m')
+SHAREDBUFFER = ReferenceKind('h:s')
+PLATFORMHANDLE = ReferenceKind('h:p')
+NULLABLE_STRING = ReferenceKind('?s', True)
+NULLABLE_HANDLE = ReferenceKind('?h', True)
+NULLABLE_DCPIPE = ReferenceKind('?h:d:c', True)
+NULLABLE_DPPIPE = ReferenceKind('?h:d:p', True)
+NULLABLE_MSGPIPE = ReferenceKind('?h:m', True)
+NULLABLE_SHAREDBUFFER = ReferenceKind('?h:s', True)
+NULLABLE_PLATFORMHANDLE = ReferenceKind('?h:p', True)
+
+# Collection of all Primitive types
+PRIMITIVES = (
+ BOOL,
+ INT8,
+ INT16,
+ INT32,
+ INT64,
+ UINT8,
+ UINT16,
+ UINT32,
+ UINT64,
+ FLOAT,
+ DOUBLE,
+ STRING,
+ HANDLE,
+ DCPIPE,
+ DPPIPE,
+ MSGPIPE,
+ SHAREDBUFFER,
+ PLATFORMHANDLE,
+ NULLABLE_STRING,
+ NULLABLE_HANDLE,
+ NULLABLE_DCPIPE,
+ NULLABLE_DPPIPE,
+ NULLABLE_MSGPIPE,
+ NULLABLE_SHAREDBUFFER,
+ NULLABLE_PLATFORMHANDLE,
+)
+
+ATTRIBUTE_MIN_VERSION = 'MinVersion'
+ATTRIBUTE_EXTENSIBLE = 'Extensible'
+ATTRIBUTE_STABLE = 'Stable'
+ATTRIBUTE_SYNC = 'Sync'
+
+
+class NamedValue(object):
+ def __init__(self, module, parent_kind, mojom_name):
+ self.module = module
+ self.parent_kind = parent_kind
+ self.mojom_name = mojom_name
+
+ def GetSpec(self):
+ return (self.module.GetNamespacePrefix() +
+ (self.parent_kind and
+ (self.parent_kind.mojom_name + '.') or "") + self.mojom_name)
+
+ def __eq__(self, rhs):
+ return (isinstance(rhs, NamedValue)
+ and (self.parent_kind, self.mojom_name) == (rhs.parent_kind,
+ rhs.mojom_name))
+
+
+class BuiltinValue(object):
+ def __init__(self, value):
+ self.value = value
+
+ def __eq__(self, rhs):
+ return isinstance(rhs, BuiltinValue) and self.value == rhs.value
+
+
+class ConstantValue(NamedValue):
+ def __init__(self, module, parent_kind, constant):
+ NamedValue.__init__(self, module, parent_kind, constant.mojom_name)
+ self.constant = constant
+
+ @property
+ def name(self):
+ return self.constant.name
+
+
+class EnumValue(NamedValue):
+ def __init__(self, module, enum, field):
+ NamedValue.__init__(self, module, enum.parent_kind, field.mojom_name)
+ self.field = field
+ self.enum = enum
+
+ def GetSpec(self):
+ return (self.module.GetNamespacePrefix() +
+ (self.parent_kind and (self.parent_kind.mojom_name + '.') or "") +
+ self.enum.mojom_name + '.' + self.mojom_name)
+
+ @property
+ def name(self):
+ return self.field.name
+
+
+class Constant(object):
+ def __init__(self, mojom_name=None, kind=None, value=None, parent_kind=None):
+ self.mojom_name = mojom_name
+ self.name = None
+ self.kind = kind
+ self.value = value
+ self.parent_kind = parent_kind
+
+ def Stylize(self, stylizer):
+ self.name = stylizer.StylizeConstant(self.mojom_name)
+
+ def __eq__(self, rhs):
+ return (isinstance(rhs, Constant)
+ and (self.mojom_name, self.kind, self.value,
+ self.parent_kind) == (rhs.mojom_name, rhs.kind, rhs.value,
+ rhs.parent_kind))
+
+
+class Field(object):
+ def __init__(self,
+ mojom_name=None,
+ kind=None,
+ ordinal=None,
+ default=None,
+ attributes=None):
+ if self.__class__.__name__ == 'Field':
+ raise Exception()
+ self.mojom_name = mojom_name
+ self.name = None
+ self.kind = kind
+ self.ordinal = ordinal
+ self.default = default
+ self.attributes = attributes
+
+ def Repr(self, as_ref=True):
+ # pylint: disable=unused-argument
+ # Fields are only referenced by objects which define them and thus
+ # they are always displayed as non-references.
+ return GenericRepr(self, {'mojom_name': False, 'kind': True})
+
+ def Stylize(self, stylizer):
+ self.name = stylizer.StylizeField(self.mojom_name)
+
+ @property
+ def min_version(self):
+ return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
+ if self.attributes else None
+
+ def __eq__(self, rhs):
+ return (isinstance(rhs, Field)
+ and (self.mojom_name, self.kind, self.ordinal, self.default,
+ self.attributes) == (rhs.mojom_name, rhs.kind, rhs.ordinal,
+ rhs.default, rhs.attributes))
+
+ def __hash__(self):
+ return hash((self.mojom_name, self.kind, self.ordinal, self.default))
+
+
+class StructField(Field):
+ pass
+
+
+class UnionField(Field):
+ pass
+
+
+def _IsFieldBackwardCompatible(new_field, old_field):
+ if (new_field.min_version or 0) != (old_field.min_version or 0):
+ return False
+
+ if isinstance(new_field.kind, (Enum, Struct, Union)):
+ return new_field.kind.IsBackwardCompatible(old_field.kind)
+
+ return new_field.kind == old_field.kind
+
+
+class Struct(ReferenceKind):
+ """A struct with typed fields.
+
+ Attributes:
+ mojom_name: {str} The name of the struct type as defined in mojom.
+ name: {str} The stylized name.
+ native_only: {bool} Does the struct have a body (i.e. any fields) or is it
+ purely a native struct.
+ custom_serializer: {bool} Should we generate a serializer for the struct or
+ will one be provided by non-generated code.
+ fields: {List[StructField]} The members of the struct.
+ enums: {List[Enum]} The enums defined in the struct scope.
+ constants: {List[Constant]} The constants defined in the struct scope.
+ attributes: {dict} Additional information about the struct, such as
+ if it's a native struct.
+ """
+
+ ReferenceKind.AddSharedProperty('mojom_name')
+ ReferenceKind.AddSharedProperty('name')
+ ReferenceKind.AddSharedProperty('native_only')
+ ReferenceKind.AddSharedProperty('custom_serializer')
+ ReferenceKind.AddSharedProperty('fields')
+ ReferenceKind.AddSharedProperty('enums')
+ ReferenceKind.AddSharedProperty('constants')
+ ReferenceKind.AddSharedProperty('attributes')
+
+ def __init__(self, mojom_name=None, module=None, attributes=None):
+ if mojom_name is not None:
+ spec = 'x:' + mojom_name
+ else:
+ spec = None
+ ReferenceKind.__init__(self, spec, False, module)
+ self.mojom_name = mojom_name
+ self.name = None
+ self.native_only = False
+ self.custom_serializer = False
+ self.fields = []
+ self.enums = []
+ self.constants = []
+ self.attributes = attributes
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s mojom_name=%r module=%s>' % (self.__class__.__name__,
+ self.mojom_name,
+ Repr(self.module, as_ref=True))
+ else:
+ return GenericRepr(self, {
+ 'mojom_name': False,
+ 'fields': False,
+ 'module': True
+ })
+
+ def AddField(self,
+ mojom_name,
+ kind,
+ ordinal=None,
+ default=None,
+ attributes=None):
+ field = StructField(mojom_name, kind, ordinal, default, attributes)
+ self.fields.append(field)
+ return field
+
+ def Stylize(self, stylizer):
+ self.name = stylizer.StylizeStruct(self.mojom_name)
+ for field in self.fields:
+ field.Stylize(stylizer)
+ for enum in self.enums:
+ enum.Stylize(stylizer)
+ for constant in self.constants:
+ constant.Stylize(stylizer)
+
+ def IsBackwardCompatible(self, older_struct):
+ """This struct is backward-compatible with older_struct if and only if all
+ of the following conditions hold:
+ - Any newly added field is tagged with a [MinVersion] attribute specifying
+ a version number greater than all previously used [MinVersion]
+ attributes within the struct.
+ - All fields present in older_struct remain present in the new struct,
+ with the same ordinal position, same optional or non-optional status,
+ same (or backward-compatible) type and where applicable, the same
+ [MinVersion] attribute value.
+ - All [MinVersion] attributes must be non-decreasing in ordinal order.
+ - All reference-typed (string, array, map, struct, or union) fields tagged
+ with a [MinVersion] greater than zero must be optional.
+ """
+
+ def buildOrdinalFieldMap(struct):
+ fields_by_ordinal = {}
+ for field in struct.fields:
+ if field.ordinal in fields_by_ordinal:
+ raise Exception('Multiple fields with ordinal %s in struct %s.' %
+ (field.ordinal, struct.mojom_name))
+ fields_by_ordinal[field.ordinal] = field
+ return fields_by_ordinal
+
+ new_fields = buildOrdinalFieldMap(self)
+ old_fields = buildOrdinalFieldMap(older_struct)
+ if len(new_fields) < len(old_fields):
+ # At least one field was removed, which is not OK.
+ return False
+
+ # If there are N fields, existing ordinal values must exactly cover the
+ # range from 0 to N-1.
+ num_old_ordinals = len(old_fields)
+ max_old_min_version = 0
+ for ordinal in range(num_old_ordinals):
+ new_field = new_fields[ordinal]
+ old_field = old_fields[ordinal]
+ if (old_field.min_version or 0) > max_old_min_version:
+ max_old_min_version = old_field.min_version
+ if not _IsFieldBackwardCompatible(new_field, old_field):
+ # Type or min-version mismatch between old and new versions of the same
+ # ordinal field.
+ return False
+
+ # At this point we know all old fields are intact in the new struct
+ # definition. Now verify that all new fields have a high enough min version
+ # and are appropriately optional where required.
+ num_new_ordinals = len(new_fields)
+ last_min_version = max_old_min_version
+ for ordinal in range(num_old_ordinals, num_new_ordinals):
+ new_field = new_fields[ordinal]
+ min_version = new_field.min_version or 0
+ if min_version <= max_old_min_version:
+ # A new field is being added to an existing version, which is not OK.
+ return False
+ if min_version < last_min_version:
+ # The [MinVersion] of a field cannot be lower than the [MinVersion] of
+ # a field with lower ordinal value.
+ return False
+ if IsReferenceKind(new_field.kind) and not IsNullableKind(new_field.kind):
+ # New fields whose type can be nullable MUST be nullable.
+ return False
+
+ return True
+
+ @property
+ def stable(self):
+ return self.attributes.get(ATTRIBUTE_STABLE, False) \
+ if self.attributes else False
+
+ @property
+ def qualified_name(self):
+ if self.parent_kind:
+ prefix = self.parent_kind.qualified_name + '.'
+ else:
+ prefix = self.module.GetNamespacePrefix()
+ return '%s%s' % (prefix, self.mojom_name)
+
+ def __eq__(self, rhs):
+ return (isinstance(rhs, Struct) and
+ (self.mojom_name, self.native_only, self.fields, self.constants,
+ self.attributes) == (rhs.mojom_name, rhs.native_only, rhs.fields,
+ rhs.constants, rhs.attributes))
+
+ def __hash__(self):
+ return id(self)
+
+
+class Union(ReferenceKind):
+ """A union of several kinds.
+
+ Attributes:
+ mojom_name: {str} The name of the union type as defined in mojom.
+ name: {str} The stylized name.
+ fields: {List[UnionField]} The members of the union.
+ attributes: {dict} Additional information about the union, such as
+ which Java class name to use to represent it in the generated
+ bindings.
+ """
+ ReferenceKind.AddSharedProperty('mojom_name')
+ ReferenceKind.AddSharedProperty('name')
+ ReferenceKind.AddSharedProperty('fields')
+ ReferenceKind.AddSharedProperty('attributes')
+
+ def __init__(self, mojom_name=None, module=None, attributes=None):
+ if mojom_name is not None:
+ spec = 'x:' + mojom_name
+ else:
+ spec = None
+ ReferenceKind.__init__(self, spec, False, module)
+ self.mojom_name = mojom_name
+ self.name = None
+ self.fields = []
+ self.attributes = attributes
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s spec=%r is_nullable=%r fields=%s>' % (
+ self.__class__.__name__, self.spec, self.is_nullable, Repr(
+ self.fields))
+ else:
+ return GenericRepr(self, {'fields': True, 'is_nullable': False})
+
+ def AddField(self, mojom_name, kind, ordinal=None, attributes=None):
+ field = UnionField(mojom_name, kind, ordinal, None, attributes)
+ self.fields.append(field)
+ return field
+
+ def Stylize(self, stylizer):
+ self.name = stylizer.StylizeUnion(self.mojom_name)
+ for field in self.fields:
+ field.Stylize(stylizer)
+
+ def IsBackwardCompatible(self, older_union):
+ """This union is backward-compatible with older_union if and only if all
+ of the following conditions hold:
+ - Any newly added field is tagged with a [MinVersion] attribute specifying
+ a version number greater than all previously used [MinVersion]
+ attributes within the union.
+ - All fields present in older_union remain present in the new union,
+ with the same ordinal value, same optional or non-optional status,
+ same (or backward-compatible) type, and where applicable, the same
+ [MinVersion] attribute value.
+ """
+
+ def buildOrdinalFieldMap(union):
+ fields_by_ordinal = {}
+ for field in union.fields:
+ if field.ordinal in fields_by_ordinal:
+ raise Exception('Multiple fields with ordinal %s in union %s.' %
+ (field.ordinal, union.mojom_name))
+ fields_by_ordinal[field.ordinal] = field
+ return fields_by_ordinal
+
+ new_fields = buildOrdinalFieldMap(self)
+ old_fields = buildOrdinalFieldMap(older_union)
+ if len(new_fields) < len(old_fields):
+ # At least one field was removed, which is not OK.
+ return False
+
+ max_old_min_version = 0
+ for ordinal, old_field in old_fields.items():
+ new_field = new_fields.get(ordinal)
+ if not new_field:
+ # A field was removed, which is not OK.
+ return False
+ if not _IsFieldBackwardCompatible(new_field, old_field):
+ # An field changed its type or MinVersion, which is not OK.
+ return False
+ old_min_version = old_field.min_version or 0
+ if old_min_version > max_old_min_version:
+ max_old_min_version = old_min_version
+
+ new_ordinals = set(new_fields.keys()) - set(old_fields.keys())
+ for ordinal in new_ordinals:
+ if (new_fields[ordinal].min_version or 0) <= max_old_min_version:
+ # New fields must use a MinVersion greater than any old fields.
+ return False
+
+ return True
+
+ @property
+ def stable(self):
+ return self.attributes.get(ATTRIBUTE_STABLE, False) \
+ if self.attributes else False
+
+ @property
+ def qualified_name(self):
+ if self.parent_kind:
+ prefix = self.parent_kind.qualified_name + '.'
+ else:
+ prefix = self.module.GetNamespacePrefix()
+ return '%s%s' % (prefix, self.mojom_name)
+
+ def __eq__(self, rhs):
+ return (isinstance(rhs, Union) and
+ (self.mojom_name, self.fields,
+ self.attributes) == (rhs.mojom_name, rhs.fields, rhs.attributes))
+
+ def __hash__(self):
+ return id(self)
+
+
+class Array(ReferenceKind):
+ """An array.
+
+ Attributes:
+ kind: {Kind} The type of the elements. May be None.
+ length: The number of elements. None if unknown.
+ """
+
+ ReferenceKind.AddSharedProperty('kind')
+ ReferenceKind.AddSharedProperty('length')
+
+ def __init__(self, kind=None, length=None):
+ if kind is not None:
+ if length is not None:
+ spec = 'a%d:%s' % (length, kind.spec)
+ else:
+ spec = 'a:%s' % kind.spec
+
+ ReferenceKind.__init__(self, spec)
+ else:
+ ReferenceKind.__init__(self)
+ self.kind = kind
+ self.length = length
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s spec=%r is_nullable=%r kind=%s length=%r>' % (
+ self.__class__.__name__, self.spec, self.is_nullable, Repr(
+ self.kind), self.length)
+ else:
+ return GenericRepr(self, {
+ 'kind': True,
+ 'length': False,
+ 'is_nullable': False
+ })
+
+ def __eq__(self, rhs):
+ return (isinstance(rhs, Array)
+ and (self.kind, self.length) == (rhs.kind, rhs.length))
+
+ def __hash__(self):
+ return id(self)
+
+
+class Map(ReferenceKind):
+ """A map.
+
+ Attributes:
+ key_kind: {Kind} The type of the keys. May be None.
+ value_kind: {Kind} The type of the elements. May be None.
+ """
+ ReferenceKind.AddSharedProperty('key_kind')
+ ReferenceKind.AddSharedProperty('value_kind')
+
+ def __init__(self, key_kind=None, value_kind=None):
+ if (key_kind is not None and value_kind is not None):
+ ReferenceKind.__init__(
+ self, 'm[' + key_kind.spec + '][' + value_kind.spec + ']')
+ if IsNullableKind(key_kind):
+ raise Exception("Nullable kinds cannot be keys in maps.")
+ if IsAnyHandleKind(key_kind):
+ raise Exception("Handles cannot be keys in maps.")
+ if IsAnyInterfaceKind(key_kind):
+ raise Exception("Interfaces cannot be keys in maps.")
+ if IsArrayKind(key_kind):
+ raise Exception("Arrays cannot be keys in maps.")
+ else:
+ ReferenceKind.__init__(self)
+
+ self.key_kind = key_kind
+ self.value_kind = value_kind
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s spec=%r is_nullable=%r key_kind=%s value_kind=%s>' % (
+ self.__class__.__name__, self.spec, self.is_nullable,
+ Repr(self.key_kind), Repr(self.value_kind))
+ else:
+ return GenericRepr(self, {'key_kind': True, 'value_kind': True})
+
+ def __eq__(self, rhs):
+ return (isinstance(rhs, Map) and
+ (self.key_kind, self.value_kind) == (rhs.key_kind, rhs.value_kind))
+
+ def __hash__(self):
+ return id(self)
+
+
+class PendingRemote(ReferenceKind):
+ ReferenceKind.AddSharedProperty('kind')
+
+ def __init__(self, kind=None):
+ if kind is not None:
+ if not isinstance(kind, Interface):
+ raise Exception(
+ 'pending_remote<T> requires T to be an interface type. Got %r' %
+ kind.spec)
+ ReferenceKind.__init__(self, 'rmt:' + kind.spec)
+ else:
+ ReferenceKind.__init__(self)
+ self.kind = kind
+
+ def __eq__(self, rhs):
+ return isinstance(rhs, PendingRemote) and self.kind == rhs.kind
+
+ def __hash__(self):
+ return id(self)
+
+
+class PendingReceiver(ReferenceKind):
+ ReferenceKind.AddSharedProperty('kind')
+
+ def __init__(self, kind=None):
+ if kind is not None:
+ if not isinstance(kind, Interface):
+ raise Exception(
+ 'pending_receiver<T> requires T to be an interface type. Got %r' %
+ kind.spec)
+ ReferenceKind.__init__(self, 'rcv:' + kind.spec)
+ else:
+ ReferenceKind.__init__(self)
+ self.kind = kind
+
+ def __eq__(self, rhs):
+ return isinstance(rhs, PendingReceiver) and self.kind == rhs.kind
+
+ def __hash__(self):
+ return id(self)
+
+
+class PendingAssociatedRemote(ReferenceKind):
+ ReferenceKind.AddSharedProperty('kind')
+
+ def __init__(self, kind=None):
+ if kind is not None:
+ if not isinstance(kind, Interface):
+ raise Exception(
+ 'pending_associated_remote<T> requires T to be an interface ' +
+ 'type. Got %r' % kind.spec)
+ ReferenceKind.__init__(self, 'rma:' + kind.spec)
+ else:
+ ReferenceKind.__init__(self)
+ self.kind = kind
+
+ def __eq__(self, rhs):
+ return isinstance(rhs, PendingAssociatedRemote) and self.kind == rhs.kind
+
+ def __hash__(self):
+ return id(self)
+
+
+class PendingAssociatedReceiver(ReferenceKind):
+ ReferenceKind.AddSharedProperty('kind')
+
+ def __init__(self, kind=None):
+ if kind is not None:
+ if not isinstance(kind, Interface):
+ raise Exception(
+ 'pending_associated_receiver<T> requires T to be an interface' +
+ 'type. Got %r' % kind.spec)
+ ReferenceKind.__init__(self, 'rca:' + kind.spec)
+ else:
+ ReferenceKind.__init__(self)
+ self.kind = kind
+
+ def __eq__(self, rhs):
+ return isinstance(rhs, PendingAssociatedReceiver) and self.kind == rhs.kind
+
+ def __hash__(self):
+ return id(self)
+
+
+class InterfaceRequest(ReferenceKind):
+ ReferenceKind.AddSharedProperty('kind')
+
+ def __init__(self, kind=None):
+ if kind is not None:
+ if not isinstance(kind, Interface):
+ raise Exception(
+ "Interface request requires %r to be an interface." % kind.spec)
+ ReferenceKind.__init__(self, 'r:' + kind.spec)
+ else:
+ ReferenceKind.__init__(self)
+ self.kind = kind
+
+ def __eq__(self, rhs):
+ return isinstance(rhs, InterfaceRequest) and self.kind == rhs.kind
+
+ def __hash__(self):
+ return id(self)
+
+
+class AssociatedInterfaceRequest(ReferenceKind):
+ ReferenceKind.AddSharedProperty('kind')
+
+ def __init__(self, kind=None):
+ if kind is not None:
+ if not isinstance(kind, InterfaceRequest):
+ raise Exception(
+ "Associated interface request requires %r to be an interface "
+ "request." % kind.spec)
+ assert not kind.is_nullable
+ ReferenceKind.__init__(self, 'asso:' + kind.spec)
+ else:
+ ReferenceKind.__init__(self)
+ self.kind = kind.kind if kind is not None else None
+
+ def __eq__(self, rhs):
+ return isinstance(rhs, AssociatedInterfaceRequest) and self.kind == rhs.kind
+
+ def __hash__(self):
+ return id(self)
+
+
+class Parameter(object):
+ def __init__(self,
+ mojom_name=None,
+ kind=None,
+ ordinal=None,
+ default=None,
+ attributes=None):
+ self.mojom_name = mojom_name
+ self.name = None
+ self.ordinal = ordinal
+ self.kind = kind
+ self.default = default
+ self.attributes = attributes
+
+ def Repr(self, as_ref=True):
+ # pylint: disable=unused-argument
+ return '<%s mojom_name=%r kind=%s>' % (
+ self.__class__.__name__, self.mojom_name, self.kind.Repr(as_ref=True))
+
+ def Stylize(self, stylizer):
+ self.name = stylizer.StylizeParameter(self.mojom_name)
+
+ @property
+ def min_version(self):
+ return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
+ if self.attributes else None
+
+ def __eq__(self, rhs):
+ return (isinstance(rhs, Parameter)
+ and (self.mojom_name, self.ordinal, self.kind, self.default,
+ self.attributes) == (rhs.mojom_name, rhs.ordinal, rhs.kind,
+ rhs.default, rhs.attributes))
+
+
+class Method(object):
+ def __init__(self, interface, mojom_name, ordinal=None, attributes=None):
+ self.interface = interface
+ self.mojom_name = mojom_name
+ self.name = None
+ self.explicit_ordinal = ordinal
+ self.ordinal = ordinal
+ self.parameters = []
+ self.param_struct = None
+ self.response_parameters = None
+ self.response_param_struct = None
+ self.attributes = attributes
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s mojom_name=%r>' % (self.__class__.__name__, self.mojom_name)
+ else:
+ return GenericRepr(self, {
+ 'mojom_name': False,
+ 'parameters': True,
+ 'response_parameters': True
+ })
+
+ def AddParameter(self,
+ mojom_name,
+ kind,
+ ordinal=None,
+ default=None,
+ attributes=None):
+ parameter = Parameter(mojom_name, kind, ordinal, default, attributes)
+ self.parameters.append(parameter)
+ return parameter
+
+ def AddResponseParameter(self,
+ mojom_name,
+ kind,
+ ordinal=None,
+ default=None,
+ attributes=None):
+ if self.response_parameters == None:
+ self.response_parameters = []
+ parameter = Parameter(mojom_name, kind, ordinal, default, attributes)
+ self.response_parameters.append(parameter)
+ return parameter
+
+ def Stylize(self, stylizer):
+ self.name = stylizer.StylizeMethod(self.mojom_name)
+ for param in self.parameters:
+ param.Stylize(stylizer)
+ if self.response_parameters is not None:
+ for param in self.response_parameters:
+ param.Stylize(stylizer)
+
+ if self.param_struct:
+ self.param_struct.Stylize(stylizer)
+ if self.response_param_struct:
+ self.response_param_struct.Stylize(stylizer)
+
+ @property
+ def min_version(self):
+ return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
+ if self.attributes else None
+
+ @property
+ def sync(self):
+ return self.attributes.get(ATTRIBUTE_SYNC) \
+ if self.attributes else None
+
+ def __eq__(self, rhs):
+ return (isinstance(rhs, Method) and
+ (self.mojom_name, self.ordinal, self.parameters,
+ self.response_parameters,
+ self.attributes) == (rhs.mojom_name, rhs.ordinal, rhs.parameters,
+ rhs.response_parameters, rhs.attributes))
+
+
+class Interface(ReferenceKind):
+ ReferenceKind.AddSharedProperty('mojom_name')
+ ReferenceKind.AddSharedProperty('name')
+ ReferenceKind.AddSharedProperty('methods')
+ ReferenceKind.AddSharedProperty('enums')
+ ReferenceKind.AddSharedProperty('constants')
+ ReferenceKind.AddSharedProperty('attributes')
+
+ def __init__(self, mojom_name=None, module=None, attributes=None):
+ if mojom_name is not None:
+ spec = 'x:' + mojom_name
+ else:
+ spec = None
+ ReferenceKind.__init__(self, spec, False, module)
+ self.mojom_name = mojom_name
+ self.name = None
+ self.methods = []
+ self.enums = []
+ self.constants = []
+ self.attributes = attributes
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s mojom_name=%r>' % (self.__class__.__name__, self.mojom_name)
+ else:
+ return GenericRepr(self, {
+ 'mojom_name': False,
+ 'attributes': False,
+ 'methods': False
+ })
+
+ def AddMethod(self, mojom_name, ordinal=None, attributes=None):
+ method = Method(self, mojom_name, ordinal, attributes)
+ self.methods.append(method)
+ return method
+
+ def Stylize(self, stylizer):
+ self.name = stylizer.StylizeInterface(self.mojom_name)
+ for method in self.methods:
+ method.Stylize(stylizer)
+ for enum in self.enums:
+ enum.Stylize(stylizer)
+ for constant in self.constants:
+ constant.Stylize(stylizer)
+
+ def IsBackwardCompatible(self, older_interface):
+ """This interface is backward-compatible with older_interface if and only
+ if all of the following conditions hold:
+ - All defined methods in older_interface (when identified by ordinal) have
+ backward-compatible definitions in this interface. For each method this
+ means:
+ - The parameter list is backward-compatible, according to backward-
+ compatibility rules for structs, where each parameter is essentially
+ a struct field.
+ - If the old method definition does not specify a reply message, the
+ new method definition must not specify a reply message.
+ - If the old method definition specifies a reply message, the new
+ method definition must also specify a reply message with a parameter
+ list that is backward-compatible according to backward-compatibility
+ rules for structs.
+ - All newly introduced methods in this interface have a [MinVersion]
+ attribute specifying a version greater than any method in
+ older_interface.
+ """
+
+ def buildOrdinalMethodMap(interface):
+ methods_by_ordinal = {}
+ for method in interface.methods:
+ if method.ordinal in methods_by_ordinal:
+ raise Exception('Multiple methods with ordinal %s in interface %s.' %
+ (method.ordinal, interface.mojom_name))
+ methods_by_ordinal[method.ordinal] = method
+ return methods_by_ordinal
+
+ new_methods = buildOrdinalMethodMap(self)
+ old_methods = buildOrdinalMethodMap(older_interface)
+ max_old_min_version = 0
+ for ordinal, old_method in old_methods.items():
+ new_method = new_methods.get(ordinal)
+ if not new_method:
+ # A method was removed, which is not OK.
+ return False
+
+ if not new_method.param_struct.IsBackwardCompatible(
+ old_method.param_struct):
+ # The parameter list is not backward-compatible, which is not OK.
+ return False
+
+ if old_method.response_param_struct is None:
+ if new_method.response_param_struct is not None:
+ # A reply was added to a message which didn't have one before, and
+ # this is not OK.
+ return False
+ else:
+ if new_method.response_param_struct is None:
+ # A reply was removed from a message, which is not OK.
+ return False
+ if not new_method.response_param_struct.IsBackwardCompatible(
+ old_method.response_param_struct):
+ # The new message's reply is not backward-compatible with the old
+ # message's reply, which is not OK.
+ return False
+
+ if (old_method.min_version or 0) > max_old_min_version:
+ max_old_min_version = old_method.min_version
+
+ # All the old methods are compatible with their new counterparts. Now verify
+ # that newly added methods are properly versioned.
+ new_ordinals = set(new_methods.keys()) - set(old_methods.keys())
+ for ordinal in new_ordinals:
+ new_method = new_methods[ordinal]
+ if (new_method.min_version or 0) <= max_old_min_version:
+ # A method was added to an existing version, which is not OK.
+ return False
+
+ return True
+
+ @property
+ def stable(self):
+ return self.attributes.get(ATTRIBUTE_STABLE, False) \
+ if self.attributes else False
+
+ @property
+ def qualified_name(self):
+ if self.parent_kind:
+ prefix = self.parent_kind.qualified_name + '.'
+ else:
+ prefix = self.module.GetNamespacePrefix()
+ return '%s%s' % (prefix, self.mojom_name)
+
+ def __eq__(self, rhs):
+ return (isinstance(rhs, Interface)
+ and (self.mojom_name, self.methods, self.enums, self.constants,
+ self.attributes) == (rhs.mojom_name, rhs.methods, rhs.enums,
+ rhs.constants, rhs.attributes))
+
+ def __hash__(self):
+ return id(self)
+
+
+class AssociatedInterface(ReferenceKind):
+ ReferenceKind.AddSharedProperty('kind')
+
+ def __init__(self, kind=None):
+ if kind is not None:
+ if not isinstance(kind, Interface):
+ raise Exception(
+ "Associated interface requires %r to be an interface." % kind.spec)
+ assert not kind.is_nullable
+ ReferenceKind.__init__(self, 'asso:' + kind.spec)
+ else:
+ ReferenceKind.__init__(self)
+ self.kind = kind
+
+ def __eq__(self, rhs):
+ return isinstance(rhs, AssociatedInterface) and self.kind == rhs.kind
+
+ def __hash__(self):
+ return id(self)
+
+
+class EnumField(object):
+ def __init__(self,
+ mojom_name=None,
+ value=None,
+ attributes=None,
+ numeric_value=None):
+ self.mojom_name = mojom_name
+ self.name = None
+ self.value = value
+ self.attributes = attributes
+ self.numeric_value = numeric_value
+
+ def Stylize(self, stylizer):
+ self.name = stylizer.StylizeEnumField(self.mojom_name)
+
+ @property
+ def min_version(self):
+ return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
+ if self.attributes else None
+
+ def __eq__(self, rhs):
+ return (isinstance(rhs, EnumField)
+ and (self.mojom_name, self.value, self.attributes,
+ self.numeric_value) == (rhs.mojom_name, rhs.value,
+ rhs.attributes, rhs.numeric_value))
+
+
+class Enum(Kind):
+ def __init__(self, mojom_name=None, module=None, attributes=None):
+ self.mojom_name = mojom_name
+ self.name = None
+ self.native_only = False
+ if mojom_name is not None:
+ spec = 'x:' + mojom_name
+ else:
+ spec = None
+ Kind.__init__(self, spec, module)
+ self.fields = []
+ self.attributes = attributes
+ self.min_value = None
+ self.max_value = None
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s mojom_name=%r>' % (self.__class__.__name__, self.mojom_name)
+ else:
+ return GenericRepr(self, {'mojom_name': False, 'fields': False})
+
+ def Stylize(self, stylizer):
+ self.name = stylizer.StylizeEnum(self.mojom_name)
+ for field in self.fields:
+ field.Stylize(stylizer)
+
+ @property
+ def extensible(self):
+ return self.attributes.get(ATTRIBUTE_EXTENSIBLE, False) \
+ if self.attributes else False
+
+ @property
+ def stable(self):
+ return self.attributes.get(ATTRIBUTE_STABLE, False) \
+ if self.attributes else False
+
+ @property
+ def qualified_name(self):
+ if self.parent_kind:
+ prefix = self.parent_kind.qualified_name + '.'
+ else:
+ prefix = self.module.GetNamespacePrefix()
+ return '%s%s' % (prefix, self.mojom_name)
+
+ def IsBackwardCompatible(self, older_enum):
+ """This enum is backward-compatible with older_enum if and only if one of
+ the following conditions holds:
+ - Neither enum is [Extensible] and both have the exact same set of valid
+ numeric values. Field names and aliases for the same numeric value do
+ not affect compatibility.
+ - older_enum is [Extensible], and for every version defined by
+ older_enum, this enum has the exact same set of valid numeric values.
+ """
+
+ def buildVersionFieldMap(enum):
+ fields_by_min_version = {}
+ for field in enum.fields:
+ if field.min_version not in fields_by_min_version:
+ fields_by_min_version[field.min_version] = set()
+ fields_by_min_version[field.min_version].add(field.numeric_value)
+ return fields_by_min_version
+
+ old_fields = buildVersionFieldMap(older_enum)
+ new_fields = buildVersionFieldMap(self)
+
+ if new_fields.keys() != old_fields.keys() and not older_enum.extensible:
+ return False
+
+ for min_version, valid_values in old_fields.items():
+ if (min_version not in new_fields
+ or new_fields[min_version] != valid_values):
+ return False
+
+ return True
+
+ def __eq__(self, rhs):
+ return (isinstance(rhs, Enum) and
+ (self.mojom_name, self.native_only, self.fields, self.attributes,
+ self.min_value,
+ self.max_value) == (rhs.mojom_name, rhs.native_only, rhs.fields,
+ rhs.attributes, rhs.min_value, rhs.max_value))
+
+ def __hash__(self):
+ return id(self)
+
+
+class Module(object):
+ def __init__(self, path=None, mojom_namespace=None, attributes=None):
+ self.path = path
+ self.mojom_namespace = mojom_namespace
+ self.namespace = None
+ self.structs = []
+ self.unions = []
+ self.interfaces = []
+ self.enums = []
+ self.constants = []
+ self.kinds = {}
+ self.attributes = attributes
+ self.imports = []
+ self.imported_kinds = {}
+
+ def __repr__(self):
+ # Gives us a decent __repr__ for modules.
+ return self.Repr()
+
+ def __eq__(self, rhs):
+ return (isinstance(rhs, Module) and
+ (self.path, self.attributes, self.mojom_namespace, self.imports,
+ self.constants, self.enums, self.structs, self.unions,
+ self.interfaces) == (rhs.path, rhs.attributes, rhs.mojom_namespace,
+ rhs.imports, rhs.constants, rhs.enums,
+ rhs.structs, rhs.unions, rhs.interfaces))
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s path=%r mojom_namespace=%r>' % (
+ self.__class__.__name__, self.path, self.mojom_namespace)
+ else:
+ return GenericRepr(
+ self, {
+ 'path': False,
+ 'mojom_namespace': False,
+ 'attributes': False,
+ 'structs': False,
+ 'interfaces': False,
+ 'unions': False
+ })
+
+ def GetNamespacePrefix(self):
+ return '%s.' % self.mojom_namespace if self.mojom_namespace else ''
+
+ def AddInterface(self, mojom_name, attributes=None):
+ interface = Interface(mojom_name, self, attributes)
+ self.interfaces.append(interface)
+ return interface
+
+ def AddStruct(self, mojom_name, attributes=None):
+ struct = Struct(mojom_name, self, attributes)
+ self.structs.append(struct)
+ return struct
+
+ def AddUnion(self, mojom_name, attributes=None):
+ union = Union(mojom_name, self, attributes)
+ self.unions.append(union)
+ return union
+
+ def Stylize(self, stylizer):
+ self.namespace = stylizer.StylizeModule(self.mojom_namespace)
+ for struct in self.structs:
+ struct.Stylize(stylizer)
+ for union in self.unions:
+ union.Stylize(stylizer)
+ for interface in self.interfaces:
+ interface.Stylize(stylizer)
+ for enum in self.enums:
+ enum.Stylize(stylizer)
+ for constant in self.constants:
+ constant.Stylize(stylizer)
+
+ for imported_module in self.imports:
+ imported_module.Stylize(stylizer)
+
+ def Dump(self, f):
+ pickle.dump(self, f, 2)
+
+ @classmethod
+ def Load(cls, f):
+ result = pickle.load(f)
+ assert isinstance(result, Module)
+ return result
+
+
+def IsBoolKind(kind):
+ return kind.spec == BOOL.spec
+
+
+def IsFloatKind(kind):
+ return kind.spec == FLOAT.spec
+
+
+def IsDoubleKind(kind):
+ return kind.spec == DOUBLE.spec
+
+
+def IsIntegralKind(kind):
+ return (kind.spec == BOOL.spec or kind.spec == INT8.spec
+ or kind.spec == INT16.spec or kind.spec == INT32.spec
+ or kind.spec == INT64.spec or kind.spec == UINT8.spec
+ or kind.spec == UINT16.spec or kind.spec == UINT32.spec
+ or kind.spec == UINT64.spec)
+
+
+def IsStringKind(kind):
+ return kind.spec == STRING.spec or kind.spec == NULLABLE_STRING.spec
+
+
+def IsGenericHandleKind(kind):
+ return kind.spec == HANDLE.spec or kind.spec == NULLABLE_HANDLE.spec
+
+
+def IsDataPipeConsumerKind(kind):
+ return kind.spec == DCPIPE.spec or kind.spec == NULLABLE_DCPIPE.spec
+
+
+def IsDataPipeProducerKind(kind):
+ return kind.spec == DPPIPE.spec or kind.spec == NULLABLE_DPPIPE.spec
+
+
+def IsMessagePipeKind(kind):
+ return kind.spec == MSGPIPE.spec or kind.spec == NULLABLE_MSGPIPE.spec
+
+
+def IsSharedBufferKind(kind):
+ return (kind.spec == SHAREDBUFFER.spec
+ or kind.spec == NULLABLE_SHAREDBUFFER.spec)
+
+
+def IsPlatformHandleKind(kind):
+ return (kind.spec == PLATFORMHANDLE.spec
+ or kind.spec == NULLABLE_PLATFORMHANDLE.spec)
+
+
+def IsStructKind(kind):
+ return isinstance(kind, Struct)
+
+
+def IsUnionKind(kind):
+ return isinstance(kind, Union)
+
+
+def IsArrayKind(kind):
+ return isinstance(kind, Array)
+
+
+def IsInterfaceKind(kind):
+ return isinstance(kind, Interface)
+
+
+def IsAssociatedInterfaceKind(kind):
+ return isinstance(kind, AssociatedInterface)
+
+
+def IsInterfaceRequestKind(kind):
+ return isinstance(kind, InterfaceRequest)
+
+
+def IsAssociatedInterfaceRequestKind(kind):
+ return isinstance(kind, AssociatedInterfaceRequest)
+
+
+def IsPendingRemoteKind(kind):
+ return isinstance(kind, PendingRemote)
+
+
+def IsPendingReceiverKind(kind):
+ return isinstance(kind, PendingReceiver)
+
+
+def IsPendingAssociatedRemoteKind(kind):
+ return isinstance(kind, PendingAssociatedRemote)
+
+
+def IsPendingAssociatedReceiverKind(kind):
+ return isinstance(kind, PendingAssociatedReceiver)
+
+
+def IsEnumKind(kind):
+ return isinstance(kind, Enum)
+
+
+def IsReferenceKind(kind):
+ return isinstance(kind, ReferenceKind)
+
+
+def IsNullableKind(kind):
+ return IsReferenceKind(kind) and kind.is_nullable
+
+
+def IsMapKind(kind):
+ return isinstance(kind, Map)
+
+
+def IsObjectKind(kind):
+ return IsPointerKind(kind) or IsUnionKind(kind)
+
+
+def IsPointerKind(kind):
+ return (IsStructKind(kind) or IsArrayKind(kind) or IsStringKind(kind)
+ or IsMapKind(kind))
+
+
+# Please note that it doesn't include any interface kind.
+def IsAnyHandleKind(kind):
+ return (IsGenericHandleKind(kind) or IsDataPipeConsumerKind(kind)
+ or IsDataPipeProducerKind(kind) or IsMessagePipeKind(kind)
+ or IsSharedBufferKind(kind) or IsPlatformHandleKind(kind))
+
+
+def IsAnyInterfaceKind(kind):
+ return (IsInterfaceKind(kind) or IsInterfaceRequestKind(kind)
+ or IsAssociatedKind(kind) or IsPendingRemoteKind(kind)
+ or IsPendingReceiverKind(kind))
+
+
+def IsAnyHandleOrInterfaceKind(kind):
+ return IsAnyHandleKind(kind) or IsAnyInterfaceKind(kind)
+
+
+def IsAssociatedKind(kind):
+ return (IsAssociatedInterfaceKind(kind)
+ or IsAssociatedInterfaceRequestKind(kind)
+ or IsPendingAssociatedRemoteKind(kind)
+ or IsPendingAssociatedReceiverKind(kind))
+
+
+def HasCallbacks(interface):
+ for method in interface.methods:
+ if method.response_parameters != None:
+ return True
+ return False
+
+
+# Finds out whether an interface passes associated interfaces and associated
+# interface requests.
+def PassesAssociatedKinds(interface):
+ visited_kinds = set()
+ for method in interface.methods:
+ if MethodPassesAssociatedKinds(method, visited_kinds):
+ return True
+ return False
+
+
+def _AnyMethodParameterRecursive(method, predicate, visited_kinds=None):
+ def _HasProperty(kind):
+ if kind in visited_kinds:
+ # No need to examine the kind again.
+ return False
+ visited_kinds.add(kind)
+ if predicate(kind):
+ return True
+ if IsArrayKind(kind):
+ return _HasProperty(kind.kind)
+ if IsStructKind(kind) or IsUnionKind(kind):
+ for field in kind.fields:
+ if _HasProperty(field.kind):
+ return True
+ if IsMapKind(kind):
+ if _HasProperty(kind.key_kind) or _HasProperty(kind.value_kind):
+ return True
+ return False
+
+ if visited_kinds is None:
+ visited_kinds = set()
+
+ for param in method.parameters:
+ if _HasProperty(param.kind):
+ return True
+ if method.response_parameters != None:
+ for param in method.response_parameters:
+ if _HasProperty(param.kind):
+ return True
+ return False
+
+
+# Finds out whether a method passes associated interfaces and associated
+# interface requests.
+def MethodPassesAssociatedKinds(method, visited_kinds=None):
+ return _AnyMethodParameterRecursive(
+ method, IsAssociatedKind, visited_kinds=visited_kinds)
+
+
+# Determines whether a method passes interfaces.
+def MethodPassesInterfaces(method):
+ return _AnyMethodParameterRecursive(method, IsInterfaceKind)
+
+
+def HasSyncMethods(interface):
+ for method in interface.methods:
+ if method.sync:
+ return True
+ return False
+
+
+def ContainsHandlesOrInterfaces(kind):
+ """Check if the kind contains any handles.
+
+ This check is recursive so it checks all struct fields, containers elements,
+ etc.
+
+ Args:
+ struct: {Kind} The kind to check.
+
+ Returns:
+ {bool}: True if the kind contains handles.
+ """
+ # We remember the types we already checked to avoid infinite recursion when
+ # checking recursive (or mutually recursive) types:
+ checked = set()
+
+ def Check(kind):
+ if kind.spec in checked:
+ return False
+ checked.add(kind.spec)
+ if IsStructKind(kind):
+ return any(Check(field.kind) for field in kind.fields)
+ elif IsUnionKind(kind):
+ return any(Check(field.kind) for field in kind.fields)
+ elif IsAnyHandleKind(kind):
+ return True
+ elif IsAnyInterfaceKind(kind):
+ return True
+ elif IsArrayKind(kind):
+ return Check(kind.kind)
+ elif IsMapKind(kind):
+ return Check(kind.key_kind) or Check(kind.value_kind)
+ else:
+ return False
+
+ return Check(kind)
+
+
+def ContainsNativeTypes(kind):
+ """Check if the kind contains any native type (struct or enum).
+
+ This check is recursive so it checks all struct fields, scoped interface
+ enums, etc.
+
+ Args:
+ struct: {Kind} The kind to check.
+
+ Returns:
+ {bool}: True if the kind contains native types.
+ """
+ # We remember the types we already checked to avoid infinite recursion when
+ # checking recursive (or mutually recursive) types:
+ checked = set()
+
+ def Check(kind):
+ if kind.spec in checked:
+ return False
+ checked.add(kind.spec)
+ if IsEnumKind(kind):
+ return kind.native_only
+ elif IsStructKind(kind):
+ if kind.native_only:
+ return True
+ if any(enum.native_only for enum in kind.enums):
+ return True
+ return any(Check(field.kind) for field in kind.fields)
+ elif IsUnionKind(kind):
+ return any(Check(field.kind) for field in kind.fields)
+ elif IsInterfaceKind(kind):
+ return any(enum.native_only for enum in kind.enums)
+ elif IsArrayKind(kind):
+ return Check(kind.kind)
+ elif IsMapKind(kind):
+ return Check(kind.key_kind) or Check(kind.value_kind)
+ else:
+ return False
+
+ return Check(kind)