summaryrefslogtreecommitdiff
path: root/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py
diff options
context:
space:
mode:
Diffstat (limited to 'utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py')
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py151
1 files changed, 130 insertions, 21 deletions
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