diff options
Diffstat (limited to 'utils/ipc/mojo/public/tools/mojom/mojom')
5 files changed, 175 insertions, 40 deletions
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn b/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn index 7416ef19..51facc0c 100644 --- a/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn +++ b/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn @@ -8,7 +8,6 @@ group("mojom") { "error.py", "fileutil.py", "generate/__init__.py", - "generate/constant_resolver.py", "generate/generator.py", "generate/module.py", "generate/pack.py", diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py index de62260a..4a1c73fc 100644 --- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py +++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py @@ -136,9 +136,14 @@ class Stylizer(object): def WriteFile(contents, full_path): # If |contents| is same with the file content, we skip updating. + if not isinstance(contents, bytes): + data = contents.encode('utf8') + else: + data = contents + if os.path.isfile(full_path): with open(full_path, 'rb') as destination_file: - if destination_file.read() == contents: + if destination_file.read() == data: return # Make sure the containing directory exists. @@ -146,11 +151,8 @@ def WriteFile(contents, full_path): fileutil.EnsureDirectoryExists(full_dir) # Dump the data to disk. - with open(full_path, "wb") as f: - if not isinstance(contents, bytes): - f.write(contents.encode('utf-8')) - else: - f.write(contents) + with open(full_path, 'wb') as f: + f.write(data) def AddComputedData(module): 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. diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py index 7a300560..0da90058 100644 --- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py +++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py @@ -75,9 +75,8 @@ def PrecompileTemplates(generator_modules, output_dir): os.path.dirname(module.__file__), generator.GetTemplatePrefix()) ])) jinja_env.filters.update(generator.GetFilters()) - jinja_env.compile_templates( - os.path.join(output_dir, "%s.zip" % generator.GetTemplatePrefix()), - extensions=["tmpl"], - zip="stored", - py_compile=True, - ignore_errors=False) + jinja_env.compile_templates(os.path.join( + output_dir, "%s.zip" % generator.GetTemplatePrefix()), + extensions=["tmpl"], + zip="stored", + ignore_errors=False) diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py index d6df3ca6..7580b780 100644 --- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py +++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py @@ -472,6 +472,9 @@ def _Method(module, parsed_method, interface): "attribute. If no response parameters are needed, you " "could use an empty response parameter list, i.e., " "\"=> ()\".") + # And only methods with the [Sync] attribute can specify [NoInterrupt]. + if not method.allow_interrupt and not method.sync: + raise Exception("Only [Sync] methods can be marked [NoInterrupt].") return method @@ -592,6 +595,16 @@ def _Enum(module, parsed_enum, parent_kind): map(lambda field: _EnumField(module, enum, field), parsed_enum.enum_value_list)) _ResolveNumericEnumValues(enum) + # TODO(https://crbug.com/731893): Require a default value to be + # specified. + for field in enum.fields: + if field.default: + if not enum.extensible: + raise Exception('Non-extensible enums may not specify a default') + if enum.default_field is not None: + raise Exception( + 'Only one enumerator value may be specified as the default') + enum.default_field = field module.kinds[enum.spec] = enum @@ -650,7 +663,9 @@ def _CollectReferencedKinds(module, all_defined_kinds): if mojom.IsMapKind(kind): return (extract_referenced_user_kinds(kind.key_kind) + extract_referenced_user_kinds(kind.value_kind)) - if mojom.IsInterfaceRequestKind(kind) or mojom.IsAssociatedKind(kind): + if (mojom.IsInterfaceRequestKind(kind) or mojom.IsAssociatedKind(kind) + or mojom.IsPendingRemoteKind(kind) + or mojom.IsPendingReceiverKind(kind)): return [kind.kind] if mojom.IsStructKind(kind): return [kind] @@ -678,12 +693,9 @@ def _CollectReferencedKinds(module, all_defined_kinds): for method in interface.methods: for param in itertools.chain(method.parameters or [], method.response_parameters or []): - if (mojom.IsStructKind(param.kind) or mojom.IsUnionKind(param.kind) - or mojom.IsEnumKind(param.kind) - or mojom.IsAnyInterfaceKind(param.kind)): - for referenced_kind in extract_referenced_user_kinds(param.kind): - sanitized_kind = sanitize_kind(referenced_kind) - referenced_user_kinds[sanitized_kind.spec] = sanitized_kind + for referenced_kind in extract_referenced_user_kinds(param.kind): + sanitized_kind = sanitize_kind(referenced_kind) + referenced_user_kinds[sanitized_kind.spec] = sanitized_kind return referenced_user_kinds |