Fix delimited inheritance in all languages.

This was previously fixed in C++ (https://github.com/protocolbuffers/protobuf/issues/16549), but not ported to other languages.  Delimited field encoding can be inherited by fields where it's invalid, such as non-messages and maps.  In these cases, the encoding should be ignored and length-prefixed should be used.

PiperOrigin-RevId: 642792988
pull/17129/head
Mike Kruskal 8 months ago
parent eb61e6b666
commit c4f359ebf0
  1. 32
      conformance/binary_json_conformance_suite.cc
  2. 30
      conformance/test_protos/test_messages_edition2023.proto
  3. 5
      csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
  4. 5
      java/core/src/main/java/com/google/protobuf/Descriptors.java
  5. 3
      python/google/protobuf/descriptor.py
  6. 5
      python/google/protobuf/text_format.py
  7. 22
      upb/reflection/field_def.c

@ -155,6 +155,11 @@ std::string group(uint32_t fieldnum, std::string content) {
tag(fieldnum, WireFormatLite::WIRETYPE_END_GROUP));
}
std::string len(uint32_t fieldnum, std::string content) {
return absl::StrCat(tag(fieldnum, WireFormatLite::WIRETYPE_LENGTH_DELIMITED),
delim(content));
}
std::string GetDefaultValue(FieldDescriptor::Type type) {
switch (type) {
case FieldDescriptor::TYPE_INT32:
@ -363,6 +368,33 @@ void BinaryAndJsonConformanceSuite::RunDelimitedFieldTests() {
TestAllTypesEdition2023 prototype;
SetTypeUrl(GetTypeUrl(TestAllTypesEdition2023::GetDescriptor()));
RunValidProtobufTest<TestAllTypesEdition2023>(
absl::StrCat("ValidNonMessage"), REQUIRED,
field(1, WireFormatLite::WIRETYPE_VARINT, varint(99)),
R"pb(optional_int32: 99)pb");
RunValidProtobufTest<TestAllTypesEdition2023>(
absl::StrCat("ValidLengthPrefixedField"), REQUIRED,
len(18, field(1, WireFormatLite::WIRETYPE_VARINT, varint(99))),
R"pb(optional_nested_message { a: 99 })pb");
RunValidProtobufTest<TestAllTypesEdition2023>(
absl::StrCat("ValidMap.Integer"), REQUIRED,
len(56,
absl::StrCat(field(1, WireFormatLite::WIRETYPE_VARINT, varint(99)),
field(2, WireFormatLite::WIRETYPE_VARINT, varint(87)))),
R"pb(map_int32_int32 { key: 99 value: 87 })pb");
RunValidProtobufTest<TestAllTypesEdition2023>(
absl::StrCat("ValidMap.LengthPrefixed"), REQUIRED,
len(71, absl::StrCat(len(1, "a"),
len(2, field(1, WireFormatLite::WIRETYPE_VARINT,
varint(87))))),
R"pb(map_string_nested_message {
key: "a"
value: { a: 87 }
})pb");
RunValidProtobufTest<TestAllTypesEdition2023>(
absl::StrCat("ValidDelimitedField.GroupLike"), REQUIRED,
group(201, field(202, WireFormatLite::WIRETYPE_VARINT, varint(99))),

@ -2,6 +2,7 @@ edition = "2023";
package protobuf_test_messages.editions;
option features.message_encoding = DELIMITED;
option java_package = "com.google.protobuf_test_messages.edition2023";
option java_multiple_files = true;
option objc_class_prefix = "Editions";
@ -9,7 +10,8 @@ option objc_class_prefix = "Editions";
message TestAllTypesEdition2023 {
message NestedMessage {
int32 a = 1;
TestAllTypesEdition2023 corecursive = 2;
TestAllTypesEdition2023 corecursive = 2
[features.message_encoding = LENGTH_PREFIXED];
}
enum NestedEnum {
@ -36,8 +38,10 @@ message TestAllTypesEdition2023 {
string optional_string = 14;
bytes optional_bytes = 15;
NestedMessage optional_nested_message = 18;
ForeignMessageEdition2023 optional_foreign_message = 19;
NestedMessage optional_nested_message = 18
[features.message_encoding = LENGTH_PREFIXED];
ForeignMessageEdition2023 optional_foreign_message = 19
[features.message_encoding = LENGTH_PREFIXED];
NestedEnum optional_nested_enum = 21;
ForeignEnumEdition2023 optional_foreign_enum = 22;
@ -45,7 +49,8 @@ message TestAllTypesEdition2023 {
string optional_string_piece = 24 [ctype = STRING_PIECE];
string optional_cord = 25 [ctype = CORD];
TestAllTypesEdition2023 recursive_message = 27;
TestAllTypesEdition2023 recursive_message = 27
[features.message_encoding = LENGTH_PREFIXED];
// Repeated
repeated int32 repeated_int32 = 31;
@ -64,8 +69,10 @@ message TestAllTypesEdition2023 {
repeated string repeated_string = 44;
repeated bytes repeated_bytes = 45;
repeated NestedMessage repeated_nested_message = 48;
repeated ForeignMessageEdition2023 repeated_foreign_message = 49;
repeated NestedMessage repeated_nested_message = 48
[features.message_encoding = LENGTH_PREFIXED];
repeated ForeignMessageEdition2023 repeated_foreign_message = 49
[features.message_encoding = LENGTH_PREFIXED];
repeated NestedEnum repeated_nested_enum = 51;
repeated ForeignEnumEdition2023 repeated_foreign_enum = 52;
@ -152,7 +159,8 @@ message TestAllTypesEdition2023 {
oneof oneof_field {
uint32 oneof_uint32 = 111;
NestedMessage oneof_nested_message = 112;
NestedMessage oneof_nested_message = 112
[features.message_encoding = LENGTH_PREFIXED];
string oneof_string = 113;
bytes oneof_bytes = 114;
bool oneof_bool = 115;
@ -170,8 +178,8 @@ message TestAllTypesEdition2023 {
int32 group_int32 = 202;
uint32 group_uint32 = 203;
}
GroupLikeType groupliketype = 201 [features.message_encoding = DELIMITED];
GroupLikeType delimited_field = 202 [features.message_encoding = DELIMITED];
GroupLikeType groupliketype = 201;
GroupLikeType delimited_field = 202;
}
message ForeignMessageEdition2023 {
@ -193,6 +201,6 @@ message GroupLikeType {
}
extend TestAllTypesEdition2023 {
GroupLikeType groupliketype = 121 [features.message_encoding = DELIMITED];
GroupLikeType delimited_ext = 122 [features.message_encoding = DELIMITED];
GroupLikeType groupliketype = 121;
GroupLikeType delimited_ext = 122;
}

@ -407,6 +407,11 @@ namespace Google.Protobuf.Reflection
throw new DescriptorValidationException(this, $"\"{Proto.TypeName}\" is not a message type.");
}
messageType = m;
if (m.Proto.Options?.MapEntry == true || ContainingType?.Proto.Options?.MapEntry == true)
{
// Maps can't inherit delimited encoding.
FieldType = FieldType.Message;
}
if (Proto.HasDefaultValue)
{

@ -1297,6 +1297,8 @@ public final class Descriptors {
// since these are used before feature resolution when parsing java feature set defaults
// (custom options) into unknown fields.
if (type == Type.MESSAGE
&& !(messageType != null && messageType.toProto().getOptions().getMapEntry())
&& !(containingType != null && containingType.toProto().getOptions().getMapEntry())
&& this.features != null
&& getFeatures().getMessageEncoding() == FeatureSet.MessageEncoding.DELIMITED) {
return Type.GROUP;
@ -1476,8 +1478,7 @@ public final class Descriptors {
* been upgraded to editions.
*/
boolean isGroupLike() {
if (getFeatures().getMessageEncoding()
!= DescriptorProtos.FeatureSet.MessageEncoding.DELIMITED) {
if (getType() != Type.GROUP) {
// Groups are always tag-delimited.
return false;
}

@ -708,6 +708,9 @@ class FieldDescriptor(DescriptorBase):
if (
self._GetFeatures().message_encoding
== _FEATURESET_MESSAGE_ENCODING_DELIMITED
and self.message_type
and not self.message_type.GetOptions().map_entry
and not self.containing_type.GetOptions().map_entry
):
return FieldDescriptor.TYPE_GROUP
return self._type

@ -195,10 +195,7 @@ def _IsGroupLike(field):
True if this field is group-like, false otherwise.
"""
# Groups are always tag-delimited.
if (
field._GetFeatures().message_encoding
!= descriptor._FEATURESET_MESSAGE_ENCODING_DELIMITED
):
if field.type != descriptor.FieldDescriptor.TYPE_GROUP:
return False
# Group fields always are always the lowercase type name.

@ -252,8 +252,7 @@ bool _upb_FieldDef_ValidateUtf8(const upb_FieldDef* f) {
bool _upb_FieldDef_IsGroupLike(const upb_FieldDef* f) {
// Groups are always tag-delimited.
if (UPB_DESC(FeatureSet_message_encoding)(upb_FieldDef_ResolvedFeatures(f)) !=
UPB_DESC(FeatureSet_DELIMITED)) {
if (f->type_ != kUpb_FieldType_Group) {
return false;
}
@ -687,12 +686,6 @@ static void _upb_FieldDef_Create(upb_DefBuilder* ctx, const char* prefix,
UPB_DESC(FieldDescriptorProto_has_type_name)(field_proto);
f->type_ = (int)UPB_DESC(FieldDescriptorProto_type)(field_proto);
if (f->type_ == kUpb_FieldType_Message &&
// TODO: remove once we can deprecate kUpb_FieldType_Group.
UPB_DESC(FeatureSet_message_encoding)(f->resolved_features) ==
UPB_DESC(FeatureSet_DELIMITED)) {
f->type_ = kUpb_FieldType_Group;
}
if (has_type) {
switch (f->type_) {
@ -713,7 +706,7 @@ static void _upb_FieldDef_Create(upb_DefBuilder* ctx, const char* prefix,
}
}
if (!has_type && has_type_name) {
if ((!has_type && has_type_name) || f->type_ == kUpb_FieldType_Message) {
f->type_ =
UPB_FIELD_TYPE_UNSPECIFIED; // We'll assign this in resolve_subdef()
} else {
@ -863,8 +856,15 @@ static void resolve_subdef(upb_DefBuilder* ctx, const char* prefix,
break;
case UPB_DEFTYPE_MSG:
f->sub.msgdef = def;
f->type_ = kUpb_FieldType_Message; // It appears there is no way of
// this being a group.
f->type_ = kUpb_FieldType_Message;
// TODO: remove once we can deprecate
// kUpb_FieldType_Group.
if (UPB_DESC(FeatureSet_message_encoding)(f->resolved_features) ==
UPB_DESC(FeatureSet_DELIMITED) &&
!upb_MessageDef_IsMapEntry(def) &&
!(f->msgdef && upb_MessageDef_IsMapEntry(f->msgdef))) {
f->type_ = kUpb_FieldType_Group;
}
f->has_presence = !upb_FieldDef_IsRepeated(f);
break;
default:

Loading…
Cancel
Save