summaryrefslogtreecommitdiff
path: root/utils/ipc/mojo/public/tools/mojom/mojom
diff options
context:
space:
mode:
Diffstat (limited to 'utils/ipc/mojo/public/tools/mojom/mojom')
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn3
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/error.py2
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/fileutil.py3
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/fileutil_unittest.py7
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/generate/check.py26
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/generate/constant_resolver.py93
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py11
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/generate/generator_unittest.py9
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py787
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/generate/module_unittest.py2
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py151
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/generate/pack_unittest.py30
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py2
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py464
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/generate/translate_unittest.py82
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/parse/ast.py145
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/parse/ast_unittest.py12
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features.py21
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py155
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer.py8
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py10
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/parse/parser.py108
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py39
23 files changed, 1585 insertions, 585 deletions
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn b/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn
index 51facc0c..a0edf0eb 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
+# Copyright 2020 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -8,6 +8,7 @@ group("mojom") {
"error.py",
"fileutil.py",
"generate/__init__.py",
+ "generate/check.py",
"generate/generator.py",
"generate/module.py",
"generate/pack.py",
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/error.py b/utils/ipc/mojo/public/tools/mojom/mojom/error.py
index 8a1e03da..dd53b835 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/error.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/error.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/fileutil.py b/utils/ipc/mojo/public/tools/mojom/mojom/fileutil.py
index bf626f54..124f12c1 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/fileutil.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/fileutil.py
@@ -1,9 +1,8 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
+# Copyright 2015 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import errno
-import imp
import os.path
import sys
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/fileutil_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/fileutil_unittest.py
index ff5753a2..c93d2289 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/fileutil_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/fileutil_unittest.py
@@ -1,20 +1,17 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
+# Copyright 2015 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
import os.path
import shutil
-import sys
import tempfile
import unittest
from mojom import fileutil
-
class FileUtilTest(unittest.TestCase):
def testEnsureDirectoryExists(self):
- """Test that EnsureDirectoryExists fuctions correctly."""
+ """Test that EnsureDirectoryExists functions correctly."""
temp_dir = tempfile.mkdtemp()
try:
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/check.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/check.py
new file mode 100644
index 00000000..1efe2022
--- /dev/null
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/check.py
@@ -0,0 +1,26 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Code shared by the various pre-generation mojom checkers."""
+
+
+class CheckException(Exception):
+ def __init__(self, module, message):
+ self.module = module
+ self.message = message
+ super().__init__(self.message)
+
+ def __str__(self):
+ return "Failed mojo pre-generation check for {}:\n{}".format(
+ self.module.path, self.message)
+
+
+class Check:
+ def __init__(self, module):
+ self.module = module
+
+ def CheckModule(self):
+ """ Subclass should return True if its Checks pass, and throw an
+ exception otherwise. CheckModule will be called immediately before
+ mojom.generate.Generator.GenerateFiles()"""
+ raise NotImplementedError("Subclasses must override/implement this method")
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/constant_resolver.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/constant_resolver.py
deleted file mode 100644
index 0dfd996e..00000000
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/constant_resolver.py
+++ /dev/null
@@ -1,93 +0,0 @@
-# Copyright 2015 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.
-"""Resolves the values used for constants and enums."""
-
-from itertools import ifilter
-
-from mojom.generate import module as mojom
-
-
-def ResolveConstants(module, expression_to_text):
- in_progress = set()
- computed = set()
-
- def GetResolvedValue(named_value):
- assert isinstance(named_value, (mojom.EnumValue, mojom.ConstantValue))
- if isinstance(named_value, mojom.EnumValue):
- field = next(
- ifilter(lambda field: field.name == named_value.name,
- named_value.enum.fields), None)
- if not field:
- raise RuntimeError(
- 'Unable to get computed value for field %s of enum %s' %
- (named_value.name, named_value.enum.name))
- if field not in computed:
- ResolveEnum(named_value.enum)
- return field.resolved_value
- else:
- ResolveConstant(named_value.constant)
- named_value.resolved_value = named_value.constant.resolved_value
- return named_value.resolved_value
-
- def ResolveConstant(constant):
- if constant in computed:
- return
- if constant in in_progress:
- raise RuntimeError('Circular dependency for constant: %s' % constant.name)
- in_progress.add(constant)
- if isinstance(constant.value, (mojom.EnumValue, mojom.ConstantValue)):
- resolved_value = GetResolvedValue(constant.value)
- else:
- resolved_value = expression_to_text(constant.value)
- constant.resolved_value = resolved_value
- in_progress.remove(constant)
- computed.add(constant)
-
- def ResolveEnum(enum):
- def ResolveEnumField(enum, field, default_value):
- if field in computed:
- return
- if field in in_progress:
- raise RuntimeError('Circular dependency for enum: %s' % enum.name)
- in_progress.add(field)
- if field.value:
- if isinstance(field.value, mojom.EnumValue):
- resolved_value = GetResolvedValue(field.value)
- elif isinstance(field.value, str):
- resolved_value = int(field.value, 0)
- else:
- raise RuntimeError('Unexpected value: %s' % field.value)
- else:
- resolved_value = default_value
- field.resolved_value = resolved_value
- in_progress.remove(field)
- computed.add(field)
-
- current_value = 0
- for field in enum.fields:
- ResolveEnumField(enum, field, current_value)
- current_value = field.resolved_value + 1
-
- for constant in module.constants:
- ResolveConstant(constant)
-
- for enum in module.enums:
- ResolveEnum(enum)
-
- for struct in module.structs:
- for constant in struct.constants:
- ResolveConstant(constant)
- for enum in struct.enums:
- ResolveEnum(enum)
- for field in struct.fields:
- if isinstance(field.default, (mojom.ConstantValue, mojom.EnumValue)):
- field.default.resolved_value = GetResolvedValue(field.default)
-
- for interface in module.interfaces:
- for constant in interface.constants:
- ResolveConstant(constant)
- for enum in interface.enums:
- ResolveEnum(enum)
-
- return module
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 4a1c73fc..96fe3a2d 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.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.
"""Code shared by the various language-specific code generators."""
@@ -97,7 +97,7 @@ def ToLowerSnakeCase(identifier):
return _ToSnakeCase(identifier, upper=False)
-class Stylizer(object):
+class Stylizer:
"""Stylizers specify naming rules to map mojom names to names in generated
code. For example, if you would like method_name in mojom to be mapped to
MethodName in the generated code, you need to define a subclass of Stylizer
@@ -130,6 +130,9 @@ class Stylizer(object):
def StylizeEnum(self, mojom_name):
return mojom_name
+ def StylizeFeature(self, mojom_name):
+ return mojom_name
+
def StylizeModule(self, mojom_namespace):
return mojom_namespace
@@ -233,7 +236,7 @@ def AddComputedData(module):
_AddInterfaceComputedData(interface)
-class Generator(object):
+class Generator:
# Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all
# files to stdout.
def __init__(self,
@@ -243,7 +246,6 @@ class Generator(object):
variant=None,
bytecode_path=None,
for_blink=False,
- js_bindings_mode="new",
js_generate_struct_deserializers=False,
export_attribute=None,
export_header=None,
@@ -262,7 +264,6 @@ class Generator(object):
self.variant = variant
self.bytecode_path = bytecode_path
self.for_blink = for_blink
- self.js_bindings_mode = js_bindings_mode
self.js_generate_struct_deserializers = js_generate_struct_deserializers
self.export_attribute = export_attribute
self.export_header = export_header
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator_unittest.py
index 32c884a8..7143e07c 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator_unittest.py
@@ -1,13 +1,12 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
+import importlib.util
import os.path
import sys
import unittest
-
def _GetDirAbove(dirname):
"""Returns the directory "above" this file containing |dirname| (which must
also be "above" this file)."""
@@ -20,12 +19,11 @@ def _GetDirAbove(dirname):
try:
- imp.find_module("mojom")
+ importlib.util.find_spec("mojom")
except ImportError:
sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
from mojom.generate import generator
-
class StringManipulationTest(unittest.TestCase):
"""generator contains some string utilities, this tests only those."""
@@ -69,6 +67,5 @@ class StringManipulationTest(unittest.TestCase):
self.assertEquals("SNAKE_D3D11_CASE",
generator.ToUpperSnakeCase("snakeD3d11Case"))
-
if __name__ == "__main__":
unittest.main()
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)
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/module_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module_unittest.py
index e8fd4936..2a4e852c 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/module_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module_unittest.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py
index 88b77c98..61240426 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py
@@ -1,7 +1,8 @@
-# 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.
+import copy
from mojom.generate import module as mojom
# This module provides a mechanism for determining the packed order and offsets
@@ -15,7 +16,7 @@ from mojom.generate import module as mojom
HEADER_SIZE = 8
-class PackedField(object):
+class PackedField:
kind_to_size = {
mojom.BOOL: 1,
mojom.INT8: 1,
@@ -75,18 +76,55 @@ class PackedField(object):
return 8
return cls.GetSizeForKind(kind)
- def __init__(self, field, index, ordinal):
+ def __init__(self,
+ field,
+ index,
+ ordinal,
+ original_field=None,
+ sub_ordinal=None,
+ linked_value_packed_field=None):
"""
Args:
field: the original field.
index: the position of the original field in the struct.
ordinal: the ordinal of the field for serialization.
+ original_field: See below.
+ sub_ordinal: See below.
+ linked_value_packed_field: See below.
+
+ original_field, sub_ordinal, and linked_value_packed_field are used to
+ support nullable ValueKind fields. For legacy reasons, nullable ValueKind
+ fields actually generate two PackedFields. This allows:
+
+ - backwards compatibility prior to Mojo support for nullable ValueKinds.
+ - correct packing of fields for the aforementioned backwards compatibility.
+
+ When translating Fields to PackedFields, the original field is turned into
+ two PackedFields: the first PackedField always has type mojom.BOOL, while
+ the second PackedField has the non-nullable version of the field's kind.
+
+ When constructing these PackedFields, original_field references the field
+ as defined in the mojom; the name as defined in the mojom will be used for
+ all layers above the wire/data layer.
+
+ sub_ordinal is used to sort the two PackedFields correctly with respect to
+ each other: the first mojom.BOOL field always has sub_ordinal 0, while the
+ second field always has sub_ordinal 1.
+
+ Finally, linked_value_packed_field is used by the serialization and
+ deserialization helpers, which generally just iterate over a PackedStruct's
+ PackedField's in ordinal order. This allows the helpers to easily reference
+ any related PackedFields rather than having to lookup related PackedFields
+ by index while iterating.
"""
self.field = field
self.index = index
self.ordinal = ordinal
- self.size = self.GetSizeForKind(field.kind)
- self.alignment = self.GetAlignmentForKind(field.kind)
+ self.original_field = original_field
+ self.sub_ordinal = sub_ordinal
+ self.linked_value_packed_field = linked_value_packed_field
+ self.size = self.GetSizeForKind(self.field.kind)
+ self.alignment = self.GetAlignmentForKind(self.field.kind)
self.offset = None
self.bit = None
self.min_version = None
@@ -120,7 +158,33 @@ def GetPayloadSizeUpToField(field):
return offset + pad
-class PackedStruct(object):
+def IsNullableValueKindPackedField(field):
+ """Returns true if `field` is derived from a nullable ValueKind field.
+
+ Nullable ValueKind fields often require special handling in the bindings due
+ to the way the implementation is constrained for wire compatibility.
+ """
+ assert isinstance(field, PackedField)
+ return field.sub_ordinal is not None
+
+
+def IsPrimaryNullableValueKindPackedField(field):
+ """Returns true if `field` is derived from a nullable ValueKind mojom field
+ and is the "primary" field.
+
+ The primary field is a bool PackedField that controls if the field should be
+ considered as present or not; it will have a reference to the PackedField that
+ holds the actual value representation if considered present.
+
+ Bindings code that translates between the wire protocol and the higher layers
+ can use this to simplify mapping multiple PackedFields to the single field
+ that is logically exposed to bindings consumers.
+ """
+ assert isinstance(field, PackedField)
+ return field.linked_value_packed_field is not None
+
+
+class PackedStruct:
def __init__(self, struct):
self.struct = struct
# |packed_fields| contains all the fields, in increasing offset order.
@@ -139,9 +203,41 @@ class PackedStruct(object):
for index, field in enumerate(struct.fields):
if field.ordinal is not None:
ordinal = field.ordinal
- src_fields.append(PackedField(field, index, ordinal))
+ # Nullable value types are a bit weird: they generate two PackedFields
+ # despite being a single ValueKind. This is for wire compatibility to
+ # ease the transition from legacy mojom syntax where nullable value types
+ # were not supported.
+ if isinstance(field.kind, mojom.ValueKind) and field.kind.is_nullable:
+ # The suffixes intentionally use Unicode codepoints which are considered
+ # valid C++/Java/JavaScript identifiers, yet are unlikely to be used in
+ # actual user code.
+ has_value_field = copy.copy(field)
+ has_value_field.name = f'{field.mojom_name}_$flag'
+ has_value_field.kind = mojom.BOOL
+
+ value_field = copy.copy(field)
+ value_field.name = f'{field.mojom_name}_$value'
+ value_field.kind = field.kind.MakeUnnullableKind()
+
+ value_packed_field = PackedField(value_field,
+ index,
+ ordinal,
+ original_field=field,
+ sub_ordinal=1,
+ linked_value_packed_field=None)
+ has_value_packed_field = PackedField(
+ has_value_field,
+ index,
+ ordinal,
+ original_field=field,
+ sub_ordinal=0,
+ linked_value_packed_field=value_packed_field)
+ src_fields.append(has_value_packed_field)
+ src_fields.append(value_packed_field)
+ else:
+ src_fields.append(PackedField(field, index, ordinal))
ordinal += 1
- src_fields.sort(key=lambda field: field.ordinal)
+ src_fields.sort(key=lambda field: (field.ordinal, field.sub_ordinal))
# Set |min_version| for each field.
next_min_version = 0
@@ -156,10 +252,11 @@ class PackedStruct(object):
if (packed_field.min_version != 0
and mojom.IsReferenceKind(packed_field.field.kind)
and not packed_field.field.kind.is_nullable):
- raise Exception("Non-nullable fields are only allowed in version 0 of "
- "a struct. %s.%s is defined with [MinVersion=%d]." %
- (self.struct.name, packed_field.field.name,
- packed_field.min_version))
+ raise Exception(
+ "Non-nullable reference fields are only allowed in version 0 of a "
+ "struct. %s.%s is defined with [MinVersion=%d]." %
+ (self.struct.name, packed_field.field.name,
+ packed_field.min_version))
src_field = src_fields[0]
src_field.offset = 0
@@ -186,7 +283,7 @@ class PackedStruct(object):
dst_fields.append(src_field)
-class ByteInfo(object):
+class ByteInfo:
def __init__(self):
self.is_padding = False
self.packed_fields = []
@@ -214,10 +311,11 @@ def GetByteLayout(packed_struct):
return byte_info
-class VersionInfo(object):
- def __init__(self, version, num_fields, num_bytes):
+class VersionInfo:
+ def __init__(self, version, num_fields, num_packed_fields, num_bytes):
self.version = version
self.num_fields = num_fields
+ self.num_packed_fields = num_packed_fields
self.num_bytes = num_bytes
@@ -235,24 +333,35 @@ def GetVersionInfo(packed_struct):
versions = []
last_version = 0
last_num_fields = 0
+ last_num_packed_fields = 0
last_payload_size = 0
for packed_field in packed_struct.packed_fields_in_ordinal_order:
if packed_field.min_version != last_version:
versions.append(
- VersionInfo(last_version, last_num_fields,
+ VersionInfo(last_version, last_num_fields, last_num_packed_fields,
last_payload_size + HEADER_SIZE))
last_version = packed_field.min_version
- last_num_fields += 1
+ # Nullable numeric fields (e.g. `int32?`) expand to two packed fields, so to
+ # avoid double-counting, only increment if the field is:
+ # - not used for representing a nullable value kind field, or
+ # - the primary field representing the nullable value kind field.
+ last_num_fields += 1 if (
+ not IsNullableValueKindPackedField(packed_field)
+ or IsPrimaryNullableValueKindPackedField(packed_field)) else 0
+
+ last_num_packed_fields += 1
+
# The fields are iterated in ordinal order here. However, the size of a
# version is determined by the last field of that version in pack order,
# instead of ordinal order. Therefore, we need to calculate the max value.
- last_payload_size = max(
- GetPayloadSizeUpToField(packed_field), last_payload_size)
+ last_payload_size = max(GetPayloadSizeUpToField(packed_field),
+ last_payload_size)
- assert len(versions) == 0 or last_num_fields != versions[-1].num_fields
+ assert len(
+ versions) == 0 or last_num_packed_fields != versions[-1].num_packed_fields
versions.append(
- VersionInfo(last_version, last_num_fields,
+ VersionInfo(last_version, last_num_fields, last_num_packed_fields,
last_payload_size + HEADER_SIZE))
return versions
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack_unittest.py
index 98c705ad..7d8e4e01 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack_unittest.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.
@@ -205,6 +205,34 @@ class PackTest(unittest.TestCase):
self.assertEqual(4, versions[2].num_fields)
self.assertEqual(32, versions[2].num_bytes)
+ def testGetVersionInfoPackedStruct(self):
+ """Tests that pack.GetVersionInfo() correctly sets version, num_fields,
+ and num_packed_fields for a packed struct.
+ """
+ struct = mojom.Struct('test')
+ struct.AddField('field_0', mojom.BOOL, ordinal=0)
+ struct.AddField('field_1',
+ mojom.NULLABLE_BOOL,
+ ordinal=1,
+ attributes={'MinVersion': 1})
+ struct.AddField('field_2',
+ mojom.NULLABLE_BOOL,
+ ordinal=2,
+ attributes={'MinVersion': 2})
+ ps = pack.PackedStruct(struct)
+ versions = pack.GetVersionInfo(ps)
+
+ self.assertEqual(3, len(versions))
+ self.assertEqual(0, versions[0].version)
+ self.assertEqual(1, versions[1].version)
+ self.assertEqual(2, versions[2].version)
+ self.assertEqual(1, versions[0].num_fields)
+ self.assertEqual(2, versions[1].num_fields)
+ self.assertEqual(3, versions[2].num_fields)
+ self.assertEqual(1, versions[0].num_packed_fields)
+ self.assertEqual(3, versions[1].num_packed_fields)
+ self.assertEqual(5, versions[2].num_packed_fields)
+
def testInterfaceAlignment(self):
"""Tests that interfaces are aligned on 4-byte boundaries, although the size
of an interface is 8 bytes.
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 0da90058..807e2a4f 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
@@ -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.
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 7580b780..83bb297f 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.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.
"""Convert parse tree to AST.
@@ -12,17 +12,294 @@ already been parsed and converted to ASTs before.
import itertools
import os
import re
-import sys
+from collections import OrderedDict
from mojom.generate import generator
from mojom.generate import module as mojom
from mojom.parse import ast
-def _IsStrOrUnicode(x):
- if sys.version_info[0] < 3:
- return isinstance(x, (unicode, str))
- return isinstance(x, str)
+is_running_backwards_compatibility_check_hack = False
+
+### DO NOT ADD ENTRIES TO THIS LIST. ###
+_EXTENSIBLE_ENUMS_MISSING_DEFAULT = (
+ 'x:arc.keymaster.mojom.Algorithm',
+ 'x:arc.keymaster.mojom.Digest',
+ 'x:arc.keymaster.mojom.SignatureResult',
+ 'x:arc.mojom.AccessibilityActionType',
+ 'x:arc.mojom.AccessibilityBooleanProperty',
+ 'x:arc.mojom.AccessibilityEventIntListProperty',
+ 'x:arc.mojom.AccessibilityEventIntProperty',
+ 'x:arc.mojom.AccessibilityEventStringProperty',
+ 'x:arc.mojom.AccessibilityEventType',
+ 'x:arc.mojom.AccessibilityFilterType',
+ 'x:arc.mojom.AccessibilityIntListProperty',
+ 'x:arc.mojom.AccessibilityIntProperty',
+ 'x:arc.mojom.AccessibilityLiveRegionType',
+ 'x:arc.mojom.AccessibilityNotificationStateType',
+ 'x:arc.mojom.AccessibilityRangeType',
+ 'x:arc.mojom.AccessibilitySelectionMode',
+ 'x:arc.mojom.AccessibilityStringListProperty',
+ 'x:arc.mojom.AccessibilityStringProperty',
+ 'x:arc.mojom.AccessibilityWindowBooleanProperty',
+ 'x:arc.mojom.AccessibilityWindowIntListProperty',
+ 'x:arc.mojom.AccessibilityWindowIntProperty',
+ 'x:arc.mojom.AccessibilityWindowStringProperty',
+ 'x:arc.mojom.AccessibilityWindowType',
+ 'x:arc.mojom.AccountCheckStatus',
+ 'x:arc.mojom.AccountUpdateType',
+ 'x:arc.mojom.ActionType',
+ 'x:arc.mojom.Algorithm',
+ 'x:arc.mojom.AndroidIdSource',
+ 'x:arc.mojom.AnrSource',
+ 'x:arc.mojom.AnrType',
+ 'x:arc.mojom.AppDiscoveryRequestState',
+ 'x:arc.mojom.AppKillType',
+ 'x:arc.mojom.AppPermission',
+ 'x:arc.mojom.AppPermissionGroup',
+ 'x:arc.mojom.AppReinstallState',
+ 'x:arc.mojom.AppShortcutItemType',
+ 'x:arc.mojom.ArcAuthCodeStatus',
+ 'x:arc.mojom.ArcClipboardDragDropEvent',
+ 'x:arc.mojom.ArcCorePriAbiMigEvent',
+ 'x:arc.mojom.ArcDnsQuery',
+ 'x:arc.mojom.ArcImageCopyPasteCompatAction',
+ 'x:arc.mojom.ArcNetworkError',
+ 'x:arc.mojom.ArcNetworkEvent',
+ 'x:arc.mojom.ArcNotificationEvent',
+ 'x:arc.mojom.ArcNotificationExpandState',
+ 'x:arc.mojom.ArcNotificationPriority',
+ 'x:arc.mojom.ArcNotificationRemoteInputState',
+ 'x:arc.mojom.ArcNotificationShownContents',
+ 'x:arc.mojom.ArcNotificationStyle',
+ 'x:arc.mojom.ArcNotificationType',
+ 'x:arc.mojom.ArcPipEvent',
+ 'x:arc.mojom.ArcResizeLockState',
+ 'x:arc.mojom.ArcSignInSuccess',
+ 'x:arc.mojom.ArcTimerResult',
+ 'x:arc.mojom.AudioSwitch',
+ 'x:arc.mojom.BluetoothAclState',
+ 'x:arc.mojom.BluetoothAdapterState',
+ 'x:arc.mojom.BluetoothAdvertisingDataType',
+ 'x:arc.mojom.BluetoothBondState',
+ 'x:arc.mojom.BluetoothDeviceType',
+ 'x:arc.mojom.BluetoothDiscoveryState',
+ 'x:arc.mojom.BluetoothGattDBAttributeType',
+ 'x:arc.mojom.BluetoothGattStatus',
+ 'x:arc.mojom.BluetoothPropertyType',
+ 'x:arc.mojom.BluetoothScanMode',
+ 'x:arc.mojom.BluetoothSdpAttributeType',
+ 'x:arc.mojom.BluetoothSocketType',
+ 'x:arc.mojom.BluetoothStatus',
+ 'x:arc.mojom.BootType',
+ 'x:arc.mojom.CaptionTextShadowType',
+ 'x:arc.mojom.ChangeType',
+ 'x:arc.mojom.ChromeAccountType',
+ 'x:arc.mojom.ChromeApp',
+ 'x:arc.mojom.ChromePage',
+ 'x:arc.mojom.ClockId',
+ 'x:arc.mojom.CloudProvisionFlowError',
+ 'x:arc.mojom.CommandResultType',
+ 'x:arc.mojom.CompanionLibApiId',
+ 'x:arc.mojom.ConnectionStateType',
+ 'x:arc.mojom.ContentChangeType',
+ 'x:arc.mojom.CpuRestrictionState',
+ 'x:arc.mojom.CursorCoordinateSpace',
+ 'x:arc.mojom.DataRestoreStatus',
+ 'x:arc.mojom.DecoderStatus',
+ 'x:arc.mojom.DeviceType',
+ 'x:arc.mojom.Digest',
+ 'x:arc.mojom.DisplayWakeLockType',
+ 'x:arc.mojom.EapMethod',
+ 'x:arc.mojom.EapPhase2Method',
+ 'x:arc.mojom.FileSelectorEventType',
+ 'x:arc.mojom.GMSCheckInError',
+ 'x:arc.mojom.GMSSignInError',
+ 'x:arc.mojom.GeneralSignInError',
+ 'x:arc.mojom.GetNetworksRequestType',
+ 'x:arc.mojom.HalPixelFormat',
+ 'x:arc.mojom.IPAddressType',
+ 'x:arc.mojom.InstallErrorReason',
+ 'x:arc.mojom.KeyFormat',
+ 'x:arc.mojom.KeyManagement',
+ 'x:arc.mojom.KeyPurpose',
+ 'x:arc.mojom.KeymasterError',
+ 'x:arc.mojom.MainAccountHashMigrationStatus',
+ 'x:arc.mojom.MainAccountResolutionStatus',
+ 'x:arc.mojom.ManagementChangeStatus',
+ 'x:arc.mojom.ManagementState',
+ 'x:arc.mojom.MessageCenterVisibility',
+ 'x:arc.mojom.MetricsType',
+ 'x:arc.mojom.MountEvent',
+ 'x:arc.mojom.NativeBridgeType',
+ 'x:arc.mojom.NetworkResult',
+ 'x:arc.mojom.NetworkType',
+ 'x:arc.mojom.OemCryptoAlgorithm',
+ 'x:arc.mojom.OemCryptoCipherMode',
+ 'x:arc.mojom.OemCryptoHdcpCapability',
+ 'x:arc.mojom.OemCryptoLicenseType',
+ 'x:arc.mojom.OemCryptoPrivateKey',
+ 'x:arc.mojom.OemCryptoProvisioningMethod',
+ 'x:arc.mojom.OemCryptoResult',
+ 'x:arc.mojom.OemCryptoRsaPaddingScheme',
+ 'x:arc.mojom.OemCryptoUsageEntryStatus',
+ 'x:arc.mojom.Padding',
+ 'x:arc.mojom.PaiFlowState',
+ 'x:arc.mojom.PatternType',
+ 'x:arc.mojom.PressureLevel',
+ 'x:arc.mojom.PrintColorMode',
+ 'x:arc.mojom.PrintContentType',
+ 'x:arc.mojom.PrintDuplexMode',
+ 'x:arc.mojom.PrinterStatus',
+ 'x:arc.mojom.ProcessState',
+ 'x:arc.mojom.PurchaseState',
+ 'x:arc.mojom.ReauthReason',
+ 'x:arc.mojom.ScaleFactor',
+ 'x:arc.mojom.SecurityType',
+ 'x:arc.mojom.SegmentStyle',
+ 'x:arc.mojom.SelectFilesActionType',
+ 'x:arc.mojom.SetNativeChromeVoxResponse',
+ 'x:arc.mojom.ShowPackageInfoPage',
+ 'x:arc.mojom.SpanType',
+ 'x:arc.mojom.SupportedLinkChangeSource',
+ 'x:arc.mojom.TetheringClientState',
+ 'x:arc.mojom.TextInputType',
+ 'x:arc.mojom.TtsEventType',
+ 'x:arc.mojom.VideoCodecProfile',
+ 'x:arc.mojom.VideoDecodeAccelerator.Result',
+ 'x:arc.mojom.VideoEncodeAccelerator.Error',
+ 'x:arc.mojom.VideoFrameStorageType',
+ 'x:arc.mojom.VideoPixelFormat',
+ 'x:arc.mojom.WakefulnessMode',
+ 'x:arc.mojom.WebApkInstallResult',
+ 'x:ash.ime.mojom.InputFieldType',
+ 'x:ash.ime.mojom.PersonalizationMode',
+ 'x:ash.language.mojom.FeatureId',
+ 'x:blink.mojom.ScrollRestorationType',
+ 'x:chromeos.cdm.mojom.CdmKeyStatus',
+ 'x:chromeos.cdm.mojom.CdmMessageType',
+ 'x:chromeos.cdm.mojom.CdmSessionType',
+ 'x:chromeos.cdm.mojom.DecryptStatus',
+ 'x:chromeos.cdm.mojom.EmeInitDataType',
+ 'x:chromeos.cdm.mojom.EncryptionScheme',
+ 'x:chromeos.cdm.mojom.HdcpVersion',
+ 'x:chromeos.cdm.mojom.OutputProtection.LinkType',
+ 'x:chromeos.cdm.mojom.OutputProtection.ProtectionType',
+ 'x:chromeos.cdm.mojom.PromiseException',
+ 'x:chromeos.cfm.mojom.EnqueuePriority',
+ 'x:chromeos.cfm.mojom.LoggerErrorCode',
+ 'x:chromeos.cfm.mojom.LoggerState',
+ 'x:chromeos.cros_healthd.mojom.CryptoAlgorithm',
+ 'x:chromeos.cros_healthd.mojom.EncryptionState',
+ 'x:chromeos.machine_learning.mojom.AnnotationUsecase',
+ 'x:chromeos.machine_learning.mojom.BuiltinModelId',
+ 'x:chromeos.machine_learning.mojom.CreateGraphExecutorResult',
+ 'x:chromeos.machine_learning.mojom.DocumentScannerResultStatus',
+ 'x:chromeos.machine_learning.mojom.EndpointReason',
+ 'x:chromeos.machine_learning.mojom.EndpointerType',
+ 'x:chromeos.machine_learning.mojom.ExecuteResult',
+ 'x:chromeos.machine_learning.mojom.GrammarCheckerResult.Status',
+ 'x:chromeos.machine_learning.mojom.HandwritingRecognizerResult.Status',
+ 'x:chromeos.machine_learning.mojom.LoadHandwritingModelResult',
+ 'x:chromeos.machine_learning.mojom.LoadModelResult',
+ 'x:chromeos.machine_learning.mojom.Rotation',
+ 'x:chromeos.network_config.mojom.ConnectionStateType',
+ 'x:chromeos.network_config.mojom.DeviceStateType',
+ 'x:chromeos.network_config.mojom.IPConfigType',
+ 'x:chromeos.network_config.mojom.NetworkType',
+ 'x:chromeos.network_config.mojom.OncSource',
+ 'x:chromeos.network_config.mojom.PolicySource',
+ 'x:chromeos.network_config.mojom.PortalState',
+ 'x:chromeos.wilco_dtc_supportd.mojom.WilcoDtcSupportdEvent',
+ 'x:chromeos.wilco_dtc_supportd.mojom.WilcoDtcSupportdWebRequestHttpMethod',
+ 'x:chromeos.wilco_dtc_supportd.mojom.WilcoDtcSupportdWebRequestStatus',
+ 'x:cros.mojom.CameraClientType',
+ 'x:cros.mojom.CameraMetadataSectionStart',
+ 'x:cros.mojom.CameraMetadataTag',
+ 'x:cros.mojom.HalPixelFormat',
+ 'x:crosapi.mojom.AllowedPaths',
+ 'x:crosapi.mojom.BrowserAppInstanceType',
+ 'x:crosapi.mojom.CreationResult',
+ 'x:crosapi.mojom.DeviceAccessResultCode',
+ 'x:crosapi.mojom.DeviceMode',
+ 'x:crosapi.mojom.DlpRestrictionLevel',
+ 'x:crosapi.mojom.ExoImeSupport',
+ 'x:crosapi.mojom.FullscreenVisibility',
+ 'x:crosapi.mojom.GoogleServiceAuthError.State',
+ 'x:crosapi.mojom.IsInstallableResult',
+ 'x:crosapi.mojom.KeyTag',
+ 'x:crosapi.mojom.KeystoreSigningAlgorithmName',
+ 'x:crosapi.mojom.KeystoreType',
+ 'x:crosapi.mojom.LacrosFeedbackSource',
+ 'x:crosapi.mojom.MemoryPressureLevel',
+ 'x:crosapi.mojom.MetricsReportingManaged',
+ 'x:crosapi.mojom.NotificationType',
+ 'x:crosapi.mojom.OndeviceHandwritingSupport',
+ 'x:crosapi.mojom.OpenResult',
+ 'x:crosapi.mojom.PolicyDomain',
+ 'x:crosapi.mojom.RegistrationCodeType',
+ 'x:crosapi.mojom.ScaleFactor',
+ 'x:crosapi.mojom.SearchResult.OptionalBool',
+ 'x:crosapi.mojom.SelectFileDialogType',
+ 'x:crosapi.mojom.SelectFileResult',
+ 'x:crosapi.mojom.SharesheetResult',
+ 'x:crosapi.mojom.TouchEventType',
+ 'x:crosapi.mojom.VideoRotation',
+ 'x:crosapi.mojom.WallpaperLayout',
+ 'x:crosapi.mojom.WebAppInstallResultCode',
+ 'x:crosapi.mojom.WebAppUninstallResultCode',
+ 'x:device.mojom.HidBusType',
+ 'x:device.mojom.WakeLockReason',
+ 'x:device.mojom.WakeLockType',
+ 'x:drivefs.mojom.DialogReason.Type',
+ 'x:drivefs.mojom.DriveError.Type',
+ 'x:drivefs.mojom.DriveFsDelegate.ExtensionConnectionStatus',
+ 'x:drivefs.mojom.FileMetadata.CanPinStatus',
+ 'x:drivefs.mojom.FileMetadata.Type',
+ 'x:drivefs.mojom.ItemEventReason',
+ 'x:drivefs.mojom.MirrorPathStatus',
+ 'x:drivefs.mojom.MirrorSyncStatus',
+ 'x:drivefs.mojom.QueryParameters.SortField',
+ 'x:fuzz.mojom.FuzzEnum',
+ 'x:media.mojom.FillLightMode',
+ 'x:media.mojom.MeteringMode',
+ 'x:media.mojom.PowerLineFrequency',
+ 'x:media.mojom.RedEyeReduction',
+ 'x:media.mojom.ResolutionChangePolicy',
+ 'x:media.mojom.VideoCaptureApi',
+ 'x:media.mojom.VideoCaptureBufferType',
+ 'x:media.mojom.VideoCaptureError',
+ 'x:media.mojom.VideoCaptureFrameDropReason',
+ 'x:media.mojom.VideoCapturePixelFormat',
+ 'x:media.mojom.VideoCaptureTransportType',
+ 'x:media.mojom.VideoFacingMode',
+ 'x:media_session.mojom.AudioFocusType',
+ 'x:media_session.mojom.CameraState',
+ 'x:media_session.mojom.EnforcementMode',
+ 'x:media_session.mojom.MediaAudioVideoState',
+ 'x:media_session.mojom.MediaImageBitmapColorType',
+ 'x:media_session.mojom.MediaPictureInPictureState',
+ 'x:media_session.mojom.MediaPlaybackState',
+ 'x:media_session.mojom.MediaSession.SuspendType',
+ 'x:media_session.mojom.MediaSessionAction',
+ 'x:media_session.mojom.MediaSessionImageType',
+ 'x:media_session.mojom.MediaSessionInfo.SessionState',
+ 'x:media_session.mojom.MicrophoneState',
+ 'x:ml.model_loader.mojom.ComputeResult',
+ 'x:ml.model_loader.mojom.CreateModelLoaderResult',
+ 'x:ml.model_loader.mojom.LoadModelResult',
+ 'x:mojo.test.AnExtensibleEnum',
+ 'x:mojo.test.EnumB',
+ 'x:mojo.test.ExtensibleEmptyEnum',
+ 'x:mojo.test.enum_default_unittest.mojom.ExtensibleEnumWithoutDefault',
+ 'x:network.mojom.WebSandboxFlags',
+ 'x:payments.mojom.BillingResponseCode',
+ 'x:payments.mojom.CreateDigitalGoodsResponseCode',
+ 'x:payments.mojom.ItemType',
+ 'x:printing.mojom.PrinterType',
+ 'x:ui.mojom.KeyboardCode',
+)
+### DO NOT ADD ENTRIES TO THIS LIST. ###
def _DuplicateName(values):
@@ -98,12 +375,6 @@ def _MapKind(kind):
}
if kind.endswith('?'):
base_kind = _MapKind(kind[0:-1])
- # NOTE: This doesn't rule out enum types. Those will be detected later, when
- # cross-reference is established.
- reference_kinds = ('m', 's', 'h', 'a', 'r', 'x', 'asso', 'rmt', 'rcv',
- 'rma', 'rca')
- if re.split('[^a-z]', base_kind, 1)[0] not in reference_kinds:
- raise Exception('A type (spec "%s") cannot be made nullable' % base_kind)
return '?' + base_kind
if kind.endswith('}'):
lbracket = kind.rfind('{')
@@ -113,8 +384,6 @@ def _MapKind(kind):
lbracket = kind.rfind('[')
typename = kind[0:lbracket]
return 'a' + kind[lbracket + 1:-1] + ':' + _MapKind(typename)
- if kind.endswith('&'):
- return 'r:' + _MapKind(kind[0:-1])
if kind.startswith('asso<'):
assert kind.endswith('>')
return 'asso:' + _MapKind(kind[5:-1])
@@ -135,13 +404,45 @@ def _MapKind(kind):
return 'x:' + kind
-def _AttributeListToDict(attribute_list):
+def _MapAttributeValue(module, kind, value):
+ # True/False/None
+ if value is None:
+ return value
+ if not isinstance(value, str):
+ return value
+ # Is the attribute value the name of a feature?
+ try:
+ # Features cannot be nested in other types, so lookup in the global scope.
+ trial = _LookupKind(module.kinds, 'x:' + value,
+ _GetScopeForKind(module, kind))
+ if isinstance(trial, mojom.Feature):
+ return trial
+ except ValueError:
+ pass
+ # Is the attribute value a constant or enum value?
+ try:
+ trial = _LookupValue(module, None, None, ('IDENTIFIER', value))
+ if isinstance(trial, mojom.ConstantValue):
+ return trial.constant
+ if isinstance(trial, mojom.EnumValue):
+ return trial
+ except ValueError:
+ pass
+ # If not a referenceable mojo type - return as a string.
+ return value
+
+
+def _AttributeListToDict(module, kind, attribute_list):
if attribute_list is None:
return None
assert isinstance(attribute_list, ast.AttributeList)
- # TODO(vtl): Check for duplicate keys here.
- return dict(
- [(attribute.key, attribute.value) for attribute in attribute_list])
+ attributes = dict()
+ for attribute in attribute_list:
+ if attribute.key in attributes:
+ raise Exception("Duplicate key (%s) in attribute list" % attribute.key)
+ attributes[attribute.key] = _MapAttributeValue(module, kind,
+ attribute.value)
+ return attributes
builtin_values = frozenset([
@@ -257,7 +558,8 @@ def _Kind(kinds, spec, scope):
return kind
if spec.startswith('?'):
- kind = _Kind(kinds, spec[1:], scope).MakeNullableKind()
+ kind = _Kind(kinds, spec[1:], scope)
+ kind = kind.MakeNullableKind()
elif spec.startswith('a:'):
kind = mojom.Array(_Kind(kinds, spec[2:], scope))
elif spec.startswith('asso:'):
@@ -303,7 +605,8 @@ def _Kind(kinds, spec, scope):
def _Import(module, import_module):
# Copy the struct kinds from our imports into the current module.
- importable_kinds = (mojom.Struct, mojom.Union, mojom.Enum, mojom.Interface)
+ importable_kinds = (mojom.Struct, mojom.Union, mojom.Enum, mojom.Interface,
+ mojom.Feature)
for kind in import_module.kinds.values():
if (isinstance(kind, importable_kinds)
and kind.module.path == import_module.path):
@@ -316,6 +619,32 @@ def _Import(module, import_module):
return import_module
+def _Feature(module, parsed_feature):
+ """
+ Args:
+ module: {mojom.Module} Module currently being constructed.
+ parsed_feature: {ast.Feature} Parsed feature.
+
+ Returns:
+ {mojom.Feature} AST feature.
+ """
+ feature = mojom.Feature(module=module)
+ feature.mojom_name = parsed_feature.mojom_name
+ feature.spec = 'x:' + module.GetNamespacePrefix() + feature.mojom_name
+ module.kinds[feature.spec] = feature
+ feature.constants = []
+ _ProcessElements(
+ parsed_feature.mojom_name, parsed_feature.body, {
+ ast.Const:
+ lambda const: feature.constants.append(
+ _Constant(module, const, feature)),
+ })
+
+ feature.attributes = _AttributeListToDict(module, feature,
+ parsed_feature.attribute_list)
+ return feature
+
+
def _Struct(module, parsed_struct):
"""
Args:
@@ -345,7 +674,8 @@ def _Struct(module, parsed_struct):
struct.fields_data.append,
})
- struct.attributes = _AttributeListToDict(parsed_struct.attribute_list)
+ struct.attributes = _AttributeListToDict(module, struct,
+ parsed_struct.attribute_list)
# Enforce that a [Native] attribute is set to make native-only struct
# declarations more explicit.
@@ -377,7 +707,8 @@ def _Union(module, parsed_union):
union.fields_data = []
_ProcessElements(parsed_union.mojom_name, parsed_union.body,
{ast.UnionField: union.fields_data.append})
- union.attributes = _AttributeListToDict(parsed_union.attribute_list)
+ union.attributes = _AttributeListToDict(module, union,
+ parsed_union.attribute_list)
return union
@@ -398,7 +729,8 @@ def _StructField(module, parsed_field, struct):
field.ordinal = parsed_field.ordinal.value if parsed_field.ordinal else None
field.default = _LookupValue(module, struct, field.kind,
parsed_field.default_value)
- field.attributes = _AttributeListToDict(parsed_field.attribute_list)
+ field.attributes = _AttributeListToDict(module, field,
+ parsed_field.attribute_list)
return field
@@ -414,11 +746,22 @@ def _UnionField(module, parsed_field, union):
"""
field = mojom.UnionField()
field.mojom_name = parsed_field.mojom_name
+ # Disallow unions from being self-recursive.
+ parsed_typename = parsed_field.typename
+ if parsed_typename.endswith('?'):
+ parsed_typename = parsed_typename[:-1]
+ assert parsed_typename != union.mojom_name
field.kind = _Kind(module.kinds, _MapKind(parsed_field.typename),
(module.mojom_namespace, union.mojom_name))
field.ordinal = parsed_field.ordinal.value if parsed_field.ordinal else None
field.default = None
- field.attributes = _AttributeListToDict(parsed_field.attribute_list)
+ field.attributes = _AttributeListToDict(module, field,
+ parsed_field.attribute_list)
+ if field.is_default and not mojom.IsNullableKind(field.kind) and \
+ not mojom.IsIntegralKind(field.kind):
+ raise Exception(
+ '[Default] field for union %s must be nullable or integral type.' %
+ union.mojom_name)
return field
@@ -439,7 +782,8 @@ def _Parameter(module, parsed_param, interface):
parameter.ordinal = (parsed_param.ordinal.value
if parsed_param.ordinal else None)
parameter.default = None # TODO(tibell): We never have these. Remove field?
- parameter.attributes = _AttributeListToDict(parsed_param.attribute_list)
+ parameter.attributes = _AttributeListToDict(module, parameter,
+ parsed_param.attribute_list)
return parameter
@@ -464,7 +808,8 @@ def _Method(module, parsed_method, interface):
method.response_parameters = list(
map(lambda parameter: _Parameter(module, parameter, interface),
parsed_method.response_parameter_list))
- method.attributes = _AttributeListToDict(parsed_method.attribute_list)
+ method.attributes = _AttributeListToDict(module, method,
+ parsed_method.attribute_list)
# Enforce that only methods with response can have a [Sync] attribute.
if method.sync and method.response_parameters is None:
@@ -492,7 +837,8 @@ def _Interface(module, parsed_iface):
interface.mojom_name = parsed_iface.mojom_name
interface.spec = 'x:' + module.GetNamespacePrefix() + interface.mojom_name
module.kinds[interface.spec] = interface
- interface.attributes = _AttributeListToDict(parsed_iface.attribute_list)
+ interface.attributes = _AttributeListToDict(module, interface,
+ parsed_iface.attribute_list)
interface.enums = []
interface.constants = []
interface.methods_data = []
@@ -522,7 +868,8 @@ def _EnumField(module, enum, parsed_field):
field = mojom.EnumField()
field.mojom_name = parsed_field.mojom_name
field.value = _LookupValue(module, enum, None, parsed_field.value)
- field.attributes = _AttributeListToDict(parsed_field.attribute_list)
+ field.attributes = _AttributeListToDict(module, field,
+ parsed_field.attribute_list)
value = mojom.EnumValue(module, enum, field)
module.values[value.GetSpec()] = value
return field
@@ -544,7 +891,7 @@ def _ResolveNumericEnumValues(enum):
prev_value += 1
# Integral value (e.g: BEGIN = -0x1).
- elif _IsStrOrUnicode(field.value):
+ elif isinstance(field.value, str):
prev_value = int(field.value, 0)
# Reference to a previous enum value (e.g: INIT = BEGIN).
@@ -560,7 +907,10 @@ def _ResolveNumericEnumValues(enum):
else:
raise Exception('Unresolved enum value for %s' % field.value.GetSpec())
- #resolved_enum_values[field.mojom_name] = prev_value
+ if prev_value in (-128, -127):
+ raise Exception(f'{field.mojom_name} in {enum.spec} has the value '
+ f'{prev_value}, which is reserved for WTF::HashTrait\'s '
+ 'default enum specialization and may not be used.')
field.numeric_value = prev_value
if min_value is None or prev_value < min_value:
min_value = prev_value
@@ -588,7 +938,8 @@ def _Enum(module, parsed_enum, parent_kind):
mojom_name = parent_kind.mojom_name + '.' + mojom_name
enum.spec = 'x:%s.%s' % (module.mojom_namespace, mojom_name)
enum.parent_kind = parent_kind
- enum.attributes = _AttributeListToDict(parsed_enum.attribute_list)
+ enum.attributes = _AttributeListToDict(module, enum,
+ parsed_enum.attribute_list)
if not enum.native_only:
enum.fields = list(
@@ -600,11 +951,18 @@ def _Enum(module, parsed_enum, parent_kind):
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')
+ f'Non-extensible enum {enum.spec} may not specify a default')
+ if enum.default_field is not None:
+ raise Exception(f'Multiple [Default] enumerators in enum {enum.spec}')
enum.default_field = field
+ # While running the backwards compatibility check, ignore errors because the
+ # old version of the enum might not specify [Default].
+ if (enum.extensible and enum.default_field is None
+ and enum.spec not in _EXTENSIBLE_ENUMS_MISSING_DEFAULT
+ and not is_running_backwards_compatibility_check_hack):
+ raise Exception(
+ f'Extensible enum {enum.spec} must specify a [Default] enumerator')
module.kinds[enum.spec] = enum
@@ -696,6 +1054,11 @@ def _CollectReferencedKinds(module, all_defined_kinds):
for referenced_kind in extract_referenced_user_kinds(param.kind):
sanitized_kind = sanitize_kind(referenced_kind)
referenced_user_kinds[sanitized_kind.spec] = sanitized_kind
+ # Consts can reference imported enums.
+ for const in module.constants:
+ if not const.kind in mojom.PRIMITIVES:
+ sanitized_kind = sanitize_kind(const.kind)
+ referenced_user_kinds[sanitized_kind.spec] = sanitized_kind
return referenced_user_kinds
@@ -741,6 +1104,16 @@ def _AssertTypeIsStable(kind):
assertDependencyIsStable(response_param.kind)
+def _AssertStructIsValid(kind):
+ expected_ordinals = set(range(0, len(kind.fields)))
+ ordinals = set(map(lambda field: field.ordinal, kind.fields))
+ if ordinals != expected_ordinals:
+ raise Exception(
+ 'Structs must use contiguous ordinals starting from 0. ' +
+ '{} is missing the following ordinals: {}.'.format(
+ kind.mojom_name, ', '.join(map(str, expected_ordinals - ordinals))))
+
+
def _Module(tree, path, imports):
"""
Args:
@@ -778,6 +1151,8 @@ def _Module(tree, path, imports):
module.structs = []
module.unions = []
module.interfaces = []
+ module.features = []
+
_ProcessElements(
filename, tree.definition_list, {
ast.Const:
@@ -791,6 +1166,8 @@ def _Module(tree, path, imports):
ast.Interface:
lambda interface: module.interfaces.append(
_Interface(module, interface)),
+ ast.Feature:
+ lambda feature: module.features.append(_Feature(module, feature)),
})
# Second pass expands fields and methods. This allows fields and parameters
@@ -806,12 +1183,24 @@ def _Module(tree, path, imports):
for enum in struct.enums:
all_defined_kinds[enum.spec] = enum
+ for feature in module.features:
+ all_defined_kinds[feature.spec] = feature
+
for union in module.unions:
union.fields = list(
map(lambda field: _UnionField(module, field, union), union.fields_data))
_AssignDefaultOrdinals(union.fields)
+ for field in union.fields:
+ if field.is_default:
+ if union.default_field is not None:
+ raise Exception('Multiple [Default] fields in union %s.' %
+ union.mojom_name)
+ union.default_field = field
del union.fields_data
all_defined_kinds[union.spec] = union
+ if union.extensible and union.default_field is None:
+ raise Exception('Extensible union %s must specify a [Default] field' %
+ union.mojom_name)
for interface in module.interfaces:
interface.methods = list(
@@ -829,8 +1218,8 @@ def _Module(tree, path, imports):
all_defined_kinds.values())
imported_kind_specs = set(all_referenced_kinds.keys()).difference(
set(all_defined_kinds.keys()))
- module.imported_kinds = dict(
- (spec, all_referenced_kinds[spec]) for spec in imported_kind_specs)
+ module.imported_kinds = OrderedDict((spec, all_referenced_kinds[spec])
+ for spec in sorted(imported_kind_specs))
generator.AddComputedData(module)
for iface in module.interfaces:
@@ -847,6 +1236,9 @@ def _Module(tree, path, imports):
if kind.stable:
_AssertTypeIsStable(kind)
+ for kind in module.structs:
+ _AssertStructIsValid(kind)
+
return module
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
index 19905c8a..b4fea924 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
@@ -1,17 +1,13 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
-import os.path
-import sys
import unittest
from mojom.generate import module as mojom
from mojom.generate import translate
from mojom.parse import ast
-
class TranslateTest(unittest.TestCase):
"""Tests |parser.Parse()|."""
@@ -69,5 +65,77 @@ class TranslateTest(unittest.TestCase):
# pylint: disable=W0212
self.assertEquals(
translate._MapKind("asso<SomeInterface>?"), "?asso:x:SomeInterface")
- self.assertEquals(
- translate._MapKind("asso<SomeInterface&>?"), "?asso:r:x:SomeInterface")
+ self.assertEquals(translate._MapKind("rca<SomeInterface>?"),
+ "?rca:x:SomeInterface")
+
+ def testSelfRecursiveUnions(self):
+ """Verifies _UnionField() raises when a union is self-recursive."""
+ tree = ast.Mojom(None, ast.ImportList(), [
+ ast.Union("SomeUnion", None,
+ ast.UnionBody([ast.UnionField("a", None, None, "SomeUnion")]))
+ ])
+ with self.assertRaises(Exception):
+ translate.OrderedModule(tree, "mojom_tree", [])
+
+ tree = ast.Mojom(None, ast.ImportList(), [
+ ast.Union(
+ "SomeUnion", None,
+ ast.UnionBody([ast.UnionField("a", None, None, "SomeUnion?")]))
+ ])
+ with self.assertRaises(Exception):
+ translate.OrderedModule(tree, "mojom_tree", [])
+
+ def testDuplicateAttributesException(self):
+ tree = ast.Mojom(None, ast.ImportList(), [
+ ast.Union(
+ "FakeUnion",
+ ast.AttributeList([
+ ast.Attribute("key1", "value"),
+ ast.Attribute("key1", "value")
+ ]),
+ ast.UnionBody([
+ ast.UnionField("a", None, None, "int32"),
+ ast.UnionField("b", None, None, "string")
+ ]))
+ ])
+ with self.assertRaises(Exception):
+ translate.OrderedModule(tree, "mojom_tree", [])
+
+ def testEnumWithReservedValues(self):
+ """Verifies that assigning reserved values to enumerators fails."""
+ # -128 is reserved for the empty representation in WTF::HashTraits.
+ tree = ast.Mojom(None, ast.ImportList(), [
+ ast.Enum(
+ "MyEnum", None,
+ ast.EnumValueList([
+ ast.EnumValue('kReserved', None, '-128'),
+ ]))
+ ])
+ with self.assertRaises(Exception) as context:
+ translate.OrderedModule(tree, "mojom_tree", [])
+ self.assertIn("reserved for WTF::HashTrait", str(context.exception))
+
+ # -127 is reserved for the deleted representation in WTF::HashTraits.
+ tree = ast.Mojom(None, ast.ImportList(), [
+ ast.Enum(
+ "MyEnum", None,
+ ast.EnumValueList([
+ ast.EnumValue('kReserved', None, '-127'),
+ ]))
+ ])
+ with self.assertRaises(Exception) as context:
+ translate.OrderedModule(tree, "mojom_tree", [])
+ self.assertIn("reserved for WTF::HashTrait", str(context.exception))
+
+ # Implicitly assigning a reserved value should also fail.
+ tree = ast.Mojom(None, ast.ImportList(), [
+ ast.Enum(
+ "MyEnum", None,
+ ast.EnumValueList([
+ ast.EnumValue('kNotReserved', None, '-129'),
+ ast.EnumValue('kImplicitlyReserved', None, None),
+ ]))
+ ])
+ with self.assertRaises(Exception) as context:
+ translate.OrderedModule(tree, "mojom_tree", [])
+ self.assertIn("reserved for WTF::HashTrait", str(context.exception))
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast.py
index 1f0db200..aae9cdb6 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Node classes for the AST for a Mojo IDL file."""
@@ -8,17 +8,14 @@
# and lineno). You may also define __repr__() to help with analyzing test
# failures, especially for more complex types.
+import os.path
-import sys
+# Instance of 'NodeListBase' has no '_list_item_type' member (no-member)
+# pylint: disable=no-member
-def _IsStrOrUnicode(x):
- if sys.version_info[0] < 3:
- return isinstance(x, (unicode, str))
- return isinstance(x, str)
-
-class NodeBase(object):
+class NodeBase:
"""Base class for nodes in the AST."""
def __init__(self, filename=None, lineno=None):
@@ -43,7 +40,7 @@ class NodeListBase(NodeBase):
classes, in a tuple) of the members of the list.)"""
def __init__(self, item_or_items=None, **kwargs):
- super(NodeListBase, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.items = []
if item_or_items is None:
pass
@@ -62,7 +59,7 @@ class NodeListBase(NodeBase):
return self.items.__iter__()
def __eq__(self, other):
- return super(NodeListBase, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.items == other.items
# Implement this so that on failure, we get slightly more sensible output.
@@ -96,7 +93,7 @@ class Definition(NodeBase):
include parameter definitions.) This class is meant to be subclassed."""
def __init__(self, mojom_name, **kwargs):
- assert _IsStrOrUnicode(mojom_name)
+ assert isinstance(mojom_name, str)
NodeBase.__init__(self, **kwargs)
self.mojom_name = mojom_name
@@ -108,13 +105,13 @@ class Attribute(NodeBase):
"""Represents an attribute."""
def __init__(self, key, value, **kwargs):
- assert _IsStrOrUnicode(key)
- super(Attribute, self).__init__(**kwargs)
+ assert isinstance(key, str)
+ super().__init__(**kwargs)
self.key = key
self.value = value
def __eq__(self, other):
- return super(Attribute, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.key == other.key and \
self.value == other.value
@@ -131,17 +128,17 @@ class Const(Definition):
def __init__(self, mojom_name, attribute_list, typename, value, **kwargs):
assert attribute_list is None or isinstance(attribute_list, AttributeList)
# The typename is currently passed through as a string.
- assert _IsStrOrUnicode(typename)
+ assert isinstance(typename, str)
# The value is either a literal (currently passed through as a string) or a
# "wrapped identifier".
- assert _IsStrOrUnicode or isinstance(value, tuple)
- super(Const, self).__init__(mojom_name, **kwargs)
+ assert isinstance(value, (tuple, str))
+ super().__init__(mojom_name, **kwargs)
self.attribute_list = attribute_list
self.typename = typename
self.value = value
def __eq__(self, other):
- return super(Const, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.attribute_list == other.attribute_list and \
self.typename == other.typename and \
self.value == other.value
@@ -153,12 +150,12 @@ class Enum(Definition):
def __init__(self, mojom_name, attribute_list, enum_value_list, **kwargs):
assert attribute_list is None or isinstance(attribute_list, AttributeList)
assert enum_value_list is None or isinstance(enum_value_list, EnumValueList)
- super(Enum, self).__init__(mojom_name, **kwargs)
+ super().__init__(mojom_name, **kwargs)
self.attribute_list = attribute_list
self.enum_value_list = enum_value_list
def __eq__(self, other):
- return super(Enum, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.attribute_list == other.attribute_list and \
self.enum_value_list == other.enum_value_list
@@ -170,13 +167,13 @@ class EnumValue(Definition):
# The optional value is either an int (which is current a string) or a
# "wrapped identifier".
assert attribute_list is None or isinstance(attribute_list, AttributeList)
- assert value is None or _IsStrOrUnicode(value) or isinstance(value, tuple)
- super(EnumValue, self).__init__(mojom_name, **kwargs)
+ assert value is None or isinstance(value, (tuple, str))
+ super().__init__(mojom_name, **kwargs)
self.attribute_list = attribute_list
self.value = value
def __eq__(self, other):
- return super(EnumValue, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.attribute_list == other.attribute_list and \
self.value == other.value
@@ -188,18 +185,47 @@ class EnumValueList(NodeListBase):
_list_item_type = EnumValue
+class Feature(Definition):
+ """Represents a runtime feature definition."""
+ def __init__(self, mojom_name, attribute_list, body, **kwargs):
+ assert attribute_list is None or isinstance(attribute_list, AttributeList)
+ assert isinstance(body, FeatureBody) or body is None
+ super().__init__(mojom_name, **kwargs)
+ self.attribute_list = attribute_list
+ self.body = body
+
+ def __eq__(self, other):
+ return super().__eq__(other) and \
+ self.attribute_list == other.attribute_list and \
+ self.body == other.body
+
+ def __repr__(self):
+ return "Feature(mojom_name = %s, attribute_list = %s, body = %s)" % (
+ self.mojom_name, self.attribute_list, self.body)
+
+
+# This needs to be declared after `FeatureConst` and `FeatureField`.
+class FeatureBody(NodeListBase):
+ """Represents the body of (i.e., list of definitions inside) a feature."""
+
+ # Features are compile time helpers so all fields are initializers/consts
+ # for the underlying platform feature type.
+ _list_item_type = (Const)
+
+
class Import(NodeBase):
"""Represents an import statement."""
def __init__(self, attribute_list, import_filename, **kwargs):
assert attribute_list is None or isinstance(attribute_list, AttributeList)
- assert _IsStrOrUnicode(import_filename)
- super(Import, self).__init__(**kwargs)
+ assert isinstance(import_filename, str)
+ super().__init__(**kwargs)
self.attribute_list = attribute_list
- self.import_filename = import_filename
+ # TODO(crbug.com/953884): Use pathlib once we're migrated fully to Python 3.
+ self.import_filename = os.path.normpath(import_filename).replace('\\', '/')
def __eq__(self, other):
- return super(Import, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.attribute_list == other.attribute_list and \
self.import_filename == other.import_filename
@@ -216,12 +242,12 @@ class Interface(Definition):
def __init__(self, mojom_name, attribute_list, body, **kwargs):
assert attribute_list is None or isinstance(attribute_list, AttributeList)
assert isinstance(body, InterfaceBody)
- super(Interface, self).__init__(mojom_name, **kwargs)
+ super().__init__(mojom_name, **kwargs)
self.attribute_list = attribute_list
self.body = body
def __eq__(self, other):
- return super(Interface, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.attribute_list == other.attribute_list and \
self.body == other.body
@@ -236,14 +262,14 @@ class Method(Definition):
assert isinstance(parameter_list, ParameterList)
assert response_parameter_list is None or \
isinstance(response_parameter_list, ParameterList)
- super(Method, self).__init__(mojom_name, **kwargs)
+ super().__init__(mojom_name, **kwargs)
self.attribute_list = attribute_list
self.ordinal = ordinal
self.parameter_list = parameter_list
self.response_parameter_list = response_parameter_list
def __eq__(self, other):
- return super(Method, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.attribute_list == other.attribute_list and \
self.ordinal == other.ordinal and \
self.parameter_list == other.parameter_list and \
@@ -264,12 +290,12 @@ class Module(NodeBase):
# |mojom_namespace| is either none or a "wrapped identifier".
assert mojom_namespace is None or isinstance(mojom_namespace, tuple)
assert attribute_list is None or isinstance(attribute_list, AttributeList)
- super(Module, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.mojom_namespace = mojom_namespace
self.attribute_list = attribute_list
def __eq__(self, other):
- return super(Module, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.mojom_namespace == other.mojom_namespace and \
self.attribute_list == other.attribute_list
@@ -281,13 +307,13 @@ class Mojom(NodeBase):
assert module is None or isinstance(module, Module)
assert isinstance(import_list, ImportList)
assert isinstance(definition_list, list)
- super(Mojom, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.module = module
self.import_list = import_list
self.definition_list = definition_list
def __eq__(self, other):
- return super(Mojom, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.module == other.module and \
self.import_list == other.import_list and \
self.definition_list == other.definition_list
@@ -302,11 +328,11 @@ class Ordinal(NodeBase):
def __init__(self, value, **kwargs):
assert isinstance(value, int)
- super(Ordinal, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.value = value
def __eq__(self, other):
- return super(Ordinal, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.value == other.value
@@ -314,18 +340,18 @@ class Parameter(NodeBase):
"""Represents a method request or response parameter."""
def __init__(self, mojom_name, attribute_list, ordinal, typename, **kwargs):
- assert _IsStrOrUnicode(mojom_name)
+ assert isinstance(mojom_name, str)
assert attribute_list is None or isinstance(attribute_list, AttributeList)
assert ordinal is None or isinstance(ordinal, Ordinal)
- assert _IsStrOrUnicode(typename)
- super(Parameter, self).__init__(**kwargs)
+ assert isinstance(typename, str)
+ super().__init__(**kwargs)
self.mojom_name = mojom_name
self.attribute_list = attribute_list
self.ordinal = ordinal
self.typename = typename
def __eq__(self, other):
- return super(Parameter, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.mojom_name == other.mojom_name and \
self.attribute_list == other.attribute_list and \
self.ordinal == other.ordinal and \
@@ -344,42 +370,51 @@ class Struct(Definition):
def __init__(self, mojom_name, attribute_list, body, **kwargs):
assert attribute_list is None or isinstance(attribute_list, AttributeList)
assert isinstance(body, StructBody) or body is None
- super(Struct, self).__init__(mojom_name, **kwargs)
+ super().__init__(mojom_name, **kwargs)
self.attribute_list = attribute_list
self.body = body
def __eq__(self, other):
- return super(Struct, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.attribute_list == other.attribute_list and \
self.body == other.body
+ def __repr__(self):
+ return "Struct(mojom_name = %s, attribute_list = %s, body = %s)" % (
+ self.mojom_name, self.attribute_list, self.body)
+
class StructField(Definition):
"""Represents a struct field definition."""
def __init__(self, mojom_name, attribute_list, ordinal, typename,
default_value, **kwargs):
- assert _IsStrOrUnicode(mojom_name)
+ assert isinstance(mojom_name, str)
assert attribute_list is None or isinstance(attribute_list, AttributeList)
assert ordinal is None or isinstance(ordinal, Ordinal)
- assert _IsStrOrUnicode(typename)
+ assert isinstance(typename, str)
# The optional default value is currently either a value as a string or a
# "wrapped identifier".
- assert default_value is None or _IsStrOrUnicode(default_value) or \
- isinstance(default_value, tuple)
- super(StructField, self).__init__(mojom_name, **kwargs)
+ assert default_value is None or isinstance(default_value, (str, tuple))
+ super().__init__(mojom_name, **kwargs)
self.attribute_list = attribute_list
self.ordinal = ordinal
self.typename = typename
self.default_value = default_value
def __eq__(self, other):
- return super(StructField, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.attribute_list == other.attribute_list and \
self.ordinal == other.ordinal and \
self.typename == other.typename and \
self.default_value == other.default_value
+ def __repr__(self):
+ return ("StructField(mojom_name = %s, attribute_list = %s, ordinal = %s, "
+ "typename = %s, default_value = %s") % (
+ self.mojom_name, self.attribute_list, self.ordinal,
+ self.typename, self.default_value)
+
# This needs to be declared after |StructField|.
class StructBody(NodeListBase):
@@ -394,29 +429,29 @@ class Union(Definition):
def __init__(self, mojom_name, attribute_list, body, **kwargs):
assert attribute_list is None or isinstance(attribute_list, AttributeList)
assert isinstance(body, UnionBody)
- super(Union, self).__init__(mojom_name, **kwargs)
+ super().__init__(mojom_name, **kwargs)
self.attribute_list = attribute_list
self.body = body
def __eq__(self, other):
- return super(Union, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.attribute_list == other.attribute_list and \
self.body == other.body
class UnionField(Definition):
def __init__(self, mojom_name, attribute_list, ordinal, typename, **kwargs):
- assert _IsStrOrUnicode(mojom_name)
+ assert isinstance(mojom_name, str)
assert attribute_list is None or isinstance(attribute_list, AttributeList)
assert ordinal is None or isinstance(ordinal, Ordinal)
- assert _IsStrOrUnicode(typename)
- super(UnionField, self).__init__(mojom_name, **kwargs)
+ assert isinstance(typename, str)
+ super().__init__(mojom_name, **kwargs)
self.attribute_list = attribute_list
self.ordinal = ordinal
self.typename = typename
def __eq__(self, other):
- return super(UnionField, self).__eq__(other) and \
+ return super().__eq__(other) and \
self.attribute_list == other.attribute_list and \
self.ordinal == other.ordinal and \
self.typename == other.typename
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast_unittest.py
index 62798631..b289f7b1 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast_unittest.py
@@ -1,32 +1,26 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
-import os.path
-import sys
import unittest
from mojom.parse import ast
-
class _TestNode(ast.NodeBase):
"""Node type for tests."""
def __init__(self, value, **kwargs):
- super(_TestNode, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.value = value
def __eq__(self, other):
- return super(_TestNode, self).__eq__(other) and self.value == other.value
-
+ return super().__eq__(other) and self.value == other.value
class _TestNodeList(ast.NodeListBase):
"""Node list type for tests."""
_list_item_type = _TestNode
-
class ASTTest(unittest.TestCase):
"""Tests various AST classes."""
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features.py
index 3cb73c5d..9687edbf 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features.py
@@ -1,4 +1,4 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
+# Copyright 2018 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Helpers for processing conditionally enabled features in a mojom."""
@@ -17,8 +17,10 @@ class EnableIfError(Error):
def _IsEnabled(definition, enabled_features):
"""Returns true if a definition is enabled.
- A definition is enabled if it has no EnableIf attribute, or if the value of
- the EnableIf attribute is in enabled_features.
+ A definition is enabled if it has no EnableIf/EnableIfNot attribute.
+ It is retained if it has an EnableIf attribute and the attribute is in
+ enabled_features. It is retained if it has an EnableIfNot attribute and the
+ attribute is not in enabled features.
"""
if not hasattr(definition, "attribute_list"):
return True
@@ -27,17 +29,19 @@ def _IsEnabled(definition, enabled_features):
already_defined = False
for a in definition.attribute_list:
- if a.key == 'EnableIf':
+ if a.key == 'EnableIf' or a.key == 'EnableIfNot':
if already_defined:
raise EnableIfError(
definition.filename,
- "EnableIf attribute may only be defined once per field.",
+ "EnableIf/EnableIfNot attribute may only be set once per field.",
definition.lineno)
already_defined = True
for attribute in definition.attribute_list:
if attribute.key == 'EnableIf' and attribute.value not in enabled_features:
return False
+ if attribute.key == 'EnableIfNot' and attribute.value in enabled_features:
+ return False
return True
@@ -56,15 +60,12 @@ def _FilterDefinition(definition, enabled_features):
"""Filters definitions with a body."""
if isinstance(definition, ast.Enum):
_FilterDisabledFromNodeList(definition.enum_value_list, enabled_features)
- elif isinstance(definition, ast.Interface):
- _FilterDisabledFromNodeList(definition.body, enabled_features)
elif isinstance(definition, ast.Method):
_FilterDisabledFromNodeList(definition.parameter_list, enabled_features)
_FilterDisabledFromNodeList(definition.response_parameter_list,
enabled_features)
- elif isinstance(definition, ast.Struct):
- _FilterDisabledFromNodeList(definition.body, enabled_features)
- elif isinstance(definition, ast.Union):
+ elif isinstance(definition,
+ (ast.Interface, ast.Struct, ast.Union, ast.Feature)):
_FilterDisabledFromNodeList(definition.body, enabled_features)
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py
index aa609be7..cca1764b 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py
@@ -1,13 +1,12 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
+# Copyright 2018 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
+import importlib.util
import os
import sys
import unittest
-
def _GetDirAbove(dirname):
"""Returns the directory "above" this file containing |dirname| (which must
also be "above" this file)."""
@@ -18,9 +17,8 @@ def _GetDirAbove(dirname):
if tail == dirname:
return path
-
try:
- imp.find_module('mojom')
+ importlib.util.find_spec("mojom")
except ImportError:
sys.path.append(os.path.join(_GetDirAbove('pylib'), 'pylib'))
import mojom.parse.ast as ast
@@ -29,7 +27,6 @@ import mojom.parse.parser as parser
ENABLED_FEATURES = frozenset({'red', 'green', 'blue'})
-
class ConditionalFeaturesTest(unittest.TestCase):
"""Tests |mojom.parse.conditional_features|."""
@@ -55,6 +52,48 @@ class ConditionalFeaturesTest(unittest.TestCase):
"""
self.parseAndAssertEqual(const_source, expected_source)
+ def testFilterIfNotConst(self):
+ """Test that Consts are correctly filtered."""
+ const_source = """
+ [EnableIfNot=blue]
+ const int kMyConst1 = 1;
+ [EnableIfNot=orange]
+ const double kMyConst2 = 2;
+ [EnableIf=blue]
+ const int kMyConst3 = 3;
+ [EnableIfNot=blue]
+ const int kMyConst4 = 4;
+ [EnableIfNot=purple]
+ const int kMyConst5 = 5;
+ """
+ expected_source = """
+ [EnableIfNot=orange]
+ const double kMyConst2 = 2;
+ [EnableIf=blue]
+ const int kMyConst3 = 3;
+ [EnableIfNot=purple]
+ const int kMyConst5 = 5;
+ """
+ self.parseAndAssertEqual(const_source, expected_source)
+
+ def testFilterIfNotMultipleConst(self):
+ """Test that Consts are correctly filtered."""
+ const_source = """
+ [EnableIfNot=blue]
+ const int kMyConst1 = 1;
+ [EnableIfNot=orange]
+ const double kMyConst2 = 2;
+ [EnableIfNot=orange]
+ const int kMyConst3 = 3;
+ """
+ expected_source = """
+ [EnableIfNot=orange]
+ const double kMyConst2 = 2;
+ [EnableIfNot=orange]
+ const int kMyConst3 = 3;
+ """
+ self.parseAndAssertEqual(const_source, expected_source)
+
def testFilterEnum(self):
"""Test that EnumValues are correctly filtered from an Enum."""
enum_source = """
@@ -91,6 +130,24 @@ class ConditionalFeaturesTest(unittest.TestCase):
"""
self.parseAndAssertEqual(import_source, expected_source)
+ def testFilterIfNotImport(self):
+ """Test that imports are correctly filtered from a Mojom."""
+ import_source = """
+ [EnableIf=blue]
+ import "foo.mojom";
+ [EnableIfNot=purple]
+ import "bar.mojom";
+ [EnableIfNot=green]
+ import "baz.mojom";
+ """
+ expected_source = """
+ [EnableIf=blue]
+ import "foo.mojom";
+ [EnableIfNot=purple]
+ import "bar.mojom";
+ """
+ self.parseAndAssertEqual(import_source, expected_source)
+
def testFilterInterface(self):
"""Test that definitions are correctly filtered from an Interface."""
interface_source = """
@@ -175,6 +232,50 @@ class ConditionalFeaturesTest(unittest.TestCase):
"""
self.parseAndAssertEqual(struct_source, expected_source)
+ def testFilterIfNotStruct(self):
+ """Test that definitions are correctly filtered from a Struct."""
+ struct_source = """
+ struct MyStruct {
+ [EnableIf=blue]
+ enum MyEnum {
+ VALUE1,
+ [EnableIfNot=red]
+ VALUE2,
+ };
+ [EnableIfNot=yellow]
+ const double kMyConst = 1.23;
+ [EnableIf=green]
+ int32 a;
+ double b;
+ [EnableIfNot=purple]
+ int32 c;
+ [EnableIf=blue]
+ double d;
+ int32 e;
+ [EnableIfNot=red]
+ double f;
+ };
+ """
+ expected_source = """
+ struct MyStruct {
+ [EnableIf=blue]
+ enum MyEnum {
+ VALUE1,
+ };
+ [EnableIfNot=yellow]
+ const double kMyConst = 1.23;
+ [EnableIf=green]
+ int32 a;
+ double b;
+ [EnableIfNot=purple]
+ int32 c;
+ [EnableIf=blue]
+ double d;
+ int32 e;
+ };
+ """
+ self.parseAndAssertEqual(struct_source, expected_source)
+
def testFilterUnion(self):
"""Test that UnionFields are correctly filtered from a Union."""
union_source = """
@@ -216,6 +317,25 @@ class ConditionalFeaturesTest(unittest.TestCase):
"""
self.parseAndAssertEqual(mojom_source, expected_source)
+ def testFeaturesWithEnableIf(self):
+ mojom_source = """
+ feature Foo {
+ const string name = "FooFeature";
+ [EnableIf=red]
+ const bool default_state = false;
+ [EnableIf=yellow]
+ const bool default_state = true;
+ };
+ """
+ expected_source = """
+ feature Foo {
+ const string name = "FooFeature";
+ [EnableIf=red]
+ const bool default_state = false;
+ };
+ """
+ self.parseAndAssertEqual(mojom_source, expected_source)
+
def testMultipleEnableIfs(self):
source = """
enum Foo {
@@ -228,6 +348,29 @@ class ConditionalFeaturesTest(unittest.TestCase):
conditional_features.RemoveDisabledDefinitions,
definition, ENABLED_FEATURES)
+ def testMultipleEnableIfs(self):
+ source = """
+ enum Foo {
+ [EnableIf=red,EnableIfNot=yellow]
+ kBarValue = 5,
+ };
+ """
+ definition = parser.Parse(source, "my_file.mojom")
+ self.assertRaises(conditional_features.EnableIfError,
+ conditional_features.RemoveDisabledDefinitions,
+ definition, ENABLED_FEATURES)
+
+ def testMultipleEnableIfs(self):
+ source = """
+ enum Foo {
+ [EnableIfNot=red,EnableIfNot=yellow]
+ kBarValue = 5,
+ };
+ """
+ definition = parser.Parse(source, "my_file.mojom")
+ self.assertRaises(conditional_features.EnableIfError,
+ conditional_features.RemoveDisabledDefinitions,
+ definition, ENABLED_FEATURES)
if __name__ == '__main__':
unittest.main()
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer.py
index 3e084bbf..00136a8b 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer.py
@@ -1,8 +1,7 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
import os.path
import sys
@@ -22,7 +21,7 @@ class LexError(Error):
# We have methods which look like they could be functions:
# pylint: disable=R0201
-class Lexer(object):
+class Lexer:
def __init__(self, filename):
self.filename = filename
@@ -56,6 +55,7 @@ class Lexer(object):
'PENDING_RECEIVER',
'PENDING_ASSOCIATED_REMOTE',
'PENDING_ASSOCIATED_RECEIVER',
+ 'FEATURE',
)
keyword_map = {}
@@ -81,7 +81,6 @@ class Lexer(object):
# Operators
'MINUS',
'PLUS',
- 'AMP',
'QSTN',
# Assignment
@@ -168,7 +167,6 @@ class Lexer(object):
# Operators
t_MINUS = r'-'
t_PLUS = r'\+'
- t_AMP = r'&'
t_QSTN = r'\?'
# =
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py
index eadc6587..bc9f8354 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py
@@ -1,13 +1,12 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
+import importlib.util
import os.path
import sys
import unittest
-
def _GetDirAbove(dirname):
"""Returns the directory "above" this file containing |dirname| (which must
also be "above" this file)."""
@@ -18,17 +17,15 @@ def _GetDirAbove(dirname):
if tail == dirname:
return path
-
sys.path.insert(1, os.path.join(_GetDirAbove("mojo"), "third_party"))
from ply import lex
try:
- imp.find_module("mojom")
+ importlib.util.find_spec("mojom")
except ImportError:
sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
import mojom.parse.lexer
-
# This (monkey-patching LexToken to make comparison value-based) is evil, but
# we'll do it anyway. (I'm pretty sure ply's lexer never cares about comparing
# for object identity.)
@@ -146,7 +143,6 @@ class LexerTest(unittest.TestCase):
self._SingleTokenForInput("+"), _MakeLexToken("PLUS", "+"))
self.assertEquals(
self._SingleTokenForInput("-"), _MakeLexToken("MINUS", "-"))
- self.assertEquals(self._SingleTokenForInput("&"), _MakeLexToken("AMP", "&"))
self.assertEquals(
self._SingleTokenForInput("?"), _MakeLexToken("QSTN", "?"))
self.assertEquals(
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser.py
index b3b803d6..1dffd98b 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser.py
@@ -1,8 +1,11 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Generates a syntax tree from a Mojo IDL file."""
+# Breaking parser stanzas is unhelpful so allow longer lines.
+# pylint: disable=line-too-long
+
import os.path
import sys
@@ -33,7 +36,7 @@ class ParseError(Error):
# We have methods which look like they could be functions:
# pylint: disable=R0201
-class Parser(object):
+class Parser:
def __init__(self, lexer, source, filename):
self.tokens = lexer.tokens
self.source = source
@@ -111,7 +114,8 @@ class Parser(object):
| union
| interface
| enum
- | const"""
+ | const
+ | feature"""
p[0] = p[1]
def p_attribute_section_1(self, p):
@@ -140,12 +144,19 @@ class Parser(object):
p[0].Append(p[3])
def p_attribute_1(self, p):
- """attribute : NAME EQUALS evaled_literal
- | NAME EQUALS NAME"""
- p[0] = ast.Attribute(p[1], p[3], filename=self.filename, lineno=p.lineno(1))
+ """attribute : name_wrapped EQUALS identifier_wrapped"""
+ p[0] = ast.Attribute(p[1],
+ p[3][1],
+ filename=self.filename,
+ lineno=p.lineno(1))
def p_attribute_2(self, p):
- """attribute : NAME"""
+ """attribute : name_wrapped EQUALS evaled_literal
+ | name_wrapped EQUALS name_wrapped"""
+ p[0] = ast.Attribute(p[1], p[3], filename=self.filename, lineno=p.lineno(1))
+
+ def p_attribute_3(self, p):
+ """attribute : name_wrapped"""
p[0] = ast.Attribute(p[1], True, filename=self.filename, lineno=p.lineno(1))
def p_evaled_literal(self, p):
@@ -161,11 +172,11 @@ class Parser(object):
p[0] = eval(p[1])
def p_struct_1(self, p):
- """struct : attribute_section STRUCT NAME LBRACE struct_body RBRACE SEMI"""
+ """struct : attribute_section STRUCT name_wrapped LBRACE struct_body RBRACE SEMI"""
p[0] = ast.Struct(p[3], p[1], p[5])
def p_struct_2(self, p):
- """struct : attribute_section STRUCT NAME SEMI"""
+ """struct : attribute_section STRUCT name_wrapped SEMI"""
p[0] = ast.Struct(p[3], p[1], None)
def p_struct_body_1(self, p):
@@ -180,11 +191,24 @@ class Parser(object):
p[0].Append(p[2])
def p_struct_field(self, p):
- """struct_field : attribute_section typename NAME ordinal default SEMI"""
+ """struct_field : attribute_section typename name_wrapped ordinal default SEMI"""
p[0] = ast.StructField(p[3], p[1], p[4], p[2], p[5])
+ def p_feature(self, p):
+ """feature : attribute_section FEATURE NAME LBRACE feature_body RBRACE SEMI"""
+ p[0] = ast.Feature(p[3], p[1], p[5])
+
+ def p_feature_body_1(self, p):
+ """feature_body : """
+ p[0] = ast.FeatureBody()
+
+ def p_feature_body_2(self, p):
+ """feature_body : feature_body const"""
+ p[0] = p[1]
+ p[0].Append(p[2])
+
def p_union(self, p):
- """union : attribute_section UNION NAME LBRACE union_body RBRACE SEMI"""
+ """union : attribute_section UNION name_wrapped LBRACE union_body RBRACE SEMI"""
p[0] = ast.Union(p[3], p[1], p[5])
def p_union_body_1(self, p):
@@ -197,7 +221,7 @@ class Parser(object):
p[1].Append(p[2])
def p_union_field(self, p):
- """union_field : attribute_section typename NAME ordinal SEMI"""
+ """union_field : attribute_section typename name_wrapped ordinal SEMI"""
p[0] = ast.UnionField(p[3], p[1], p[4], p[2])
def p_default_1(self, p):
@@ -209,8 +233,7 @@ class Parser(object):
p[0] = p[2]
def p_interface(self, p):
- """interface : attribute_section INTERFACE NAME LBRACE interface_body \
- RBRACE SEMI"""
+ """interface : attribute_section INTERFACE name_wrapped LBRACE interface_body RBRACE SEMI"""
p[0] = ast.Interface(p[3], p[1], p[5])
def p_interface_body_1(self, p):
@@ -233,8 +256,7 @@ class Parser(object):
p[0] = p[3]
def p_method(self, p):
- """method : attribute_section NAME ordinal LPAREN parameter_list RPAREN \
- response SEMI"""
+ """method : attribute_section name_wrapped ordinal LPAREN parameter_list RPAREN response SEMI"""
p[0] = ast.Method(p[2], p[1], p[3], p[5], p[7])
def p_parameter_list_1(self, p):
@@ -255,7 +277,7 @@ class Parser(object):
p[0].Append(p[3])
def p_parameter(self, p):
- """parameter : attribute_section typename NAME ordinal"""
+ """parameter : attribute_section typename name_wrapped ordinal"""
p[0] = ast.Parameter(
p[3], p[1], p[4], p[2], filename=self.filename, lineno=p.lineno(3))
@@ -271,8 +293,7 @@ class Parser(object):
"""nonnullable_typename : basictypename
| array
| fixed_array
- | associative_array
- | interfacerequest"""
+ | associative_array"""
p[0] = p[1]
def p_basictypename(self, p):
@@ -297,18 +318,16 @@ class Parser(object):
p[0] = "rcv<%s>" % p[3]
def p_associatedremotetype(self, p):
- """associatedremotetype : PENDING_ASSOCIATED_REMOTE LANGLE identifier \
- RANGLE"""
+ """associatedremotetype : PENDING_ASSOCIATED_REMOTE LANGLE identifier RANGLE"""
p[0] = "rma<%s>" % p[3]
def p_associatedreceivertype(self, p):
- """associatedreceivertype : PENDING_ASSOCIATED_RECEIVER LANGLE identifier \
- RANGLE"""
+ """associatedreceivertype : PENDING_ASSOCIATED_RECEIVER LANGLE identifier RANGLE"""
p[0] = "rca<%s>" % p[3]
def p_handletype(self, p):
"""handletype : HANDLE
- | HANDLE LANGLE NAME RANGLE"""
+ | HANDLE LANGLE name_wrapped RANGLE"""
if len(p) == 2:
p[0] = p[1]
else:
@@ -342,14 +361,6 @@ class Parser(object):
"""associative_array : MAP LANGLE identifier COMMA typename RANGLE"""
p[0] = p[5] + "{" + p[3] + "}"
- def p_interfacerequest(self, p):
- """interfacerequest : identifier AMP
- | ASSOCIATED identifier AMP"""
- if len(p) == 3:
- p[0] = p[1] + "&"
- else:
- p[0] = "asso<" + p[2] + "&>"
-
def p_ordinal_1(self, p):
"""ordinal : """
p[0] = None
@@ -366,15 +377,14 @@ class Parser(object):
p[0] = ast.Ordinal(value, filename=self.filename, lineno=p.lineno(1))
def p_enum_1(self, p):
- """enum : attribute_section ENUM NAME LBRACE enum_value_list \
- RBRACE SEMI
- | attribute_section ENUM NAME LBRACE nonempty_enum_value_list \
- COMMA RBRACE SEMI"""
+ """enum : attribute_section ENUM name_wrapped LBRACE enum_value_list RBRACE SEMI
+ | attribute_section ENUM name_wrapped LBRACE \
+ nonempty_enum_value_list COMMA RBRACE SEMI"""
p[0] = ast.Enum(
p[3], p[1], p[5], filename=self.filename, lineno=p.lineno(2))
def p_enum_2(self, p):
- """enum : attribute_section ENUM NAME SEMI"""
+ """enum : attribute_section ENUM name_wrapped SEMI"""
p[0] = ast.Enum(
p[3], p[1], None, filename=self.filename, lineno=p.lineno(2))
@@ -396,9 +406,9 @@ class Parser(object):
p[0].Append(p[3])
def p_enum_value(self, p):
- """enum_value : attribute_section NAME
- | attribute_section NAME EQUALS int
- | attribute_section NAME EQUALS identifier_wrapped"""
+ """enum_value : attribute_section name_wrapped
+ | attribute_section name_wrapped EQUALS int
+ | attribute_section name_wrapped EQUALS identifier_wrapped"""
p[0] = ast.EnumValue(
p[2],
p[1],
@@ -407,7 +417,7 @@ class Parser(object):
lineno=p.lineno(2))
def p_const(self, p):
- """const : attribute_section CONST typename NAME EQUALS constant SEMI"""
+ """const : attribute_section CONST typename name_wrapped EQUALS constant SEMI"""
p[0] = ast.Const(p[4], p[1], p[3], p[6])
def p_constant(self, p):
@@ -422,10 +432,16 @@ class Parser(object):
# TODO(vtl): Make this produce a "wrapped" identifier (probably as an
# |ast.Identifier|, to be added) and get rid of identifier_wrapped.
def p_identifier(self, p):
- """identifier : NAME
- | NAME DOT identifier"""
+ """identifier : name_wrapped
+ | name_wrapped DOT identifier"""
p[0] = ''.join(p[1:])
+ # Allow 'feature' to be a name literal not just a keyword.
+ def p_name_wrapped(self, p):
+ """name_wrapped : NAME
+ | FEATURE"""
+ p[0] = p[1]
+
def p_literal(self, p):
"""literal : int
| float
@@ -458,6 +474,12 @@ class Parser(object):
# TODO(vtl): Can we figure out what's missing?
raise ParseError(self.filename, "Unexpected end of file")
+ if e.value == 'feature':
+ raise ParseError(self.filename,
+ "`feature` is reserved for a future mojom keyword",
+ lineno=e.lineno,
+ snippet=self._GetSnippet(e.lineno))
+
raise ParseError(
self.filename,
"Unexpected %r:" % e.value,
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
index 6d6b7153..0a26307b 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
@@ -1,17 +1,13 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import imp
-import os.path
-import sys
import unittest
from mojom.parse import ast
from mojom.parse import lexer
from mojom.parse import parser
-
class ParserTest(unittest.TestCase):
"""Tests |parser.Parse()|."""
@@ -1086,7 +1082,7 @@ class ParserTest(unittest.TestCase):
handle<data_pipe_producer>? k;
handle<message_pipe>? l;
handle<shared_buffer>? m;
- some_interface&? n;
+ pending_receiver<some_interface>? n;
handle<platform>? o;
};
"""
@@ -1110,7 +1106,7 @@ class ParserTest(unittest.TestCase):
ast.StructField('l', None, None, 'handle<message_pipe>?', None),
ast.StructField('m', None, None, 'handle<shared_buffer>?',
None),
- ast.StructField('n', None, None, 'some_interface&?', None),
+ ast.StructField('n', None, None, 'rcv<some_interface>?', None),
ast.StructField('o', None, None, 'handle<platform>?', None)
]))
])
@@ -1138,16 +1134,6 @@ class ParserTest(unittest.TestCase):
r" *handle\?<data_pipe_consumer> a;$"):
parser.Parse(source2, "my_file.mojom")
- source3 = """\
- struct MyStruct {
- some_interface?& a;
- };
- """
- with self.assertRaisesRegexp(
- parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected '&':\n"
- r" *some_interface\?& a;$"):
- parser.Parse(source3, "my_file.mojom")
-
def testSimpleUnion(self):
"""Tests a simple .mojom source that just defines a union."""
source = """\
@@ -1317,9 +1303,9 @@ class ParserTest(unittest.TestCase):
source1 = """\
struct MyStruct {
associated MyInterface a;
- associated MyInterface& b;
+ pending_associated_receiver<MyInterface> b;
associated MyInterface? c;
- associated MyInterface&? d;
+ pending_associated_receiver<MyInterface>? d;
};
"""
expected1 = ast.Mojom(None, ast.ImportList(), [
@@ -1327,16 +1313,16 @@ class ParserTest(unittest.TestCase):
'MyStruct', None,
ast.StructBody([
ast.StructField('a', None, None, 'asso<MyInterface>', None),
- ast.StructField('b', None, None, 'asso<MyInterface&>', None),
+ ast.StructField('b', None, None, 'rca<MyInterface>', None),
ast.StructField('c', None, None, 'asso<MyInterface>?', None),
- ast.StructField('d', None, None, 'asso<MyInterface&>?', None)
+ ast.StructField('d', None, None, 'rca<MyInterface>?', None)
]))
])
self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
source2 = """\
interface MyInterface {
- MyMethod(associated A a) =>(associated B& b);
+ MyMethod(associated A a) =>(pending_associated_receiver<B> b);
};"""
expected2 = ast.Mojom(None, ast.ImportList(), [
ast.Interface(
@@ -1344,10 +1330,10 @@ class ParserTest(unittest.TestCase):
ast.InterfaceBody(
ast.Method(
'MyMethod', None, None,
- ast.ParameterList(
- ast.Parameter('a', None, None, 'asso<A>')),
- ast.ParameterList(
- ast.Parameter('b', None, None, 'asso<B&>')))))
+ ast.ParameterList(ast.Parameter('a', None, None,
+ 'asso<A>')),
+ ast.ParameterList(ast.Parameter('b', None, None,
+ 'rca<B>')))))
])
self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
@@ -1385,6 +1371,5 @@ class ParserTest(unittest.TestCase):
r" *associated\? MyInterface& a;$"):
parser.Parse(source3, "my_file.mojom")
-
if __name__ == "__main__":
unittest.main()