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 | 787 |
1 files changed, 544 insertions, 243 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 9bdb28e0..ca71059d 100644 --- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py +++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py @@ -1,4 +1,4 @@ -# Copyright 2013 The Chromium Authors. All rights reserved. +# Copyright 2013 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -12,15 +12,14 @@ # method = interface.AddMethod('Tat', 0) # method.AddParameter('baz', 0, mojom.INT32) -import sys -if sys.version_info.major == 2: - import cPickle as pickle -else: - import pickle +import pickle +from collections import OrderedDict from uuid import UUID +# pylint: disable=raise-missing-from -class BackwardCompatibilityChecker(object): + +class BackwardCompatibilityChecker: """Used for memoization while recursively checking two type definitions for backward-compatibility.""" @@ -64,23 +63,20 @@ def Repr(obj, as_ref=True): 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 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): + return ('[\n%s\n]' % + (',\n'.join(' %s' % Repr(elem, as_ref).replace('\n', '\n ') + for elem in obj))) + if 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) + 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()))) + return repr(obj) def GenericRepr(obj, names): @@ -104,7 +100,7 @@ def GenericRepr(obj, names): ReprIndent(name, as_ref) for (name, as_ref) in names.items())) -class Kind(object): +class Kind: """Kind represents a type (e.g. int8, string). Attributes: @@ -112,16 +108,43 @@ class Kind(object): 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. + is_nullable: True if the type is nullable. """ - def __init__(self, spec=None, module=None): + def __init__(self, spec=None, is_nullable=False, module=None): self.spec = spec self.module = module self.parent_kind = None + self.is_nullable = is_nullable + self.shared_definition = {} + + @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 Repr(self, as_ref=True): # pylint: disable=unused-argument - return '<%s spec=%r>' % (self.__class__.__name__, self.spec) + return '<%s spec=%r is_nullable=%r>' % (self.__class__.__name__, self.spec, + self.is_nullable) def __repr__(self): # Gives us a decent __repr__ for all kinds. @@ -130,7 +153,8 @@ class Kind(object): def __eq__(self, rhs): # pylint: disable=unidiomatic-typecheck return (type(self) == type(rhs) - and (self.spec, self.parent_kind) == (rhs.spec, rhs.parent_kind)) + and (self.spec, self.parent_kind, self.is_nullable) + == (rhs.spec, rhs.parent_kind, rhs.is_nullable)) def __hash__(self): # TODO(crbug.com/1060471): Remove this and other __hash__ methods on Kind @@ -138,32 +162,113 @@ class Kind(object): # 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)) + return hash((self.spec, self.parent_kind, self.is_nullable)) # pylint: disable=unused-argument def IsBackwardCompatible(self, rhs, checker): return self == rhs +class ValueKind(Kind): + """ValueKind represents values that aren't reference kinds. + + The primary difference is the wire representation for nullable value kinds + still reserves space for the value type itself, even if that value itself + is logically null. + """ + def __init__(self, spec=None, is_nullable=False, module=None): + assert spec is None or is_nullable == spec.startswith('?') + Kind.__init__(self, spec, is_nullable, module) + + def MakeNullableKind(self): + assert not self.is_nullable + + if self == BOOL: + return NULLABLE_BOOL + if self == INT8: + return NULLABLE_INT8 + if self == INT16: + return NULLABLE_INT16 + if self == INT32: + return NULLABLE_INT32 + if self == INT64: + return NULLABLE_INT64 + if self == UINT8: + return NULLABLE_UINT8 + if self == UINT16: + return NULLABLE_UINT16 + if self == UINT32: + return NULLABLE_UINT32 + if self == UINT64: + return NULLABLE_UINT64 + if self == FLOAT: + return NULLABLE_FLOAT + if self == DOUBLE: + return NULLABLE_DOUBLE + + 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 + + def MakeUnnullableKind(self): + assert self.is_nullable + + if self == NULLABLE_BOOL: + return BOOL + if self == NULLABLE_INT8: + return INT8 + if self == NULLABLE_INT16: + return INT16 + if self == NULLABLE_INT32: + return INT32 + if self == NULLABLE_INT64: + return INT64 + if self == NULLABLE_UINT8: + return UINT8 + if self == NULLABLE_UINT16: + return UINT16 + if self == NULLABLE_UINT32: + return UINT32 + if self == NULLABLE_UINT64: + return UINT64 + if self == NULLABLE_FLOAT: + return FLOAT + if self == NULLABLE_DOUBLE: + return DOUBLE + + nullable_kind = type(self)() + nullable_kind.shared_definition = self.shared_definition + if self.spec is not None: + nullable_kind.spec = self.spec[1:] + nullable_kind.is_nullable = False + nullable_kind.parent_kind = self.parent_kind + nullable_kind.module = self.module + + return nullable_kind + + def __eq__(self, rhs): + return (isinstance(rhs, ValueKind) and super().__eq__(rhs)) + + def __hash__(self): # pylint: disable=useless-super-delegation + return super().__hash__() + + 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) + Kind.__init__(self, spec, is_nullable, module) def MakeNullableKind(self): assert not self.is_nullable @@ -193,55 +298,65 @@ class ReferenceKind(Kind): 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 + def MakeUnnullableKind(self): + assert self.is_nullable + + if self == NULLABLE_STRING: + return STRING + if self == NULLABLE_HANDLE: + return HANDLE + if self == NULLABLE_DCPIPE: + return DCPIPE + if self == NULLABLE_DPPIPE: + return DPPIPE + if self == NULLABLE_MSGPIPE: + return MSGPIPE + if self == NULLABLE_SHAREDBUFFER: + return SHAREDBUFFER + if self == NULLABLE_PLATFORMHANDLE: + return PLATFORMHANDLE + + unnullable_kind = type(self)() + unnullable_kind.shared_definition = self.shared_definition + if self.spec is not None: + assert self.spec[0] == '?' + unnullable_kind.spec = self.spec[1:] + unnullable_kind.is_nullable = False + unnullable_kind.parent_kind = self.parent_kind + unnullable_kind.module = self.module - setattr(cls, name, property(Get, Set)) + return unnullable_kind def __eq__(self, rhs): - return (isinstance(rhs, ReferenceKind) - and super(ReferenceKind, self).__eq__(rhs) - and self.is_nullable == rhs.is_nullable) + return (isinstance(rhs, ReferenceKind) and super().__eq__(rhs)) - 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) + def __hash__(self): # pylint: disable=useless-super-delegation + return super().__hash__() # 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') +BOOL = ValueKind('b') +INT8 = ValueKind('i8') +INT16 = ValueKind('i16') +INT32 = ValueKind('i32') +INT64 = ValueKind('i64') +UINT8 = ValueKind('u8') +UINT16 = ValueKind('u16') +UINT32 = ValueKind('u32') +UINT64 = ValueKind('u64') +FLOAT = ValueKind('f') +DOUBLE = ValueKind('d') +NULLABLE_BOOL = ValueKind('?b', True) +NULLABLE_INT8 = ValueKind('?i8', True) +NULLABLE_INT16 = ValueKind('?i16', True) +NULLABLE_INT32 = ValueKind('?i32', True) +NULLABLE_INT64 = ValueKind('?i64', True) +NULLABLE_UINT8 = ValueKind('?u8', True) +NULLABLE_UINT16 = ValueKind('?u16', True) +NULLABLE_UINT32 = ValueKind('?u32', True) +NULLABLE_UINT64 = ValueKind('?u64', True) +NULLABLE_FLOAT = ValueKind('?f', True) +NULLABLE_DOUBLE = ValueKind('?d', True) STRING = ReferenceKind('s') HANDLE = ReferenceKind('h') DCPIPE = ReferenceKind('h:d:c') @@ -270,6 +385,17 @@ PRIMITIVES = ( UINT64, FLOAT, DOUBLE, + NULLABLE_BOOL, + NULLABLE_INT8, + NULLABLE_INT16, + NULLABLE_INT32, + NULLABLE_INT64, + NULLABLE_UINT8, + NULLABLE_UINT16, + NULLABLE_UINT32, + NULLABLE_UINT64, + NULLABLE_FLOAT, + NULLABLE_DOUBLE, STRING, HANDLE, DCPIPE, @@ -291,12 +417,17 @@ ATTRIBUTE_DEFAULT = 'Default' ATTRIBUTE_EXTENSIBLE = 'Extensible' ATTRIBUTE_NO_INTERRUPT = 'NoInterrupt' ATTRIBUTE_STABLE = 'Stable' +ATTRIBUTE_SUPPORTS_URGENT = 'SupportsUrgent' ATTRIBUTE_SYNC = 'Sync' ATTRIBUTE_UNLIMITED_SIZE = 'UnlimitedSize' ATTRIBUTE_UUID = 'Uuid' +ATTRIBUTE_SERVICE_SANDBOX = 'ServiceSandbox' +ATTRIBUTE_REQUIRE_CONTEXT = 'RequireContext' +ATTRIBUTE_ALLOWED_CONTEXT = 'AllowedContext' +ATTRIBUTE_RUNTIME_FEATURE = 'RuntimeFeature' -class NamedValue(object): +class NamedValue: def __init__(self, module, parent_kind, mojom_name): self.module = module self.parent_kind = parent_kind @@ -316,7 +447,7 @@ class NamedValue(object): return hash((self.parent_kind, self.mojom_name)) -class BuiltinValue(object): +class BuiltinValue: def __init__(self, value): self.value = value @@ -350,7 +481,7 @@ class EnumValue(NamedValue): return self.field.name -class Constant(object): +class Constant: def __init__(self, mojom_name=None, kind=None, value=None, parent_kind=None): self.mojom_name = mojom_name self.name = None @@ -368,7 +499,7 @@ class Constant(object): rhs.parent_kind)) -class Field(object): +class Field: def __init__(self, mojom_name=None, kind=None, @@ -414,7 +545,18 @@ class StructField(Field): class UnionField(Field): - pass + def __init__(self, + mojom_name=None, + kind=None, + ordinal=None, + default=None, + attributes=None): + Field.__init__(self, mojom_name, kind, ordinal, default, attributes) + + @property + def is_default(self): + return self.attributes.get(ATTRIBUTE_DEFAULT, False) \ + if self.attributes else False def _IsFieldBackwardCompatible(new_field, old_field, checker): @@ -424,6 +566,38 @@ def _IsFieldBackwardCompatible(new_field, old_field, checker): return checker.IsBackwardCompatible(new_field.kind, old_field.kind) +class Feature(ReferenceKind): + """A runtime enabled feature defined from mojom. + + Attributes: + mojom_name: {str} The name of the feature type as defined in mojom. + name: {str} The stylized name. (Note: not the "name" used by FeatureList.) + constants: {List[Constant]} The constants defined in the feature scope. + attributes: {dict} Additional information about the feature. + """ + + Kind.AddSharedProperty('mojom_name') + Kind.AddSharedProperty('name') + Kind.AddSharedProperty('constants') + Kind.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.constants = [] + self.attributes = attributes + + def Stylize(self, stylizer): + self.name = stylizer.StylizeFeature(self.mojom_name) + for constant in self.constants: + constant.Stylize(stylizer) + + class Struct(ReferenceKind): """A struct with typed fields. @@ -441,14 +615,14 @@ class Struct(ReferenceKind): 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') + Kind.AddSharedProperty('mojom_name') + Kind.AddSharedProperty('name') + Kind.AddSharedProperty('native_only') + Kind.AddSharedProperty('custom_serializer') + Kind.AddSharedProperty('fields') + Kind.AddSharedProperty('enums') + Kind.AddSharedProperty('constants') + Kind.AddSharedProperty('attributes') def __init__(self, mojom_name=None, module=None, attributes=None): if mojom_name is not None: @@ -470,12 +644,11 @@ class Struct(ReferenceKind): 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 - }) + return GenericRepr(self, { + 'mojom_name': False, + 'fields': False, + 'module': True + }) def AddField(self, mojom_name, @@ -496,13 +669,13 @@ class Struct(ReferenceKind): for constant in self.constants: constant.Stylize(stylizer) - def IsBackwardCompatible(self, older_struct, checker): - """This struct is backward-compatible with older_struct if and only if all - of the following conditions hold: + def IsBackwardCompatible(self, rhs, checker): + """This struct is backward-compatible with rhs (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, + - All fields present in rhs 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. @@ -521,7 +694,7 @@ class Struct(ReferenceKind): return fields_by_ordinal new_fields = buildOrdinalFieldMap(self) - old_fields = buildOrdinalFieldMap(older_struct) + old_fields = buildOrdinalFieldMap(rhs) if len(new_fields) < len(old_fields): # At least one field was removed, which is not OK. return False @@ -574,11 +747,18 @@ class Struct(ReferenceKind): prefix = self.module.GetNamespacePrefix() return '%s%s' % (prefix, self.mojom_name) + def _tuple(self): + return (self.mojom_name, self.native_only, self.fields, self.constants, + self.attributes) + 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)) + return isinstance(rhs, Struct) and self._tuple() == rhs._tuple() + + def __lt__(self, rhs): + if not isinstance(self, type(rhs)): + return str(type(self)) < str(type(rhs)) + + return self._tuple() < rhs._tuple() def __hash__(self): return id(self) @@ -595,10 +775,11 @@ class Union(ReferenceKind): 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') + Kind.AddSharedProperty('mojom_name') + Kind.AddSharedProperty('name') + Kind.AddSharedProperty('fields') + Kind.AddSharedProperty('attributes') + Kind.AddSharedProperty('default_field') def __init__(self, mojom_name=None, module=None, attributes=None): if mojom_name is not None: @@ -610,14 +791,14 @@ class Union(ReferenceKind): self.name = None self.fields = [] self.attributes = attributes + self.default_field = None 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}) + 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) @@ -629,13 +810,13 @@ class Union(ReferenceKind): for field in self.fields: field.Stylize(stylizer) - def IsBackwardCompatible(self, older_union, checker): - """This union is backward-compatible with older_union if and only if all - of the following conditions hold: + def IsBackwardCompatible(self, rhs, checker): + """This union is backward-compatible with rhs (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, + - All fields present in rhs 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. @@ -651,7 +832,7 @@ class Union(ReferenceKind): return fields_by_ordinal new_fields = buildOrdinalFieldMap(self) - old_fields = buildOrdinalFieldMap(older_union) + old_fields = buildOrdinalFieldMap(rhs) if len(new_fields) < len(old_fields): # At least one field was removed, which is not OK. return False @@ -678,6 +859,11 @@ class Union(ReferenceKind): return True @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 @@ -690,10 +876,17 @@ class Union(ReferenceKind): prefix = self.module.GetNamespacePrefix() return '%s%s' % (prefix, self.mojom_name) + def _tuple(self): + return (self.mojom_name, self.fields, self.attributes) + def __eq__(self, rhs): - return (isinstance(rhs, Union) and - (self.mojom_name, self.fields, - self.attributes) == (rhs.mojom_name, rhs.fields, rhs.attributes)) + return isinstance(rhs, Union) and self._tuple() == rhs._tuple() + + def __lt__(self, rhs): + if not isinstance(self, type(rhs)): + return str(type(self)) < str(type(rhs)) + + return self._tuple() < rhs._tuple() def __hash__(self): return id(self) @@ -707,8 +900,8 @@ class Array(ReferenceKind): length: The number of elements. None if unknown. """ - ReferenceKind.AddSharedProperty('kind') - ReferenceKind.AddSharedProperty('length') + Kind.AddSharedProperty('kind') + Kind.AddSharedProperty('length') def __init__(self, kind=None, length=None): if kind is not None: @@ -728,12 +921,11 @@ class Array(ReferenceKind): 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 - }) + return GenericRepr(self, { + 'kind': True, + 'length': False, + 'is_nullable': False + }) def __eq__(self, rhs): return (isinstance(rhs, Array) @@ -754,8 +946,8 @@ class Map(ReferenceKind): 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') + Kind.AddSharedProperty('key_kind') + Kind.AddSharedProperty('value_kind') def __init__(self, key_kind=None, value_kind=None): if (key_kind is not None and value_kind is not None): @@ -780,8 +972,7 @@ class Map(ReferenceKind): 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}) + return GenericRepr(self, {'key_kind': True, 'value_kind': True}) def __eq__(self, rhs): return (isinstance(rhs, Map) and @@ -797,7 +988,7 @@ class Map(ReferenceKind): class PendingRemote(ReferenceKind): - ReferenceKind.AddSharedProperty('kind') + Kind.AddSharedProperty('kind') def __init__(self, kind=None): if kind is not None: @@ -822,7 +1013,7 @@ class PendingRemote(ReferenceKind): class PendingReceiver(ReferenceKind): - ReferenceKind.AddSharedProperty('kind') + Kind.AddSharedProperty('kind') def __init__(self, kind=None): if kind is not None: @@ -847,7 +1038,7 @@ class PendingReceiver(ReferenceKind): class PendingAssociatedRemote(ReferenceKind): - ReferenceKind.AddSharedProperty('kind') + Kind.AddSharedProperty('kind') def __init__(self, kind=None): if kind is not None: @@ -873,7 +1064,7 @@ class PendingAssociatedRemote(ReferenceKind): class PendingAssociatedReceiver(ReferenceKind): - ReferenceKind.AddSharedProperty('kind') + Kind.AddSharedProperty('kind') def __init__(self, kind=None): if kind is not None: @@ -899,7 +1090,7 @@ class PendingAssociatedReceiver(ReferenceKind): class InterfaceRequest(ReferenceKind): - ReferenceKind.AddSharedProperty('kind') + Kind.AddSharedProperty('kind') def __init__(self, kind=None): if kind is not None: @@ -923,7 +1114,7 @@ class InterfaceRequest(ReferenceKind): class AssociatedInterfaceRequest(ReferenceKind): - ReferenceKind.AddSharedProperty('kind') + Kind.AddSharedProperty('kind') def __init__(self, kind=None): if kind is not None: @@ -949,7 +1140,7 @@ class AssociatedInterfaceRequest(ReferenceKind): self.kind, rhs.kind) -class Parameter(object): +class Parameter: def __init__(self, mojom_name=None, kind=None, @@ -983,7 +1174,7 @@ class Parameter(object): rhs.default, rhs.attributes)) -class Method(object): +class Method: def __init__(self, interface, mojom_name, ordinal=None, attributes=None): self.interface = interface self.mojom_name = mojom_name @@ -999,12 +1190,11 @@ class Method(object): 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 - }) + return GenericRepr(self, { + 'mojom_name': False, + 'parameters': True, + 'response_parameters': True + }) def AddParameter(self, mojom_name, @@ -1061,21 +1251,49 @@ class Method(object): return self.attributes.get(ATTRIBUTE_UNLIMITED_SIZE) \ if self.attributes else False + @property + def allowed_context(self): + return self.attributes.get(ATTRIBUTE_ALLOWED_CONTEXT) \ + if self.attributes else None + + @property + def supports_urgent(self): + return self.attributes.get(ATTRIBUTE_SUPPORTS_URGENT) \ + if self.attributes else None + + @property + def runtime_feature(self): + if not self.attributes: + return None + runtime_feature = self.attributes.get(ATTRIBUTE_RUNTIME_FEATURE, None) + if runtime_feature is None: + return None + if not isinstance(runtime_feature, Feature): + raise Exception("RuntimeFeature attribute on %s must be a feature." % + self.name) + return runtime_feature + + def _tuple(self): + return (self.mojom_name, self.ordinal, self.parameters, + self.response_parameters, self.attributes) + 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)) + return isinstance(rhs, Method) and self._tuple() == rhs._tuple() + + def __lt__(self, rhs): + if not isinstance(self, type(rhs)): + return str(type(self)) < str(type(rhs)) + + return self._tuple() < rhs._tuple() class Interface(ReferenceKind): - ReferenceKind.AddSharedProperty('mojom_name') - ReferenceKind.AddSharedProperty('name') - ReferenceKind.AddSharedProperty('methods') - ReferenceKind.AddSharedProperty('enums') - ReferenceKind.AddSharedProperty('constants') - ReferenceKind.AddSharedProperty('attributes') + Kind.AddSharedProperty('mojom_name') + Kind.AddSharedProperty('name') + Kind.AddSharedProperty('methods') + Kind.AddSharedProperty('enums') + Kind.AddSharedProperty('constants') + Kind.AddSharedProperty('attributes') def __init__(self, mojom_name=None, module=None, attributes=None): if mojom_name is not None: @@ -1093,12 +1311,11 @@ class Interface(ReferenceKind): 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 - }) + 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) @@ -1114,10 +1331,10 @@ class Interface(ReferenceKind): for constant in self.constants: constant.Stylize(stylizer) - 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 + def IsBackwardCompatible(self, rhs, checker): + """This interface is backward-compatible with rhs (older_interface) if and + only if all of the following conditions hold: + - All defined methods in rhs (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- @@ -1131,7 +1348,7 @@ class Interface(ReferenceKind): rules for structs. - All newly introduced methods in this interface have a [MinVersion] attribute specifying a version greater than any method in - older_interface. + rhs. """ def buildOrdinalMethodMap(interface): @@ -1144,7 +1361,7 @@ class Interface(ReferenceKind): return methods_by_ordinal new_methods = buildOrdinalMethodMap(self) - old_methods = buildOrdinalMethodMap(older_interface) + old_methods = buildOrdinalMethodMap(rhs) max_old_min_version = 0 for ordinal, old_method in old_methods.items(): new_method = new_methods.get(ordinal) @@ -1187,6 +1404,39 @@ class Interface(ReferenceKind): return True @property + def service_sandbox(self): + if not self.attributes: + return None + service_sandbox = self.attributes.get(ATTRIBUTE_SERVICE_SANDBOX, None) + if service_sandbox is None: + return None + # Constants are only allowed to refer to an enum here, so replace. + if isinstance(service_sandbox, Constant): + service_sandbox = service_sandbox.value + if not isinstance(service_sandbox, EnumValue): + raise Exception("ServiceSandbox attribute on %s must be an enum value." % + self.module.name) + return service_sandbox + + @property + def runtime_feature(self): + if not self.attributes: + return None + runtime_feature = self.attributes.get(ATTRIBUTE_RUNTIME_FEATURE, None) + if runtime_feature is None: + return None + if not isinstance(runtime_feature, Feature): + raise Exception("RuntimeFeature attribute on %s must be a feature." % + self.name) + return runtime_feature + + @property + def require_context(self): + if not self.attributes: + return None + return self.attributes.get(ATTRIBUTE_REQUIRE_CONTEXT, None) + + @property def stable(self): return self.attributes.get(ATTRIBUTE_STABLE, False) \ if self.attributes else False @@ -1199,11 +1449,18 @@ class Interface(ReferenceKind): prefix = self.module.GetNamespacePrefix() return '%s%s' % (prefix, self.mojom_name) + def _tuple(self): + return (self.mojom_name, self.methods, self.enums, self.constants, + self.attributes) + 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)) + return isinstance(rhs, Interface) and self._tuple() == rhs._tuple() + + def __lt__(self, rhs): + if not isinstance(self, type(rhs)): + return str(type(self)) < str(type(rhs)) + + return self._tuple() < rhs._tuple() @property def uuid(self): @@ -1224,7 +1481,7 @@ class Interface(ReferenceKind): class AssociatedInterface(ReferenceKind): - ReferenceKind.AddSharedProperty('kind') + Kind.AddSharedProperty('kind') def __init__(self, kind=None): if kind is not None: @@ -1249,7 +1506,7 @@ class AssociatedInterface(ReferenceKind): self.kind, rhs.kind) -class EnumField(object): +class EnumField: def __init__(self, mojom_name=None, value=None, @@ -1281,16 +1538,25 @@ class EnumField(object): rhs.attributes, rhs.numeric_value)) -class Enum(Kind): +class Enum(ValueKind): + Kind.AddSharedProperty('mojom_name') + Kind.AddSharedProperty('name') + Kind.AddSharedProperty('native_only') + Kind.AddSharedProperty('fields') + Kind.AddSharedProperty('attributes') + Kind.AddSharedProperty('min_value') + Kind.AddSharedProperty('max_value') + Kind.AddSharedProperty('default_field') + 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) + ValueKind.__init__(self, spec, False, module) + self.mojom_name = mojom_name + self.name = None + self.native_only = False self.fields = [] self.attributes = attributes self.min_value = None @@ -1300,8 +1566,7 @@ class Enum(Kind): 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}) + return GenericRepr(self, {'mojom_name': False, 'fields': False}) def Stylize(self, stylizer): self.name = stylizer.StylizeEnum(self.mojom_name) @@ -1327,14 +1592,14 @@ class Enum(Kind): return '%s%s' % (prefix, self.mojom_name) # 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: + def IsBackwardCompatible(self, rhs, checker): + """This enum is backward-compatible with rhs (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. + - rhs is [Extensible], and for every version defined by + rhs, this enum has the exact same set of valid numeric values. """ def buildVersionFieldMap(enum): @@ -1345,32 +1610,49 @@ class Enum(Kind): fields_by_min_version[field.min_version].add(field.numeric_value) return fields_by_min_version - old_fields = buildVersionFieldMap(older_enum) + old_fields = buildVersionFieldMap(rhs) new_fields = buildVersionFieldMap(self) - if new_fields.keys() != old_fields.keys() and not older_enum.extensible: - return False + if new_fields.keys() != old_fields.keys() and not rhs.extensible: + raise Exception("Non-extensible enum cannot be modified") 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 + if min_version not in new_fields: + raise Exception('New values added to an extensible enum ' + 'do not specify MinVersion: %s' % new_fields) + + if (new_fields[min_version] != valid_values): + if (len(new_fields[min_version]) < len(valid_values)): + raise Exception('Removing values for an existing MinVersion %s ' + 'is not allowed' % min_version) + raise Exception( + 'New values don\'t match old values' + 'for an existing MinVersion %s,' + ' please specify MinVersion equal to "Next version" ' + 'in the enum description' + ' for the following values:\n%s' % + (min_version, new_fields[min_version].difference(valid_values))) return True + def _tuple(self): + return (self.mojom_name, self.native_only, self.fields, self.attributes, + self.min_value, self.max_value, self.default_field) + 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, - self.default_field) == (rhs.mojom_name, rhs.native_only, - rhs.fields, rhs.attributes, rhs.min_value, - rhs.max_value, rhs.default_field)) + return isinstance(rhs, Enum) and self._tuple() == rhs._tuple() + + def __lt__(self, rhs): + if not isinstance(self, type(rhs)): + return str(type(self)) < str(type(rhs)) + + return self._tuple() < rhs._tuple() def __hash__(self): return id(self) -class Module(object): +class Module: def __init__(self, path=None, mojom_namespace=None, attributes=None): self.path = path self.mojom_namespace = mojom_namespace @@ -1379,24 +1661,26 @@ class Module(object): self.unions = [] self.interfaces = [] self.enums = [] + self.features = [] self.constants = [] - self.kinds = {} + self.kinds = OrderedDict() self.attributes = attributes self.imports = [] - self.imported_kinds = {} - self.metadata = {} + self.imported_kinds = OrderedDict() + self.metadata = OrderedDict() 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)) + return (isinstance(rhs, Module) + and (self.path, self.attributes, self.mojom_namespace, self.imports, + self.constants, self.enums, self.structs, self.unions, + self.interfaces, self.features) + == (rhs.path, rhs.attributes, rhs.mojom_namespace, rhs.imports, + rhs.constants, rhs.enums, rhs.structs, rhs.unions, + rhs.interfaces, rhs.features)) def __hash__(self): return id(self) @@ -1405,16 +1689,16 @@ class Module(object): 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 - }) + return GenericRepr( + self, { + 'path': False, + 'mojom_namespace': False, + 'attributes': False, + 'structs': False, + 'interfaces': False, + 'unions': False, + 'features': False, + }) def GetNamespacePrefix(self): return '%s.' % self.mojom_namespace if self.mojom_namespace else '' @@ -1434,6 +1718,11 @@ class Module(object): self.unions.append(union) return union + def AddFeature(self, mojom_name, attributes=None): + feature = Feature(mojom_name, self, attributes) + self.features.append(feature) + return feature + def Stylize(self, stylizer): self.namespace = stylizer.StylizeModule(self.mojom_namespace) for struct in self.structs: @@ -1446,12 +1735,14 @@ class Module(object): enum.Stylize(stylizer) for constant in self.constants: constant.Stylize(stylizer) + for feature in self.features: + feature.Stylize(stylizer) for imported_module in self.imports: imported_module.Stylize(stylizer) def Dump(self, f): - pickle.dump(self, f, 2) + pickle.dump(self, f) @classmethod def Load(cls, f): @@ -1461,15 +1752,15 @@ class Module(object): def IsBoolKind(kind): - return kind.spec == BOOL.spec + return kind.spec == BOOL.spec or kind.spec == NULLABLE_BOOL.spec def IsFloatKind(kind): - return kind.spec == FLOAT.spec + return kind.spec == FLOAT.spec or kind.spec == NULLABLE_FLOAT.spec def IsDoubleKind(kind): - return kind.spec == DOUBLE.spec + return kind.spec == DOUBLE.spec or kind.spec == NULLABLE_DOUBLE.spec def IsIntegralKind(kind): @@ -1477,7 +1768,14 @@ def IsIntegralKind(kind): 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) + or kind.spec == UINT64.spec or kind.spec == NULLABLE_BOOL.spec + or kind.spec == NULLABLE_INT8.spec or kind.spec == NULLABLE_INT16.spec + or kind.spec == NULLABLE_INT32.spec + or kind.spec == NULLABLE_INT64.spec + or kind.spec == NULLABLE_UINT8.spec + or kind.spec == NULLABLE_UINT16.spec + or kind.spec == NULLABLE_UINT32.spec + or kind.spec == NULLABLE_UINT64.spec) def IsStringKind(kind): @@ -1522,6 +1820,10 @@ def IsArrayKind(kind): return isinstance(kind, Array) +def IsFeatureKind(kind): + return isinstance(kind, Feature) + + def IsInterfaceKind(kind): return isinstance(kind, Interface) @@ -1558,12 +1860,16 @@ def IsEnumKind(kind): return isinstance(kind, Enum) +def IsValueKind(kind): + return isinstance(kind, ValueKind) + + def IsReferenceKind(kind): return isinstance(kind, ReferenceKind) def IsNullableKind(kind): - return IsReferenceKind(kind) and kind.is_nullable + return kind.is_nullable def IsMapKind(kind): @@ -1664,11 +1970,8 @@ def MethodPassesInterfaces(method): return _AnyMethodParameterRecursive(method, IsInterfaceKind) -def HasSyncMethods(interface): - for method in interface.methods: - if method.sync: - return True - return False +def GetSyncMethodOrdinals(interface): + return [method.ordinal for method in interface.methods if method.sync] def HasUninterruptableMethods(interface): @@ -1700,18 +2003,17 @@ def ContainsHandlesOrInterfaces(kind): checked.add(kind.spec) if IsStructKind(kind): return any(Check(field.kind) for field in kind.fields) - elif IsUnionKind(kind): + if IsUnionKind(kind): return any(Check(field.kind) for field in kind.fields) - elif IsAnyHandleKind(kind): + if IsAnyHandleKind(kind): return True - elif IsAnyInterfaceKind(kind): + if IsAnyInterfaceKind(kind): return True - elif IsArrayKind(kind): + if IsArrayKind(kind): return Check(kind.kind) - elif IsMapKind(kind): + if IsMapKind(kind): return Check(kind.key_kind) or Check(kind.value_kind) - else: - return False + return False return Check(kind) @@ -1738,21 +2040,20 @@ def ContainsNativeTypes(kind): checked.add(kind.spec) if IsEnumKind(kind): return kind.native_only - elif IsStructKind(kind): + if 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): + if IsUnionKind(kind): return any(Check(field.kind) for field in kind.fields) - elif IsInterfaceKind(kind): + if IsInterfaceKind(kind): return any(enum.native_only for enum in kind.enums) - elif IsArrayKind(kind): + if IsArrayKind(kind): return Check(kind.kind) - elif IsMapKind(kind): + if IsMapKind(kind): return Check(kind.key_kind) or Check(kind.value_kind) - else: - return False + return False return Check(kind) |