Mojom backend: Stop re-computing version info and field packing data.
We have removed most of the logic from pack.py because these computations are now being performed
in the front end. What remains in pack.py is some logic to re-organize the data from the
intermediate representation into a form that the code generators want. As we start to build
new-generation backends we can re-visit the exact shape of the data we put into the
intermediate representation and perhaps some more of this reorganization logic might be
moved to the front end:
- We left the PackedStruct object because this is what the code generators expect.
It is mostly populated now by copying data from the intermediate representation.
- We left the function GetByteLayout(). This function synthesizes the byte layout for a struct based on
equivalent data in the intermediate representation.
- The utility function GetPad() seems to be used directly by some of the code generators.
Summary of changes:
- In pack.py we delete GetAlignmentForKind, GetFieldOffset, GetVersionInfo, and most of the logic in the PackedStruct
constructor.
- We delete pack_tests.py and pack_unittest.py. These tests have been ported to Go tests in the frontend
in https://codereview.chromium.org/1833593002/.
- In mojom_translator.py we translate the version_info field of a struct and the offset, bit and
min_version fields of a struct field.
- In generator.py we stop creating synthetic request and response parameter structs and instead use
the ones translated from the intermediate representation. Similarly we stop computing struct.versions.
- We modify the logic in mojom_go_generatory.py so that rather than constructing a new object
to represent a synthetic parameter struct instead we use the object that has been translated
from the intermediate representation, because that object contains the packing data.
- In module.py we eliminate the min_versions property getter because the min_version property is now
part of the intermediate representation.
BUG=#713
R=azani@chromium.org
Review URL: https://codereview.chromium.org/1824263002 .
diff --git a/mojo/public/tools/bindings/generators/mojom_go_generator.py b/mojo/public/tools/bindings/generators/mojom_go_generator.py
index a295450..e9ea33f 100644
--- a/mojo/public/tools/bindings/generators/mojom_go_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_go_generator.py
@@ -158,7 +158,9 @@
mojom.Field,
mojom.Method,
mojom.Parameter)):
- return FormatName(element.name, exported)
+ element_name = (element.go_name if hasattr(element, "go_name")
+ else element.name)
+ return FormatName(element_name, exported)
if isinstance(element, (mojom.Enum,
mojom.Constant,
mojom.ConstantValue)):
@@ -423,23 +425,29 @@
return imports, mojom_imports
# Overrides the implementation from the base class in order to customize the
- # struct and field names.
+ # struct and field names. Since the Python objects representing the struct
+ # and fields are shared by all language generators we don't want to actually
+ # modify the |name| property. Instead we add a |go_name| property.
def _GetStructFromMethod(self, method):
- params_class = "%s_%s_Params" % (GetNameForElement(method.interface),
- GetNameForElement(method))
- struct = mojom.Struct(params_class, module=method.interface.module)
- for param in method.parameters:
- struct.AddField("in%s" % GetNameForElement(param),
- param.kind, param.ordinal, attributes=param.attributes)
- return self._AddStructComputedData(False, struct)
+ self._AddStructComputedData(False, method.param_struct)
+ # Only generate the go_names if they have not already been generated.
+ if not hasattr(method.param_struct, "go_name"):
+ method.param_struct.go_name = "%s_%s_Params" % (
+ GetNameForElement(method.interface), GetNameForElement(method))
+ for field in method.param_struct.fields:
+ field.go_name = "in%s" % GetNameForElement(field)
+ return method.param_struct
# Overrides the implementation from the base class in order to customize the
- # struct and field names.
+ # struct and field names. Since the Python objects representing the struct
+ # and fields are shared by all language generators we don't want to actually
+ # modify the |name| property. Instead we add a |go_name| property.
def _GetResponseStructFromMethod(self, method):
- params_class = "%s_%s_ResponseParams" % (
- GetNameForElement(method.interface), GetNameForElement(method))
- struct = mojom.Struct(params_class, module=method.interface.module)
- for param in method.response_parameters:
- struct.AddField("out%s" % GetNameForElement(param),
- param.kind, param.ordinal, attributes=param.attributes)
- return self._AddStructComputedData(False, struct)
+ self._AddStructComputedData(False, method.response_param_struct)
+ if not hasattr(method.response_param_struct, "go_name"):
+ # Only generate the go_names if they have not already been generated.
+ method.response_param_struct.go_name = "%s_%s_ResponseParams" % (
+ GetNameForElement(method.interface), GetNameForElement(method))
+ for field in method.response_param_struct.fields:
+ field.go_name = "out%s" % GetNameForElement(field)
+ return method.response_param_struct
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
index fec8871..1d57528 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
@@ -88,15 +88,20 @@
def _AddStructComputedData(self, exported, struct):
"""Adds computed data to the given struct. The data is computed once and
used repeatedly in the generation process."""
- struct.packed = pack.PackedStruct(struct)
- struct.bytes = pack.GetByteLayout(struct.packed)
- struct.versions = pack.GetVersionInfo(struct.packed)
+ if not hasattr(struct, 'packed') or struct.packed is None:
+ struct.packed = pack.PackedStruct(struct)
+ struct.bytes = pack.GetByteLayout(struct.packed)
struct.exported = exported
return struct
def _AddInterfaceComputedData(self, interface):
"""Adds computed data to the given interface. The data is computed once and
used repeatedly in the generation process."""
+ # Here we set the interface's |version| attribute to be the maximum value
+ # of the |min_version| attributes of all methods in the interface and all
+ # parameters in those methods.
+ # TODO(rudominer) Consider adding this value to the intermediate
+ # representation.
interface.version = 0
for method in interface.methods:
if method.min_version is not None:
@@ -116,19 +121,9 @@
return interface
def _GetStructFromMethod(self, method):
- """Converts a method's parameters into the fields of a struct."""
- params_class = "%s_%s_Params" % (method.interface.name, method.name)
- struct = mojom.Struct(params_class, module=method.interface.module)
- for param in method.parameters:
- struct.AddField(param.name, param.kind, param.ordinal,
- attributes=param.attributes)
- return self._AddStructComputedData(False, struct)
+ """Returns a method's parameters as a struct."""
+ return self._AddStructComputedData(False, method.param_struct)
def _GetResponseStructFromMethod(self, method):
- """Converts a method's response_parameters into the fields of a struct."""
- params_class = "%s_%s_ResponseParams" % (method.interface.name, method.name)
- struct = mojom.Struct(params_class, module=method.interface.module)
- for param in method.response_parameters:
- struct.AddField(param.name, param.kind, param.ordinal,
- attributes=param.attributes)
- return self._AddStructComputedData(False, struct)
+ """Returns a method's response_parameters as a struct."""
+ return self._AddStructComputedData(False, method.response_param_struct)
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/module.py b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
index 089ba8d..b4aa9d9 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/module.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
@@ -130,9 +130,6 @@
)
-ATTRIBUTE_MIN_VERSION = 'MinVersion'
-
-
class NamedValue(object):
def __init__(self, module=None, parent_kind=None, name=None):
self.module = module
@@ -195,11 +192,6 @@
self.default = default
self.attributes = attributes
- @property
- def min_version(self):
- return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
- if self.attributes else None
-
class StructField(Field): pass
@@ -333,11 +325,6 @@
self.default = default
self.attributes = attributes
- @property
- def min_version(self):
- return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
- if self.attributes else None
-
class Method(object):
def __init__(self, interface, name, ordinal=None, attributes=None):
@@ -362,11 +349,6 @@
self.response_parameters.append(parameter)
return parameter
- @property
- def min_version(self):
- return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
- if self.attributes else None
-
class Interface(ReferenceKind):
ReferenceKind.AddSharedProperty('module')
@@ -411,11 +393,6 @@
self.attributes = attributes
self.numeric_value = numeric_value
- @property
- def min_version(self):
- return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
- if self.attributes else None
-
class Enum(Kind):
def __init__(self, name=None, module=None, attributes=None):
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/mojom_translator.py b/mojo/public/tools/bindings/pylib/mojom/generate/mojom_translator.py
index caca052..061adef 100755
--- a/mojo/public/tools/bindings/pylib/mojom/generate/mojom_translator.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/mojom_translator.py
@@ -29,6 +29,7 @@
from generated import mojom_types_mojom
import module
import operator
+import pack
class FileTranslator(object):
@@ -192,12 +193,26 @@
"""
assert mojom_type.tag == mojom_types_mojom.UserDefinedType.Tags.struct_type
mojom_struct = mojom_type.struct_type
+ self.StructFromMojomStruct(struct, mojom_struct)
+
+ def StructFromMojomStruct(self, struct, mojom_struct):
+ """Populates a module.Struct based on a MojomStruct.
+
+ Args:
+ struct: {module.Struct} to be populated.
+ mojom_struct: {mojom_types.MojomStruct} to be translated.
+ """
self.PopulateUserDefinedType(struct, mojom_struct)
- # mojom_struct.fields is indexed by the field ordinals. We want
- # to capture these ordinals but sort struct.fields by declaration_order.
- struct.fields = [self.StructFieldFromMojom(ordinal, f) for (ordinal, f) in
- enumerate(mojom_struct.fields)]
+ # mojom_struct.fields is indexed by the field ordinals.
+ struct.fields_in_ordinal_order = [self.StructFieldFromMojom(ordinal, f)
+ for (ordinal, f) in enumerate(mojom_struct.fields)]
+ # We also want a list of fields sorted in declaration_order.
+ struct.fields = [f for f in struct.fields_in_ordinal_order]
struct.fields.sort(key=lambda field: field.declaration_order)
+
+ assert mojom_struct.version_info
+ struct.versions = [self.VersionInfoFromMojom(version) for version in
+ mojom_struct.version_info]
self.PopulateContainedDeclarationsFromMojom(
struct, mojom_struct.decl_data.contained_declarations)
@@ -239,6 +254,9 @@
"""
struct_field = module.StructField()
self.PopulateCommonFieldValues(struct_field, mojom_field)
+ struct_field.computed_offset = mojom_field.offset
+ struct_field.computed_bit = mojom_field.bit
+ struct_field.computed_min_version = mojom_field.min_version
# Note that the |ordinal| attribute of |struct_field| records only the
# *declared* ordinal and as such is not defined for every field whereas
# the |computed_ordinal| attribute is defined for every field. If
@@ -256,6 +274,18 @@
return struct_field
+ def VersionInfoFromMojom(self, mojom_version):
+ """Translates a mojom_types_mojom.StructVersion to a pack.VersionInfo
+ Args:
+ mojom_version: {mojom_types_mojom.StructVersion} to be translated.
+
+ Returns:
+ {pack.VersionInfo} translated from |mojom_version|.
+ """
+ return pack.VersionInfo(mojom_version.version_number,
+ mojom_version.num_fields, mojom_version.num_bytes)
+
+
def ParamFromMojom(self, mojom):
"""Translates a mojom_types_mojom.StructField to a module.Parameter.
@@ -454,11 +484,35 @@
"""
method = module.Method(interface, mojom_method.decl_data.short_name)
method.ordinal = mojom_method.ordinal
+ method.param_struct = module.Struct()
+ self.StructFromMojomStruct(method.param_struct, mojom_method.parameters)
+ # The name of a synthetic request parameter struct is not guaranteed by
+ # the frontend to be anything in particular so we set the name of the
+ # translated struct to a value that the code generators are expecting.
+ method.param_struct.name = "%s_%s_Params" % (
+ method.interface.name, method.name)
method.parameters = [self.ParamFromMojom(param)
for param in mojom_method.parameters.fields]
if mojom_method.response_params is not None:
+ method.response_param_struct = module.Struct()
+ self.StructFromMojomStruct(method.response_param_struct,
+ mojom_method.response_params)
+ # The name of a synthetic response parameter struct is not guaranteed by
+ # the frontend to be anything in particular so we set the name of the
+ # translated struct to a value that the code generators are expecting.
+ method.response_param_struct.name = "%s_%s_ResponseParams" % (
+ method.interface.name, method.name)
method.response_parameters = [self.ParamFromMojom(param)
for param in mojom_method.response_params.fields]
+
+ # Set the min_version attribute on the method.
+ method.min_version=None
+ # TODO(rudominer) For now we parse the "MinVersion" attribute here but
+ # after we add a min_version field to mojom_types_mojom.MojomMethod then
+ # we should take the value from there instead.
+ if method.attributes:
+ method.min_version=method.get('MinVersion')
+
return method
def ConstantFromValueKey(self, value_key):
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/mojom_translator_unittest.py b/mojo/public/tools/bindings/pylib/mojom/generate/mojom_translator_unittest.py
index c589c3c..6dece45 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/mojom_translator_unittest.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/mojom_translator_unittest.py
@@ -86,6 +86,7 @@
full_identifier='foo.AStruct',
source_file_info=mojom_types_mojom.SourceFileInfo(
file_name=file_name)))
+ add_version_info(mojom_struct, 0)
graph.resolved_types['struct_key'] = mojom_types_mojom.UserDefinedType(
struct_type=mojom_struct)
@@ -248,24 +249,39 @@
short_name='field03',
declaration_order=2),
type=mojom_types_mojom.Type(
- simple_type=mojom_types_mojom.SimpleType.BOOL)),
+ simple_type=mojom_types_mojom.SimpleType.BOOL),
+ offset=21,
+ bit=6,
+ min_version=11),
mojom_types_mojom.StructField(
decl_data=mojom_types_mojom.DeclarationData(
short_name='field01',
declared_ordinal=1,
declaration_order=0),
type=mojom_types_mojom.Type(
- simple_type=mojom_types_mojom.SimpleType.BOOL)),
+ simple_type=mojom_types_mojom.SimpleType.BOOL),
+ offset=17,
+ bit=1,
+ min_version=4),
mojom_types_mojom.StructField(
decl_data=mojom_types_mojom.DeclarationData(
short_name='field02',
declaration_order=1),
type=mojom_types_mojom.Type(
simple_type=mojom_types_mojom.SimpleType.DOUBLE),
+ offset=0,
+ bit=0,
+ min_version=0,
default_value=mojom_types_mojom.DefaultFieldValue(
value=mojom_types_mojom.Value(
literal_value=mojom_types_mojom.LiteralValue(double_value=15)))),
]
+ mojom_struct.version_info=[
+ mojom_types_mojom.StructVersion(
+ version_number=0, num_bytes=67, num_fields=1),
+ mojom_types_mojom.StructVersion(
+ version_number=1, num_bytes=76, num_fields=3),
+ ]
# mojom_fields_declaration_order lists, in declaration order, the indices
# of the fields in mojom_types_mojom.StructField.
mojom_fields_declaration_order = [1, 2, 0]
@@ -290,6 +306,20 @@
else:
self.assertEquals(None, f.ordinal)
self.assertEquals(gold_index, f.computed_ordinal)
+ self.assertEquals(gold.offset, f.computed_offset)
+ self.assertEquals(gold.bit, f.computed_bit)
+ self.assertEquals(gold.min_version, f.computed_min_version)
+ self.assertEquals(struct.fields_in_ordinal_order[index].name,
+ mojom_struct.fields[index].decl_data.short_name)
+
+ self.assertEquals(2, len(struct.versions))
+ for i in xrange(0, 2):
+ self.assertEquals(mojom_struct.version_info[i].version_number,
+ struct.versions[i].version)
+ self.assertEquals(mojom_struct.version_info[i].num_bytes,
+ struct.versions[i].num_bytes)
+ self.assertEquals(mojom_struct.version_info[i].num_fields,
+ struct.versions[i].num_fields)
self.assertEquals(module.BOOL, struct.fields[0].kind)
self.assertEquals(module.DOUBLE, struct.fields[1].kind)
@@ -315,6 +345,7 @@
short_name='AStruct',
source_file_info =mojom_types_mojom.SourceFileInfo(
file_name=file_name)))
+ add_version_info(mojom_struct, 0)
graph.resolved_types = {'struct_key': mojom_types_mojom.UserDefinedType(
struct_type=mojom_struct)}
@@ -384,6 +415,7 @@
short_name='AStruct',
source_file_info =mojom_types_mojom.SourceFileInfo(
file_name=file_name)))
+ add_version_info(mojom_struct, 0)
graph.resolved_types = {'struct_key': mojom_types_mojom.UserDefinedType(
struct_type=mojom_struct)}
@@ -522,6 +554,7 @@
source_file_info=mojom_types_mojom.SourceFileInfo(
file_name='root/c.mojom'))
mojom_struct.fields = []
+ add_version_info(mojom_struct, 0)
type_key = 'some_type_key'
graph.resolved_types = {
@@ -566,21 +599,27 @@
short_name='AMethod10',
source_file_info=mojom_types_mojom.SourceFileInfo(
file_name=file_name)),
- parameters=mojom_types_mojom.MojomStruct(fields=[]))
+ parameters=mojom_types_mojom.MojomStruct(fields=[],
+ version_info=build_version_info(0),
+ decl_data=build_decl_data('AMethod10_Request')))
mojom_method0 = mojom_types_mojom.MojomMethod(
ordinal=0,
decl_data=mojom_types_mojom.DeclarationData(
short_name='AMethod0',
source_file_info=mojom_types_mojom.SourceFileInfo(
file_name=file_name)),
- parameters=mojom_types_mojom.MojomStruct(fields=[]))
+ parameters=mojom_types_mojom.MojomStruct(fields=[],
+ version_info=build_version_info(0),
+ decl_data=build_decl_data('AMethod0_Request')))
mojom_method7 = mojom_types_mojom.MojomMethod(
ordinal=7,
decl_data=mojom_types_mojom.DeclarationData(
- short_name='AMethod10',
+ short_name='AMethod7',
source_file_info=mojom_types_mojom.SourceFileInfo(
file_name=file_name)),
- parameters=mojom_types_mojom.MojomStruct(fields=[]))
+ parameters=mojom_types_mojom.MojomStruct(fields=[],
+ version_info=build_version_info(0),
+ decl_data=build_decl_data('AMethod7_Request')))
mojom_interface.methods = {10: mojom_method10, 0: mojom_method0,
7: mojom_method7}
@@ -614,15 +653,23 @@
param1 = mojom_types_mojom.StructField(
decl_data=mojom_types_mojom.DeclarationData(short_name='a_param'),
type=mojom_types_mojom.Type(
- simple_type=mojom_types_mojom.SimpleType.UINT32))
+ simple_type=mojom_types_mojom.SimpleType.UINT32),
+ offset=21,
+ bit=6,
+ min_version=11)
param2 = mojom_types_mojom.StructField(
decl_data=mojom_types_mojom.DeclarationData(short_name='b_param'),
type=mojom_types_mojom.Type(
- simple_type=mojom_types_mojom.SimpleType.UINT64))
+ simple_type=mojom_types_mojom.SimpleType.UINT64),
+ offset=22,
+ bit=7,
+ min_version=12)
mojom_method.parameters = mojom_types_mojom.MojomStruct(
- fields=[param1, param2])
+ fields=[param1, param2],
+ version_info=build_version_info(2),
+ decl_data=build_decl_data('Not used'))
- interface = module.Interface()
+ interface = module.Interface('MyInterface')
graph = mojom_files_mojom.MojomFileGraph()
translator = mojom_translator.FileTranslator(graph, file_name)
method = translator.MethodFromMojom(mojom_method, interface)
@@ -635,9 +682,21 @@
len(mojom_method.parameters.fields), len(method.parameters))
self.assertEquals(param1.decl_data.short_name, method.parameters[0].name)
self.assertEquals(param2.decl_data.short_name, method.parameters[1].name)
+ self.assertEquals('MyInterface_AMethod_Params', method.param_struct.name)
+ self.assertEquals(len(mojom_method.parameters.fields),
+ len(method.param_struct.fields))
+ for i in xrange(0, len(mojom_method.parameters.fields)):
+ gold = mojom_method.parameters.fields[i]
+ f = method.param_struct.fields_in_ordinal_order[i]
+ self.assertEquals(gold.decl_data.short_name, f.name)
+ self.assertEquals(gold.offset, f.computed_offset)
+ self.assertEquals(gold.bit, f.computed_bit)
+ self.assertEquals(gold.min_version, f.computed_min_version)
# Add empty return params.
mojom_method.response_params = mojom_types_mojom.MojomStruct(fields=[])
+ add_version_info(mojom_method.response_params, 0)
+ add_decl_data(mojom_method.response_params, 'AMethod_Response')
method = translator.MethodFromMojom(mojom_method, interface)
self.assertEquals([], method.response_parameters)
@@ -967,6 +1026,7 @@
type=mojom_types_mojom.Type(
type_reference=mojom_types_mojom.TypeReference(type_key=type_key)))
]
+ add_version_info(mojom_struct, 1)
graph.resolved_types = {
type_key: mojom_types_mojom.UserDefinedType(struct_type=mojom_struct)}
@@ -992,3 +1052,50 @@
if __name__ == '__main__':
unittest.main()
+
+def build_decl_data(short_name):
+ """Builds and returns a DeclarationData with the given short_name.
+
+ Args:
+ short_name: {str} short_name to use
+
+ Returns:
+ {mojom_types_mojom.DeclarationData} With the given short_name
+ """
+ return mojom_types_mojom.DeclarationData(short_name=short_name)
+
+def add_decl_data(element, short_name):
+ """Builds a DeclarationData with the given short_name and adds it
+ as the |decl_data| attribute of |element|.
+
+ Args:
+ element: {any} The Python object to which a |decl_data| attribute will be
+ added.
+ short_name: {str} short_name to use
+ """
+ element.decl_data=build_decl_data(short_name)
+
+def build_version_info(num_fields):
+ """Builds and returns a list containing a single StructVersion with
+ version_number=0, num_bytes=0, and the given value for num_fields.
+
+ Args:
+ num_fields: {int} The value of num_fields to use.
+ Returns:
+ {[mojom_types_mojom.StructVersion]} Containing a single element with
+ the given value for num_fields.
+ """
+ return [mojom_types_mojom.StructVersion(
+ version_number=0, num_bytes=0,num_fields=num_fields)]
+
+def add_version_info(mojom_struct, num_fields):
+ """Builds a list containing a single StructVersion with
+ version_number=0, num_bytes=0, and the given value for num_fields. Adds this
+ as the |version_info| attribute of |mojom_struct|.
+
+ Args:
+ mojom_struct: {any} The Python object to which a |version_info| attribute
+ will be added.
+ num_fields: {int} The value of num_fields to use.
+ """
+ mojom_struct.version_info=build_version_info(num_fields)
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/pack.py b/mojo/public/tools/bindings/pylib/mojom/generate/pack.py
index b034f97..c898be0 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/pack.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/pack.py
@@ -57,29 +57,18 @@
raise Exception("Invalid kind: %s" % kind.spec)
return cls.kind_to_size[kind]
- @classmethod
- def GetAlignmentForKind(cls, kind):
- if isinstance(kind, mojom.Interface):
- return 4
- if isinstance(kind, mojom.Union):
- return 8
- return cls.GetSizeForKind(kind)
-
- def __init__(self, field, index, ordinal):
+ def __init__(self, field):
"""
Args:
field: the original field.
- index: the position of the original field in the struct.
- ordinal: the ordinal of the field for serialization.
"""
self.field = field
- self.index = index
- self.ordinal = ordinal
+ self.index = field.declaration_order
+ self.ordinal = field.computed_ordinal
self.size = self.GetSizeForKind(field.kind)
- self.alignment = self.GetAlignmentForKind(field.kind)
- self.offset = None
- self.bit = None
- self.min_version = None
+ self.offset = field.computed_offset
+ self.bit = field.computed_bit
+ self.min_version = field.computed_min_version
def GetPad(offset, alignment):
@@ -88,94 +77,20 @@
return (alignment - (offset % alignment)) % alignment
-def GetFieldOffset(field, last_field):
- """Returns a 2-tuple of the field offset and bit (for BOOLs)."""
- if (field.field.kind == mojom.BOOL and
- last_field.field.kind == mojom.BOOL and
- last_field.bit < 7):
- return (last_field.offset, last_field.bit + 1)
-
- offset = last_field.offset + last_field.size
- pad = GetPad(offset, field.alignment)
- return (offset + pad, 0)
-
-
-def GetPayloadSizeUpToField(field):
- """Returns the payload size (not including struct header) if |field| is the
- last field.
- """
- if not field:
- return 0
- offset = field.offset + field.size
- pad = GetPad(offset, 8)
- return offset + pad
-
-
class PackedStruct(object):
def __init__(self, struct):
self.struct = struct
- # |packed_fields| contains all the fields, in increasing offset order.
- self.packed_fields = []
- # |packed_fields_in_ordinal_order| refers to the same fields as
- # |packed_fields|, but in ordinal order.
- self.packed_fields_in_ordinal_order = []
- # No fields.
- if (len(struct.fields) == 0):
- return
+ # |packed_fields_in_ordinal_order| contains all the fields,
+ # in ordinal order.
+ self.packed_fields_in_ordinal_order = [PackedField(field)
+ for field in struct.fields_in_ordinal_order]
- # Start by sorting by ordinal.
- src_fields = self.packed_fields_in_ordinal_order
- ordinal = 0
- for index, field in enumerate(struct.fields):
- if field.ordinal is not None:
- ordinal = field.ordinal
- src_fields.append(PackedField(field, index, ordinal))
- ordinal += 1
- src_fields.sort(key=lambda field: field.ordinal)
-
- # Set |min_version| for each field.
- next_min_version = 0
- for packed_field in src_fields:
- if packed_field.field.min_version is None:
- assert next_min_version == 0
- else:
- assert packed_field.field.min_version >= next_min_version
- next_min_version = packed_field.field.min_version
- packed_field.min_version = next_min_version
-
- 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))
-
- src_field = src_fields[0]
- src_field.offset = 0
- src_field.bit = 0
- dst_fields = self.packed_fields
- dst_fields.append(src_field)
-
- # Then find first slot that each field will fit.
- for src_field in src_fields[1:]:
- last_field = dst_fields[0]
- for i in xrange(1, len(dst_fields)):
- next_field = dst_fields[i]
- offset, bit = GetFieldOffset(src_field, last_field)
- if offset + src_field.size <= next_field.offset:
- # Found hole.
- src_field.offset = offset
- src_field.bit = bit
- dst_fields.insert(i, src_field)
- break
- last_field = next_field
- if src_field.offset is None:
- # Add to end
- src_field.offset, src_field.bit = GetFieldOffset(src_field, last_field)
- dst_fields.append(src_field)
-
+ # |packed_fields| refers to the same fields as
+ # |packed_fields_in_ordinal_order|, but in increasing offset order.
+ self.packed_fields = [field for field in
+ self.packed_fields_in_ordinal_order]
+ self.packed_fields.sort(key=lambda f: (f.offset, f.bit))
class ByteInfo(object):
def __init__(self):
@@ -184,8 +99,7 @@
def GetByteLayout(packed_struct):
- total_payload_size = GetPayloadSizeUpToField(
- packed_struct.packed_fields[-1] if packed_struct.packed_fields else None)
+ total_payload_size = packed_struct.struct.versions[-1].num_bytes - HEADER_SIZE
bytes = [ByteInfo() for i in xrange(total_payload_size)]
limit_of_previous_field = 0
@@ -211,38 +125,3 @@
self.num_fields = num_fields
self.num_bytes = num_bytes
-
-def GetVersionInfo(packed_struct):
- """Get version information for a struct.
-
- Args:
- packed_struct: A PackedStruct instance.
-
- Returns:
- A non-empty list of VersionInfo instances, sorted by version in increasing
- order.
- Note: The version numbers may not be consecutive.
- """
- versions = []
- last_version = 0
- last_num_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,
- last_payload_size + HEADER_SIZE))
- last_version = packed_field.min_version
-
- last_num_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)
-
- assert len(versions) == 0 or last_num_fields != versions[-1].num_fields
- versions.append(VersionInfo(last_version, last_num_fields,
- last_payload_size + HEADER_SIZE))
- return versions
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py
deleted file mode 100644
index e30a2ab..0000000
--- a/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py
+++ /dev/null
@@ -1,193 +0,0 @@
-# Copyright 2013 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.
-
-import sys
-
-import module as mojom
-import pack
-import test_support
-
-
-EXPECT_EQ = test_support.EXPECT_EQ
-EXPECT_TRUE = test_support.EXPECT_TRUE
-RunTest = test_support.RunTest
-
-
-def TestOrdinalOrder():
- errors = 0
- struct = mojom.Struct('test')
- struct.AddField('testfield1', mojom.INT32, 2)
- struct.AddField('testfield2', mojom.INT32, 1)
- ps = pack.PackedStruct(struct)
-
- errors += EXPECT_EQ(2, len(ps.packed_fields))
- errors += EXPECT_EQ('testfield2', ps.packed_fields[0].field.name)
- errors += EXPECT_EQ('testfield1', ps.packed_fields[1].field.name)
-
- return errors
-
-def TestZeroFields():
- errors = 0
- struct = mojom.Struct('test')
- ps = pack.PackedStruct(struct)
- errors += EXPECT_EQ(0, len(ps.packed_fields))
- return errors
-
-
-def TestOneField():
- errors = 0
- struct = mojom.Struct('test')
- struct.AddField('testfield1', mojom.INT8)
- ps = pack.PackedStruct(struct)
- errors += EXPECT_EQ(1, len(ps.packed_fields))
- return errors
-
-# Pass three tuples.
-# |kinds| is a sequence of mojom.Kinds that specify the fields that are to
-# be created.
-# |fields| is the expected order of the resulting fields, with the integer
-# "1" first.
-# |offsets| is the expected order of offsets, with the integer "0" first.
-def TestSequence(kinds, fields, offsets):
- errors = 0
- struct = mojom.Struct('test')
- index = 1
- for kind in kinds:
- struct.AddField("%d" % index, kind)
- index += 1
- ps = pack.PackedStruct(struct)
- num_fields = len(ps.packed_fields)
- errors += EXPECT_EQ(len(kinds), num_fields)
- for i in xrange(num_fields):
- EXPECT_EQ("%d" % fields[i], ps.packed_fields[i].field.name)
- EXPECT_EQ(offsets[i], ps.packed_fields[i].offset)
-
- return errors
-
-
-def TestPaddingPackedInOrder():
- return TestSequence(
- (mojom.INT8, mojom.UINT8, mojom.INT32),
- (1, 2, 3),
- (0, 1, 4))
-
-
-def TestPaddingPackedOutOfOrder():
- return TestSequence(
- (mojom.INT8, mojom.INT32, mojom.UINT8),
- (1, 3, 2),
- (0, 1, 4))
-
-
-def TestPaddingPackedOverflow():
- kinds = (mojom.INT8, mojom.INT32, mojom.INT16, mojom.INT8, mojom.INT8)
- # 2 bytes should be packed together first, followed by short, then by int.
- fields = (1, 4, 3, 2, 5)
- offsets = (0, 1, 2, 4, 8)
- return TestSequence(kinds, fields, offsets)
-
-
-def TestNullableTypes():
- kinds = (mojom.STRING.MakeNullableKind(),
- mojom.HANDLE.MakeNullableKind(),
- mojom.Struct('test_struct').MakeNullableKind(),
- mojom.DCPIPE.MakeNullableKind(),
- mojom.Array().MakeNullableKind(),
- mojom.DPPIPE.MakeNullableKind(),
- mojom.Array(length=5).MakeNullableKind(),
- mojom.MSGPIPE.MakeNullableKind(),
- mojom.Interface('test_inteface').MakeNullableKind(),
- mojom.SHAREDBUFFER.MakeNullableKind(),
- mojom.InterfaceRequest().MakeNullableKind())
- fields = (1, 2, 4, 3, 5, 6, 8, 7, 9, 10, 11)
- offsets = (0, 8, 12, 16, 24, 32, 36, 40, 48, 56, 60)
- return TestSequence(kinds, fields, offsets)
-
-
-def TestAllTypes():
- return TestSequence(
- (mojom.BOOL, mojom.INT8, mojom.STRING, mojom.UINT8,
- mojom.INT16, mojom.DOUBLE, mojom.UINT16,
- mojom.INT32, mojom.UINT32, mojom.INT64,
- mojom.FLOAT, mojom.STRING, mojom.HANDLE,
- mojom.UINT64, mojom.Struct('test'), mojom.Array(),
- mojom.STRING.MakeNullableKind()),
- (1, 2, 4, 5, 7, 3, 6, 8, 9, 10, 11, 13, 12, 14, 15, 16, 17, 18),
- (0, 1, 2, 4, 6, 8, 16, 24, 28, 32, 40, 44, 48, 56, 64, 72, 80))
-
-
-def TestPaddingPackedOutOfOrderByOrdinal():
- errors = 0
- struct = mojom.Struct('test')
- struct.AddField('testfield1', mojom.INT8)
- struct.AddField('testfield3', mojom.UINT8, 3)
- struct.AddField('testfield2', mojom.INT32, 2)
- ps = pack.PackedStruct(struct)
- errors += EXPECT_EQ(3, len(ps.packed_fields))
-
- # Second byte should be packed in behind first, altering order.
- errors += EXPECT_EQ('testfield1', ps.packed_fields[0].field.name)
- errors += EXPECT_EQ('testfield3', ps.packed_fields[1].field.name)
- errors += EXPECT_EQ('testfield2', ps.packed_fields[2].field.name)
-
- # Second byte should be packed with first.
- errors += EXPECT_EQ(0, ps.packed_fields[0].offset)
- errors += EXPECT_EQ(1, ps.packed_fields[1].offset)
- errors += EXPECT_EQ(4, ps.packed_fields[2].offset)
-
- return errors
-
-
-def TestBools():
- errors = 0
- struct = mojom.Struct('test')
- struct.AddField('bit0', mojom.BOOL)
- struct.AddField('bit1', mojom.BOOL)
- struct.AddField('int', mojom.INT32)
- struct.AddField('bit2', mojom.BOOL)
- struct.AddField('bit3', mojom.BOOL)
- struct.AddField('bit4', mojom.BOOL)
- struct.AddField('bit5', mojom.BOOL)
- struct.AddField('bit6', mojom.BOOL)
- struct.AddField('bit7', mojom.BOOL)
- struct.AddField('bit8', mojom.BOOL)
- ps = pack.PackedStruct(struct)
- errors += EXPECT_EQ(10, len(ps.packed_fields))
-
- # First 8 bits packed together.
- for i in xrange(8):
- pf = ps.packed_fields[i]
- errors += EXPECT_EQ(0, pf.offset)
- errors += EXPECT_EQ("bit%d" % i, pf.field.name)
- errors += EXPECT_EQ(i, pf.bit)
-
- # Ninth bit goes into second byte.
- errors += EXPECT_EQ("bit8", ps.packed_fields[8].field.name)
- errors += EXPECT_EQ(1, ps.packed_fields[8].offset)
- errors += EXPECT_EQ(0, ps.packed_fields[8].bit)
-
- # int comes last.
- errors += EXPECT_EQ("int", ps.packed_fields[9].field.name)
- errors += EXPECT_EQ(4, ps.packed_fields[9].offset)
-
- return errors
-
-
-def Main(args):
- errors = 0
- errors += RunTest(TestZeroFields)
- errors += RunTest(TestOneField)
- errors += RunTest(TestPaddingPackedInOrder)
- errors += RunTest(TestPaddingPackedOutOfOrder)
- errors += RunTest(TestPaddingPackedOverflow)
- errors += RunTest(TestNullableTypes)
- errors += RunTest(TestAllTypes)
- errors += RunTest(TestPaddingPackedOutOfOrderByOrdinal)
- errors += RunTest(TestBools)
-
- return errors
-
-
-if __name__ == '__main__':
- sys.exit(Main(sys.argv[1:]))
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py
index b439119..3ed50a9 100755
--- a/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py
@@ -23,7 +23,6 @@
def main(args):
errors = 0
errors += TestMojom('module_tests.py', ['--test'])
- errors += TestMojom('pack_tests.py', ['--test'])
if errors:
print '\nFailed tests.'
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/pack_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/pack_unittest.py
deleted file mode 100644
index 692e2f7..0000000
--- a/mojo/public/tools/bindings/pylib/mojom_tests/generate/pack_unittest.py
+++ /dev/null
@@ -1,126 +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.
-
-import imp
-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)."""
- path = os.path.abspath(__file__)
- while True:
- path, tail = os.path.split(path)
- assert tail
- if tail == dirname:
- return path
-
-
-try:
- imp.find_module("mojom")
-except ImportError:
- sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
-from mojom.generate import pack
-from mojom.generate import module as mojom
-
-
-# TODO(yzshen): Move tests in pack_tests.py here.
-class PackTest(unittest.TestCase):
- def _CheckPackSequence(self, kinds, fields, offsets):
- """Checks the pack order and offsets of a sequence of mojom.Kinds.
-
- Args:
- kinds: A sequence of mojom.Kinds that specify the fields that are to be
- created.
- fields: The expected order of the resulting fields, with the integer "1"
- first.
- offsets: The expected order of offsets, with the integer "0" first.
- """
- struct = mojom.Struct('test')
- index = 1
- for kind in kinds:
- struct.AddField('%d' % index, kind)
- index += 1
- ps = pack.PackedStruct(struct)
- num_fields = len(ps.packed_fields)
- self.assertEquals(len(kinds), num_fields)
- for i in xrange(num_fields):
- self.assertEquals('%d' % fields[i], ps.packed_fields[i].field.name)
- self.assertEquals(offsets[i], ps.packed_fields[i].offset)
-
- def testMinVersion(self):
- """Tests that |min_version| is properly set for packed fields."""
- struct = mojom.Struct('test')
- struct.AddField('field_2', mojom.BOOL, 2)
- struct.AddField('field_0', mojom.INT32, 0)
- struct.AddField('field_1', mojom.INT64, 1)
- ps = pack.PackedStruct(struct)
-
- self.assertEquals('field_0', ps.packed_fields[0].field.name)
- self.assertEquals('field_2', ps.packed_fields[1].field.name)
- self.assertEquals('field_1', ps.packed_fields[2].field.name)
-
- self.assertEquals(0, ps.packed_fields[0].min_version)
- self.assertEquals(0, ps.packed_fields[1].min_version)
- self.assertEquals(0, ps.packed_fields[2].min_version)
-
- struct.fields[0].attributes = {'MinVersion': 1}
- ps = pack.PackedStruct(struct)
-
- self.assertEquals(0, ps.packed_fields[0].min_version)
- self.assertEquals(1, ps.packed_fields[1].min_version)
- self.assertEquals(0, ps.packed_fields[2].min_version)
-
- def testGetVersionInfoEmptyStruct(self):
- """Tests that pack.GetVersionInfo() never returns an empty list, even for
- empty structs.
- """
- struct = mojom.Struct('test')
- ps = pack.PackedStruct(struct)
-
- versions = pack.GetVersionInfo(ps)
- self.assertEquals(1, len(versions))
- self.assertEquals(0, versions[0].version)
- self.assertEquals(0, versions[0].num_fields)
- self.assertEquals(8, versions[0].num_bytes)
-
- def testGetVersionInfoComplexOrder(self):
- """Tests pack.GetVersionInfo() using a struct whose definition order,
- ordinal order and pack order for fields are all different.
- """
- struct = mojom.Struct('test')
- struct.AddField('field_3', mojom.BOOL, ordinal=3,
- attributes={'MinVersion': 3})
- struct.AddField('field_0', mojom.INT32, ordinal=0)
- struct.AddField('field_1', mojom.INT64, ordinal=1,
- attributes={'MinVersion': 2})
- struct.AddField('field_2', mojom.INT64, ordinal=2,
- attributes={'MinVersion': 3})
- ps = pack.PackedStruct(struct)
-
- versions = pack.GetVersionInfo(ps)
- self.assertEquals(3, len(versions))
-
- self.assertEquals(0, versions[0].version)
- self.assertEquals(1, versions[0].num_fields)
- self.assertEquals(16, versions[0].num_bytes)
-
- self.assertEquals(2, versions[1].version)
- self.assertEquals(2, versions[1].num_fields)
- self.assertEquals(24, versions[1].num_bytes)
-
- self.assertEquals(3, versions[2].version)
- self.assertEquals(4, versions[2].num_fields)
- self.assertEquals(32, versions[2].num_bytes)
-
- def testInterfaceAlignment(self):
- """Tests that interfaces are aligned on 4-byte boundaries, although the size
- of an interface is 8 bytes.
- """
- kinds = (mojom.INT32, mojom.Interface('test_interface'))
- fields = (1, 2)
- offsets = (0, 4)
- self._CheckPackSequence(kinds, fields, offsets)