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.py163
1 files changed, 143 insertions, 20 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
index 8547ff64..9bdb28e0 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py
@@ -12,7 +12,33 @@
# method = interface.AddMethod('Tat', 0)
# method.AddParameter('baz', 0, mojom.INT32)
-import pickle
+import sys
+if sys.version_info.major == 2:
+ import cPickle as pickle
+else:
+ import pickle
+from uuid import UUID
+
+
+class BackwardCompatibilityChecker(object):
+ """Used for memoization while recursively checking two type definitions for
+ backward-compatibility."""
+
+ def __init__(self):
+ self._cache = {}
+
+ def IsBackwardCompatible(self, new_kind, old_kind):
+ key = (new_kind, old_kind)
+ result = self._cache.get(key)
+ if result is None:
+ # Assume they're compatible at first to effectively ignore recursive
+ # checks between these types, e.g. if both kinds are a struct or union
+ # that references itself in a field.
+ self._cache[key] = True
+ result = new_kind.IsBackwardCompatible(old_kind, self)
+ self._cache[key] = result
+ return result
+
# 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
@@ -114,6 +140,10 @@ class Kind(object):
# during a subsequent run of the parser.
return hash((self.spec, self.parent_kind))
+ # pylint: disable=unused-argument
+ def IsBackwardCompatible(self, rhs, checker):
+ return self == rhs
+
class ReferenceKind(Kind):
"""ReferenceKind represents pointer and handle types.
@@ -195,6 +225,10 @@ class ReferenceKind(Kind):
def __hash__(self):
return hash((super(ReferenceKind, self).__hash__(), self.is_nullable))
+ def IsBackwardCompatible(self, rhs, checker):
+ return (super(ReferenceKind, self).IsBackwardCompatible(rhs, checker)
+ and self.is_nullable == rhs.is_nullable)
+
# Initialize the set of primitive types. These can be accessed by clients.
BOOL = Kind('b')
@@ -253,9 +287,13 @@ PRIMITIVES = (
)
ATTRIBUTE_MIN_VERSION = 'MinVersion'
+ATTRIBUTE_DEFAULT = 'Default'
ATTRIBUTE_EXTENSIBLE = 'Extensible'
+ATTRIBUTE_NO_INTERRUPT = 'NoInterrupt'
ATTRIBUTE_STABLE = 'Stable'
ATTRIBUTE_SYNC = 'Sync'
+ATTRIBUTE_UNLIMITED_SIZE = 'UnlimitedSize'
+ATTRIBUTE_UUID = 'Uuid'
class NamedValue(object):
@@ -274,6 +312,9 @@ class NamedValue(object):
and (self.parent_kind, self.mojom_name) == (rhs.parent_kind,
rhs.mojom_name))
+ def __hash__(self):
+ return hash((self.parent_kind, self.mojom_name))
+
class BuiltinValue(object):
def __init__(self, value):
@@ -368,21 +409,19 @@ class Field(object):
class StructField(Field):
- pass
+ def __hash__(self):
+ return super(Field, self).__hash__()
class UnionField(Field):
pass
-def _IsFieldBackwardCompatible(new_field, old_field):
+def _IsFieldBackwardCompatible(new_field, old_field, checker):
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
+ return checker.IsBackwardCompatible(new_field.kind, old_field.kind)
class Struct(ReferenceKind):
@@ -457,7 +496,7 @@ class Struct(ReferenceKind):
for constant in self.constants:
constant.Stylize(stylizer)
- def IsBackwardCompatible(self, older_struct):
+ def IsBackwardCompatible(self, older_struct, checker):
"""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
@@ -496,7 +535,7 @@ class Struct(ReferenceKind):
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):
+ if not _IsFieldBackwardCompatible(new_field, old_field, checker):
# Type or min-version mismatch between old and new versions of the same
# ordinal field.
return False
@@ -590,7 +629,7 @@ class Union(ReferenceKind):
for field in self.fields:
field.Stylize(stylizer)
- def IsBackwardCompatible(self, older_union):
+ def IsBackwardCompatible(self, older_union, checker):
"""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
@@ -623,7 +662,7 @@ class Union(ReferenceKind):
if not new_field:
# A field was removed, which is not OK.
return False
- if not _IsFieldBackwardCompatible(new_field, old_field):
+ if not _IsFieldBackwardCompatible(new_field, old_field, checker):
# An field changed its type or MinVersion, which is not OK.
return False
old_min_version = old_field.min_version or 0
@@ -703,6 +742,10 @@ class Array(ReferenceKind):
def __hash__(self):
return id(self)
+ def IsBackwardCompatible(self, rhs, checker):
+ return (isinstance(rhs, Array) and self.length == rhs.length
+ and checker.IsBackwardCompatible(self.kind, rhs.kind))
+
class Map(ReferenceKind):
"""A map.
@@ -747,6 +790,11 @@ class Map(ReferenceKind):
def __hash__(self):
return id(self)
+ def IsBackwardCompatible(self, rhs, checker):
+ return (isinstance(rhs, Map)
+ and checker.IsBackwardCompatible(self.key_kind, rhs.key_kind)
+ and checker.IsBackwardCompatible(self.value_kind, rhs.value_kind))
+
class PendingRemote(ReferenceKind):
ReferenceKind.AddSharedProperty('kind')
@@ -768,6 +816,10 @@ class PendingRemote(ReferenceKind):
def __hash__(self):
return id(self)
+ def IsBackwardCompatible(self, rhs, checker):
+ return (isinstance(rhs, PendingRemote)
+ and checker.IsBackwardCompatible(self.kind, rhs.kind))
+
class PendingReceiver(ReferenceKind):
ReferenceKind.AddSharedProperty('kind')
@@ -789,6 +841,10 @@ class PendingReceiver(ReferenceKind):
def __hash__(self):
return id(self)
+ def IsBackwardCompatible(self, rhs, checker):
+ return isinstance(rhs, PendingReceiver) and checker.IsBackwardCompatible(
+ self.kind, rhs.kind)
+
class PendingAssociatedRemote(ReferenceKind):
ReferenceKind.AddSharedProperty('kind')
@@ -810,6 +866,11 @@ class PendingAssociatedRemote(ReferenceKind):
def __hash__(self):
return id(self)
+ def IsBackwardCompatible(self, rhs, checker):
+ return isinstance(rhs,
+ PendingAssociatedRemote) and checker.IsBackwardCompatible(
+ self.kind, rhs.kind)
+
class PendingAssociatedReceiver(ReferenceKind):
ReferenceKind.AddSharedProperty('kind')
@@ -831,6 +892,11 @@ class PendingAssociatedReceiver(ReferenceKind):
def __hash__(self):
return id(self)
+ def IsBackwardCompatible(self, rhs, checker):
+ return isinstance(
+ rhs, PendingAssociatedReceiver) and checker.IsBackwardCompatible(
+ self.kind, rhs.kind)
+
class InterfaceRequest(ReferenceKind):
ReferenceKind.AddSharedProperty('kind')
@@ -851,6 +917,10 @@ class InterfaceRequest(ReferenceKind):
def __hash__(self):
return id(self)
+ def IsBackwardCompatible(self, rhs, checker):
+ return isinstance(rhs, InterfaceRequest) and checker.IsBackwardCompatible(
+ self.kind, rhs.kind)
+
class AssociatedInterfaceRequest(ReferenceKind):
ReferenceKind.AddSharedProperty('kind')
@@ -873,6 +943,11 @@ class AssociatedInterfaceRequest(ReferenceKind):
def __hash__(self):
return id(self)
+ def IsBackwardCompatible(self, rhs, checker):
+ return isinstance(
+ rhs, AssociatedInterfaceRequest) and checker.IsBackwardCompatible(
+ self.kind, rhs.kind)
+
class Parameter(object):
def __init__(self,
@@ -976,6 +1051,16 @@ class Method(object):
return self.attributes.get(ATTRIBUTE_SYNC) \
if self.attributes else None
+ @property
+ def allow_interrupt(self):
+ return not self.attributes.get(ATTRIBUTE_NO_INTERRUPT) \
+ if self.attributes else True
+
+ @property
+ def unlimited_message_size(self):
+ return self.attributes.get(ATTRIBUTE_UNLIMITED_SIZE) \
+ if self.attributes else False
+
def __eq__(self, rhs):
return (isinstance(rhs, Method) and
(self.mojom_name, self.ordinal, self.parameters,
@@ -1029,7 +1114,7 @@ class Interface(ReferenceKind):
for constant in self.constants:
constant.Stylize(stylizer)
- def IsBackwardCompatible(self, older_interface):
+ def IsBackwardCompatible(self, older_interface, checker):
"""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
@@ -1067,8 +1152,8 @@ class Interface(ReferenceKind):
# A method was removed, which is not OK.
return False
- if not new_method.param_struct.IsBackwardCompatible(
- old_method.param_struct):
+ if not checker.IsBackwardCompatible(new_method.param_struct,
+ old_method.param_struct):
# The parameter list is not backward-compatible, which is not OK.
return False
@@ -1081,8 +1166,8 @@ class Interface(ReferenceKind):
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):
+ if not checker.IsBackwardCompatible(new_method.response_param_struct,
+ 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
@@ -1120,6 +1205,20 @@ class Interface(ReferenceKind):
self.attributes) == (rhs.mojom_name, rhs.methods, rhs.enums,
rhs.constants, rhs.attributes))
+ @property
+ def uuid(self):
+ uuid_str = self.attributes.get(ATTRIBUTE_UUID) if self.attributes else None
+ if uuid_str is None:
+ return None
+
+ try:
+ u = UUID(uuid_str)
+ except:
+ raise ValueError('Invalid format for Uuid attribute on interface {}. '
+ 'Expected standard RFC 4122 string representation of '
+ 'a UUID.'.format(self.mojom_name))
+ return (int(u.hex[:16], 16), int(u.hex[16:], 16))
+
def __hash__(self):
return id(self)
@@ -1144,6 +1243,11 @@ class AssociatedInterface(ReferenceKind):
def __hash__(self):
return id(self)
+ def IsBackwardCompatible(self, rhs, checker):
+ return isinstance(rhs,
+ AssociatedInterface) and checker.IsBackwardCompatible(
+ self.kind, rhs.kind)
+
class EnumField(object):
def __init__(self,
@@ -1161,6 +1265,11 @@ class EnumField(object):
self.name = stylizer.StylizeEnumField(self.mojom_name)
@property
+ def default(self):
+ return self.attributes.get(ATTRIBUTE_DEFAULT, False) \
+ if self.attributes else False
+
+ @property
def min_version(self):
return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
if self.attributes else None
@@ -1186,6 +1295,7 @@ class Enum(Kind):
self.attributes = attributes
self.min_value = None
self.max_value = None
+ self.default_field = None
def Repr(self, as_ref=True):
if as_ref:
@@ -1216,7 +1326,8 @@ class Enum(Kind):
prefix = self.module.GetNamespacePrefix()
return '%s%s' % (prefix, self.mojom_name)
- def IsBackwardCompatible(self, older_enum):
+ # pylint: disable=unused-argument
+ def IsBackwardCompatible(self, older_enum, checker):
"""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
@@ -1250,9 +1361,10 @@ class Enum(Kind):
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))
+ self.min_value, self.max_value,
+ self.default_field) == (rhs.mojom_name, rhs.native_only,
+ rhs.fields, rhs.attributes, rhs.min_value,
+ rhs.max_value, rhs.default_field))
def __hash__(self):
return id(self)
@@ -1272,6 +1384,7 @@ class Module(object):
self.attributes = attributes
self.imports = []
self.imported_kinds = {}
+ self.metadata = {}
def __repr__(self):
# Gives us a decent __repr__ for modules.
@@ -1285,6 +1398,9 @@ class Module(object):
rhs.imports, rhs.constants, rhs.enums,
rhs.structs, rhs.unions, rhs.interfaces))
+ def __hash__(self):
+ return id(self)
+
def Repr(self, as_ref=True):
if as_ref:
return '<%s path=%r mojom_namespace=%r>' % (
@@ -1555,6 +1671,13 @@ def HasSyncMethods(interface):
return False
+def HasUninterruptableMethods(interface):
+ for method in interface.methods:
+ if not method.allow_interrupt:
+ return True
+ return False
+
+
def ContainsHandlesOrInterfaces(kind):
"""Check if the kind contains any handles.