diff options
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.py | 163 |
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. |