Skip to content

Commit e25c6e3

Browse files
Update google.protobuf.Type handling in protobuf/json to support editions
PiperOrigin-RevId: 782939203
1 parent 930036a commit e25c6e3

File tree

8 files changed

+253
-48
lines changed

8 files changed

+253
-48
lines changed

src/google/protobuf/descriptor.cc

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5190,17 +5190,7 @@ bool DescriptorPool::ShouldEnforceExtensionDeclaration(
51905190

51915191
const FeatureSetDefaults& DescriptorPool::GetFeatureSetDefaults() const {
51925192
if (feature_set_defaults_spec_ != nullptr) return *feature_set_defaults_spec_;
5193-
static const FeatureSetDefaults* cpp_default_spec =
5194-
internal::OnShutdownDelete([] {
5195-
auto* defaults = new FeatureSetDefaults();
5196-
internal::ParseNoReflection(
5197-
absl::string_view{
5198-
PROTOBUF_INTERNAL_CPP_EDITION_DEFAULTS,
5199-
sizeof(PROTOBUF_INTERNAL_CPP_EDITION_DEFAULTS) - 1},
5200-
*defaults);
5201-
return defaults;
5202-
}());
5203-
return *cpp_default_spec;
5193+
return internal::cpp::GetFeatureSetDefaults();
52045194
}
52055195

52065196
bool DescriptorPool::ResolvesFeaturesForImpl(int extension_number) const {
@@ -10671,6 +10661,18 @@ bool ParseNoReflection(absl::string_view from, google::protobuf::MessageLite& to
1067110661
return to.IsInitializedWithErrors();
1067210662
}
1067310663

10664+
const FeatureSet& GetUnresolvedFeatureSet(const FileDescriptor& descriptor) {
10665+
return InternalFeatureHelper::GetUnresolvedFeatures(descriptor);
10666+
}
10667+
10668+
const FeatureSet& GetUnresolvedFeatureSet(const Descriptor& descriptor) {
10669+
return InternalFeatureHelper::GetUnresolvedFeatures(descriptor);
10670+
}
10671+
10672+
const FeatureSet& GetUnresolvedFeatureSet(const EnumDescriptor& descriptor) {
10673+
return InternalFeatureHelper::GetUnresolvedFeatures(descriptor);
10674+
}
10675+
1067410676
namespace cpp {
1067510677
bool HasPreservingUnknownEnumSemantics(const FieldDescriptor* field) {
1067610678
if (field->legacy_enum_field_treated_as_closed()) {
@@ -10779,9 +10781,29 @@ bool IsStringFieldWithPrivatizedAccessors(const FieldDescriptor& field) {
1077910781
Edition FileDescriptor::edition() const { return edition_; }
1078010782

1078110783
namespace internal {
10784+
1078210785
absl::string_view ShortEditionName(Edition edition) {
1078310786
return absl::StripPrefix(Edition_Name(edition), "EDITION_");
1078410787
}
10788+
10789+
namespace cpp {
10790+
10791+
const FeatureSetDefaults& GetFeatureSetDefaults() {
10792+
static const FeatureSetDefaults* cpp_default_spec =
10793+
internal::OnShutdownDelete([] {
10794+
auto* defaults = new FeatureSetDefaults();
10795+
internal::ParseNoReflection(
10796+
absl::string_view{
10797+
PROTOBUF_INTERNAL_CPP_EDITION_DEFAULTS,
10798+
sizeof(PROTOBUF_INTERNAL_CPP_EDITION_DEFAULTS) - 1},
10799+
*defaults);
10800+
return defaults;
10801+
}());
10802+
return *cpp_default_spec;
10803+
}
10804+
10805+
} // namespace cpp
10806+
1078510807
} // namespace internal
1078610808

1078710809
} // namespace protobuf

src/google/protobuf/descriptor.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3144,11 +3144,24 @@ struct FieldRangeImpl {
31443144
// parsing because that uses reflection to verify consistency.
31453145
bool ParseNoReflection(absl::string_view from, google::protobuf::MessageLite& to);
31463146

3147+
PROTOBUF_EXPORT const FeatureSet& GetUnresolvedFeatureSet(
3148+
const FileDescriptor& descriptor);
3149+
PROTOBUF_EXPORT const FeatureSet& GetUnresolvedFeatureSet(
3150+
const Descriptor& descriptor);
3151+
PROTOBUF_EXPORT const FeatureSet& GetUnresolvedFeatureSet(
3152+
const EnumDescriptor& descriptor);
3153+
3154+
// Gets the default feature set for a given edition.
3155+
absl::StatusOr<FeatureSet> PROTOBUF_EXPORT GetEditionFeatureSetDefaults(
3156+
Edition edition, const FeatureSetDefaults& defaults);
3157+
31473158
// The context for these functions under `cpp` is "for the C++ implementation".
31483159
// In particular, questions like "does this field have a has bit?" have a
31493160
// different answer depending on the language.
31503161
namespace cpp {
31513162

3163+
PROTOBUF_EXPORT const FeatureSetDefaults& GetFeatureSetDefaults();
3164+
31523165
// The maximum allowed nesting for message declarations.
31533166
// Going over this limit will make the proto definition invalid.
31543167
constexpr int MaxMessageDeclarationNestingDepth() { return 32; }

src/google/protobuf/feature_resolver.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,9 @@ class PROTOBUF_EXPORT FeatureResolver {
7979

8080
FeatureSet defaults_;
8181
};
82-
83-
namespace internal {
84-
// Gets the default feature set for a given edition.
85-
absl::StatusOr<FeatureSet> PROTOBUF_EXPORT GetEditionFeatureSetDefaults(
86-
Edition edition, const FeatureSetDefaults& defaults);
87-
} // namespace internal
8882
} // namespace protobuf
8983
} // namespace google
9084

9185
#include "google/protobuf/port_undef.inc"
9286

9387
#endif // GOOGLE_PROTOBUF_FEATURE_RESOLVER_H__
94-

src/google/protobuf/internal_feature_helper.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ class PROTOBUF_EXPORT InternalFeatureHelper {
2626
return desc.features();
2727
}
2828

29+
template <typename DescriptorT>
30+
static const FeatureSet& GetUnresolvedFeatures(const DescriptorT& desc) {
31+
return desc.proto_features_ != nullptr ? *desc.proto_features_
32+
: FeatureSet::default_instance();
33+
}
34+
2935
private:
3036
friend class ::google::protobuf::compiler::CodeGenerator;
3137
friend class ::google::protobuf::compiler::CommandLineInterface;

src/google/protobuf/json/internal/descriptor_traits.h

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919
#include <utility>
2020

2121
#include "google/protobuf/type.pb.h"
22+
#include "google/protobuf/descriptor.pb.h"
2223
#include "absl/algorithm/container.h"
2324
#include "absl/log/absl_log.h"
2425
#include "absl/status/status.h"
2526
#include "absl/status/statusor.h"
2627
#include "absl/strings/match.h"
28+
#include "absl/strings/str_cat.h"
2729
#include "absl/strings/str_format.h"
2830
#include "absl/strings/string_view.h"
2931
#include "google/protobuf/descriptor.h"
@@ -411,14 +413,19 @@ struct Proto3Type {
411413

412414
static const Desc& ContainingType(Field f) { return f->parent(); }
413415
static bool IsMap(Field f) {
414-
if (f->proto().kind() != google::protobuf::Field::TYPE_MESSAGE) {
416+
if (!IsRepeated(f) ||
417+
f->proto().kind() != google::protobuf::Field::TYPE_MESSAGE) {
415418
return false;
416419
}
417420

418421
bool value = false;
419422
(void)WithFieldType(f, [&value](const Desc& desc) {
420-
value = absl::c_any_of(desc.proto().options(), [&](auto& option) {
421-
return option.name() == "map_entry";
423+
value = absl::c_any_of(desc.proto().options(), [&](const auto& option) {
424+
// Per the docs this should only be "map_entry", but some code failed to
425+
// heed this and included the full name. Support both.
426+
return option.name() == "map_entry" ||
427+
option.name() == "google.protobuf.MessageOptions.map_entry" ||
428+
option.name() == "google.protobuf.MessageOptions.map_entry";
422429
});
423430
return absl::OkStatus();
424431
});
@@ -431,6 +438,9 @@ struct Proto3Type {
431438
}
432439

433440
static bool IsExplicitPresence(Field f) {
441+
if (IsRepeated(f)) {
442+
return false;
443+
}
434444
// Implicit presence requires this weird check: in proto3 the following
435445
// cases support presence:
436446
// 1) Anything contained in a oneof (including things explicitly declared
@@ -439,8 +449,13 @@ struct Proto3Type {
439449
// TYPE_MESSAGE here).
440450
if (f->parent().proto().syntax() == google::protobuf::SYNTAX_PROTO3) {
441451
return f->proto().oneof_index() != 0 ||
442-
(f->proto().kind() == google::protobuf::Field::TYPE_MESSAGE &&
443-
!IsRepeated(f));
452+
f->proto().kind() == google::protobuf::Field::TYPE_MESSAGE;
453+
}
454+
455+
if (f->parent().proto().syntax() == google::protobuf::SYNTAX_EDITIONS) {
456+
return f->proto().kind() == google::protobuf::Field::TYPE_MESSAGE ||
457+
f->proto().oneof_index() != 0 ||
458+
f->features().field_presence() != google::protobuf::FeatureSet::IMPLICIT;
444459
}
445460

446461
return f->proto().cardinality() ==

src/google/protobuf/json/internal/untyped_message.cc

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,17 @@
1818
#include <vector>
1919

2020
#include "google/protobuf/type.pb.h"
21+
#include "absl/algorithm/container.h"
2122
#include "absl/container/flat_hash_map.h"
2223
#include "absl/log/absl_check.h"
2324
#include "absl/log/absl_log.h"
2425
#include "absl/status/status.h"
26+
#include "absl/status/statusor.h"
2527
#include "absl/strings/str_cat.h"
2628
#include "absl/strings/str_format.h"
2729
#include "absl/strings/string_view.h"
2830
#include "absl/types/span.h"
31+
#include "google/protobuf/descriptor.h"
2932
#include "google/protobuf/io/coded_stream.h"
3033
#include "google/protobuf/port.h"
3134
#include "google/protobuf/util/type_resolver.h"
@@ -75,6 +78,18 @@ absl::Span<const ResolverPool::Field> ResolverPool::Message::FieldsByIndex()
7578
fields_[i].pool_ = pool_;
7679
fields_[i].raw_ = &raw_.fields(i);
7780
fields_[i].parent_ = this;
81+
if (features_ != nullptr) {
82+
fields_[i].features_ = std::make_unique<FeatureSet>(*features_);
83+
auto status_or_features = GetFeatureSet(
84+
fields_[i].raw_->options(), "google.protobuf.FieldOptions.features",
85+
"google.protobuf.FieldOptions.features");
86+
if (status_or_features.ok()) {
87+
fields_[i].features_->MergeFrom(*status_or_features);
88+
} else {
89+
// We cannot do much here.
90+
status_or_features.IgnoreError();
91+
}
92+
}
7893
}
7994
}
8095

@@ -137,6 +152,17 @@ absl::StatusOr<const ResolverPool::Message*> ResolverPool::FindMessage(
137152
auto msg = absl::WrapUnique(new Message(this));
138153
std::string url_buf(url);
139154
RETURN_IF_ERROR(resolver_->ResolveMessageType(url_buf, &msg->raw_));
155+
if (msg->raw_.syntax() == google::protobuf::SYNTAX_EDITIONS) {
156+
ASSIGN_OR_RETURN(auto edition, ParseEdition(msg->raw_.edition()));
157+
ASSIGN_OR_RETURN(const auto* default_feature_set,
158+
GetDefaultFeatureSet(edition));
159+
msg->features_ = std::make_unique<google::protobuf::FeatureSet>(*default_feature_set);
160+
ASSIGN_OR_RETURN(
161+
auto features,
162+
GetFeatureSet(msg->raw_.options(), "google.protobuf.MessageOptions.features",
163+
"google.protobuf.MessageOptions.features"));
164+
msg->features_->MergeFrom(features);
165+
}
140166

141167
return messages_.try_emplace(std::move(url_buf), std::move(msg))
142168
.first->second.get();
@@ -152,11 +178,79 @@ absl::StatusOr<const ResolverPool::Enum*> ResolverPool::FindEnum(
152178
auto enoom = absl::WrapUnique(new Enum(this));
153179
std::string url_buf(url);
154180
RETURN_IF_ERROR(resolver_->ResolveEnumType(url_buf, &enoom->raw_));
181+
if (enoom->raw_.syntax() == google::protobuf::SYNTAX_EDITIONS) {
182+
ASSIGN_OR_RETURN(auto edition, ParseEdition(enoom->raw_.edition()));
183+
ASSIGN_OR_RETURN(const auto* default_feature_set,
184+
GetDefaultFeatureSet(edition));
185+
enoom->features_ = std::make_unique<FeatureSet>(*default_feature_set);
186+
ASSIGN_OR_RETURN(
187+
auto features,
188+
GetFeatureSet(enoom->raw_.options(), "google.protobuf.EnumOptions.features",
189+
"google.protobuf.EnumOptions.features"));
190+
enoom->features_->MergeFrom(features);
191+
}
155192

156193
return enums_.try_emplace(std::move(url_buf), std::move(enoom))
157194
.first->second.get();
158195
}
159196

197+
absl::StatusOr<Edition> ResolverPool::ParseEdition(
198+
absl::string_view edition_suffix) {
199+
Edition edition;
200+
if (!Edition_Parse(absl::StrCat("EDITION_", edition_suffix), &edition)) {
201+
return absl::InvalidArgumentError(
202+
absl::StrCat("unknown edition: %s", edition_suffix));
203+
}
204+
return edition;
205+
}
206+
207+
absl::StatusOr<const FeatureSet*> ResolverPool::GetDefaultFeatureSet(
208+
Edition edition) {
209+
auto it = default_feature_sets_.find(edition);
210+
if (it != default_feature_sets_.end()) {
211+
return it->second.get();
212+
}
213+
ASSIGN_OR_RETURN(
214+
auto feature_set,
215+
internal::GetEditionFeatureSetDefaults(
216+
edition, google::protobuf::internal::cpp::GetFeatureSetDefaults()));
217+
auto feature_set_ptr =
218+
absl::WrapUnique(new FeatureSet(std::move(feature_set)));
219+
return default_feature_sets_.try_emplace(edition, std::move(feature_set_ptr))
220+
.first->second.get();
221+
}
222+
223+
absl::StatusOr<FeatureSet> ResolverPool::GetFeatureSet(
224+
const RepeatedPtrField<google::protobuf::Option>& options,
225+
absl::string_view option_name, absl::string_view oss_option_name) {
226+
auto option = absl::c_find_if(
227+
options, [option_name, oss_option_name](const auto& option) {
228+
return option.name() == "features" || option.name() == option_name ||
229+
option.name() == oss_option_name;
230+
});
231+
if (option == options.end()) {
232+
return FeatureSet();
233+
}
234+
absl::string_view type_url = option->value().type_url();
235+
auto type_url_slash = type_url.find('/');
236+
if (type_url_slash == absl::string_view::npos || type_url_slash == 0) {
237+
return absl::InvalidArgumentError(absl::StrCat(
238+
"type_url must contain at least one / and a nonempty host; got: ",
239+
type_url));
240+
}
241+
absl::string_view type_name = type_url.substr(type_url_slash + 1);
242+
if (type_name != "google.protobuf.FeatureSet") {
243+
return absl::InvalidArgumentError(absl::StrCat(
244+
"expected type name for option value to be google.protobuf.FeatureSet; got: ",
245+
type_name));
246+
}
247+
FeatureSet feature_set;
248+
if (!feature_set.MergeFromString(option->value().value())) {
249+
return absl::UnknownError("failed to merge feature set");
250+
}
251+
return feature_set;
252+
}
253+
160254
PROTOBUF_NOINLINE static absl::Status MakeEndGroupWithoutGroupError(
161255
int field_number) {
162256
return absl::InvalidArgumentError(absl::StrFormat(

src/google/protobuf/json/internal/untyped_message.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#ifndef GOOGLE_PROTOBUF_JSON_INTERNAL_UNTYPED_MESSAGE_H__
22
#define GOOGLE_PROTOBUF_JSON_INTERNAL_UNTYPED_MESSAGE_H__
33

4+
#include "google/protobuf/descriptor.pb.h"
45
#include "absl/log/absl_check.h"
6+
#include "absl/status/statusor.h"
57
// Protocol Buffers - Google's data interchange format
68
// Copyright 2008 Google Inc. All rights reserved.
79
//
@@ -71,6 +73,9 @@ class ResolverPool {
7173

7274
const Message& parent() const { return *parent_; }
7375
const google::protobuf::Field& proto() const { return *raw_; }
76+
const FeatureSet& features() const {
77+
return features_ != nullptr ? *features_ : FeatureSet::default_instance();
78+
}
7479

7580
private:
7681
friend class ResolverPool;
@@ -81,6 +86,7 @@ class ResolverPool {
8186
const google::protobuf::Field* raw_ = nullptr;
8287
const Message* parent_ = nullptr;
8388
mutable const void* type_ = nullptr;
89+
std::unique_ptr<google::protobuf::FeatureSet> features_;
8490
};
8591

8692
class Message {
@@ -106,6 +112,7 @@ class ResolverPool {
106112
mutable absl::flat_hash_map<absl::string_view, const Field*>
107113
fields_by_name_;
108114
mutable absl::flat_hash_map<int32_t, const Field*> fields_by_number_;
115+
std::unique_ptr<google::protobuf::FeatureSet> features_;
109116
};
110117

111118
class Enum {
@@ -125,6 +132,7 @@ class ResolverPool {
125132
google::protobuf::Enum raw_;
126133
mutable absl::flat_hash_map<absl::string_view, google::protobuf::EnumValue*>
127134
values_;
135+
std::unique_ptr<google::protobuf::FeatureSet> features_;
128136
};
129137

130138
explicit ResolverPool(google::protobuf::util::TypeResolver* resolver)
@@ -137,8 +145,18 @@ class ResolverPool {
137145
absl::StatusOr<const Enum*> FindEnum(absl::string_view url);
138146

139147
private:
148+
static absl::StatusOr<Edition> ParseEdition(absl::string_view edition_suffix);
149+
150+
absl::StatusOr<const FeatureSet*> GetDefaultFeatureSet(Edition edition);
151+
152+
static absl::StatusOr<FeatureSet> GetFeatureSet(
153+
const RepeatedPtrField<google::protobuf::Option>& options,
154+
absl::string_view option_name, absl::string_view oss_option_name);
155+
140156
absl::flat_hash_map<std::string, std::unique_ptr<Message>> messages_;
141157
absl::flat_hash_map<std::string, std::unique_ptr<Enum>> enums_;
158+
absl::flat_hash_map<Edition, std::unique_ptr<FeatureSet>>
159+
default_feature_sets_;
142160
google::protobuf::util::TypeResolver* resolver_;
143161
};
144162

0 commit comments

Comments
 (0)