Reenable extension support for Weak Descriptor messages.

Extension registration now happens in two phases, where we register all linked in messages first and any dynamic message afterwards.
This guarantees that we register all extensions that will be used by descriptor parsing before we parse any descriptors.

This is currently disabled by default.

PiperOrigin-RevId: 616840653
pull/16156/head
Protobuf Team Bot 1 year ago committed by Copybara-Service
parent e6684ac037
commit db60e52991
  1. 58
      src/google/protobuf/compiler/cpp/extension.cc
  2. 16
      src/google/protobuf/compiler/cpp/extension.h
  3. 144
      src/google/protobuf/compiler/cpp/file.cc
  4. 5
      src/google/protobuf/compiler/cpp/file.h
  5. 6
      src/google/protobuf/compiler/cpp/helpers.h
  6. 17
      src/google/protobuf/compiler/java/java_features.pb.cc
  7. 9
      src/google/protobuf/compiler/plugin.pb.cc
  8. 1
      src/google/protobuf/cpp_features.pb.cc
  9. 1
      src/google/protobuf/descriptor.pb.cc
  10. 2
      src/google/protobuf/editions/golden/compare_cpp_codegen_failure.xml
  11. 20
      src/google/protobuf/extension_set.h
  12. 38
      src/google/protobuf/extension_set_heavy.cc
  13. 8
      src/google/protobuf/generated_message_reflection.cc
  14. 6
      src/google/protobuf/generated_message_reflection.h

@ -14,6 +14,7 @@
#include <string>
#include <vector>
#include "absl/log/absl_check.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_replace.h"
#include "google/protobuf/compiler/cpp/helpers.h"
@ -178,7 +179,16 @@ void ExtensionGenerator::GenerateDefinition(io::Printer* p) {
)cc");
}
void ExtensionGenerator::GenerateRegistration(io::Printer* p) {
bool ExtensionGenerator::WillGenerateRegistration(InitPriority priority) {
// We only use priority 101 for weak descriptors.
return priority != kInitPriority101 ||
(descriptor_->cpp_type() == descriptor_->CPPTYPE_MESSAGE &&
UsingImplicitWeakDescriptor(descriptor_->file(), options_));
}
void ExtensionGenerator::GenerateRegistration(io::Printer* p,
InitPriority priority) {
ABSL_CHECK(WillGenerateRegistration(priority));
auto vars = p->WithVars(variables_);
switch (descriptor_->cpp_type()) {
case FieldDescriptor::CPPTYPE_ENUM:
@ -205,10 +215,15 @@ void ExtensionGenerator::GenerateRegistration(io::Printer* p) {
{"lazy", descriptor_->options().has_lazy()
? descriptor_->options().lazy() ? "kLazy" : "kEager"
: "kUndefined"}});
// Temporarily disable weak descriptor message for extensions.
// It is currently incorrect for custom descriptor options. We end up
// parsing the descriptors before the custom options are registered.
if (false && UsingImplicitWeakDescriptor(descriptor_->file(), options_)) {
const auto register_message = [&] {
p->Emit(R"cc(
::_pbi::ExtensionSet::RegisterMessageExtension(
&$extendee$::default_instance(), $number$, $field_type$,
$repeated$, $packed$, &$message_type$::default_instance(),
$verify$, ::_pbi::LazyAnnotation::$lazy$),
)cc");
};
if (UsingImplicitWeakDescriptor(descriptor_->file(), options_)) {
const auto find_index = [](auto* desc) {
const std::vector<const Descriptor*> msgs =
FlattenMessagesInFile(desc->file());
@ -224,23 +239,30 @@ void ExtensionGenerator::GenerateRegistration(io::Printer* p) {
DescriptorTableName(descriptor_->message_type()->file(),
options_)},
{"extension_index", find_index(descriptor_->message_type())},
{"preregister", priority == kInitPriority101},
{"fallback_for_opt_out",
// For now we have a fallback to use normal registration,
// which should only happen at priority 102.
// Once we turn this on by default we can remove the opt-in
// and the fallback.
[&] {
if (priority != kInitPriority102) return;
register_message();
}},
},
R"cc(
::_pbi::ExtensionSet::RegisterMessageExtension(
::_pbi::GetPrototypeForWeakDescriptor(&$extendee_table$,
$extendee_index$),
$number$, $field_type$, $repeated$, $packed$,
::_pbi::GetPrototypeForWeakDescriptor(&$extension_table$,
$extension_index$),
$verify$, ::_pbi::LazyAnnotation::$lazy$),
//
#if defined(PROTOBUF_INTERNAL_TEMPORARY_WEAK_EXTENSION_OPT_IN)
::_pbi::ExtensionSet::RegisterWeakMessageExtension(
{&$extendee_table$, $extendee_index$}, $number$, $field_type$,
$repeated$, {&$extension_table$, $extension_index$}, $verify$,
::_pbi::LazyAnnotation::$lazy$, $preregister$),
#else
$fallback_for_opt_out$,
#endif
)cc");
} else {
p->Emit(R"cc(
::_pbi::ExtensionSet::RegisterMessageExtension(
&$extendee$::default_instance(), $number$, $field_type$,
$repeated$, $packed$, &$message_type$::default_instance(),
$verify$, ::_pbi::LazyAnnotation::$lazy$),
)cc");
register_message();
}
break;
}

@ -15,6 +15,7 @@
#include <string>
#include "absl/container/flat_hash_map.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/compiler/cpp/options.h"
#include "google/protobuf/port.h"
@ -56,7 +57,20 @@ class PROTOC_EXPORT ExtensionGenerator {
// Source file stuff.
void GenerateDefinition(io::Printer* p);
void GenerateRegistration(io::Printer* p);
// Extension registration can happen at different priority levels depending on
// the features used.
//
// For Weak Descriptor messages, we must use a two phase approach where we
// first register all the extensions that are fully linked in, and then we
// register the rest. To do that, we register the linked in extensions on
// priority 101 and the rest as priority 102.
// For extensions that are missing prototypes we need to create the prototypes
// before we can register them, but for that we need to successfully parse
// its descriptors, which might require other extensions to be registered
// first. All extensions required for descriptor parsing will be fully linked
// in and registered in the first phase.
void GenerateRegistration(io::Printer* p, InitPriority priority);
bool WillGenerateRegistration(InitPriority priority);
bool IsScoped() const;

@ -41,6 +41,7 @@
#include "google/protobuf/compiler/versions.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/dynamic_message.h"
#include "google/protobuf/io/printer.h"
// Must be last.
@ -480,6 +481,7 @@ void FileGenerator::GenerateSourceIncludes(io::Printer* p) {
#include $h_include$
#include <algorithm>
#include <type_traits>
)");
IncludeFile("third_party/protobuf/io/coded_stream.h", p);
@ -835,20 +837,26 @@ void FileGenerator::GenerateSourceForMessage(int idx, io::Printer* p) {
}
void FileGenerator::GenerateStaticInitializer(io::Printer* p) {
if (static_initializers_.empty()) return;
p->Emit({{"expr",
[&] {
for (auto& init : static_initializers_) {
init(p);
}
}}},
R"cc(
PROTOBUF_ATTRIBUTE_INIT_PRIORITY2
static ::std::false_type _static_init_ PROTOBUF_UNUSED =
($expr$, ::std::false_type{});
)cc");
// Reset the vector because we might be generating many files.
static_initializers_.clear();
int priority = 0;
for (auto& inits : static_initializers_) {
++priority;
if (inits.empty()) continue;
p->Emit(
{{"priority", priority},
{"expr",
[&] {
for (auto& init : inits) {
init(p);
}
}}},
R"cc(
PROTOBUF_ATTRIBUTE_INIT_PRIORITY$priority$ static ::std::false_type
_static_init$priority$_ PROTOBUF_UNUSED =
($expr$, ::std::false_type{});
)cc");
// Reset the vector because we might be generating many files.
inits.clear();
}
}
void FileGenerator::GenerateSourceForExtension(int idx, io::Printer* p) {
@ -858,9 +866,13 @@ void FileGenerator::GenerateSourceForExtension(int idx, io::Printer* p) {
NamespaceOpener ns(Namespace(file_, options_), p);
extension_generators_[idx]->GenerateDefinition(p);
static_initializers_.push_back([this, idx](auto* p) {
extension_generators_[idx]->GenerateRegistration(p);
});
for (auto priority : {kInitPriority101, kInitPriority102}) {
if (extension_generators_[idx]->WillGenerateRegistration(priority)) {
static_initializers_[priority].push_back([this, idx, priority](auto* p) {
extension_generators_[idx]->GenerateRegistration(p, priority);
});
}
}
GenerateStaticInitializer(p);
}
@ -989,9 +1001,14 @@ void FileGenerator::GenerateSource(io::Printer* p) {
for (int i = 0; i < extension_generators_.size(); ++i) {
extension_generators_[i]->GenerateDefinition(p);
if (!is_lazily_init) {
static_initializers_.push_back([&, i](auto* p) {
extension_generators_[i]->GenerateRegistration(p);
});
for (auto priority : {kInitPriority101, kInitPriority102}) {
if (extension_generators_[i]->WillGenerateRegistration(priority)) {
static_initializers_[priority].push_back(
[this, i, priority](auto* p) {
extension_generators_[i]->GenerateRegistration(p, priority);
});
}
}
}
}
@ -1020,9 +1037,69 @@ void FileGenerator::GenerateSource(io::Printer* p) {
IncludeFile("third_party/protobuf/port_undef.inc", p);
}
static void GatherAllCustomOptionTypes(
const FileDescriptor* file,
absl::btree_map<absl::string_view, const Descriptor*>& out) {
const DescriptorPool* pool = file->pool();
const Descriptor* fd_proto_descriptor = pool->FindMessageTypeByName(
FileDescriptorProto::descriptor()->full_name());
// Not all pools have descriptor.proto in them. In these cases there for sure
// are no custom options.
if (fd_proto_descriptor == nullptr) {
return;
}
// It's easier to inspect file as a proto, because we can use reflection on
// the proto to iterate over all content.
// However, we can't use the generated proto linked into the proto compiler
// for this, since it doesn't know the extensions that are potentially present
// the protos that are being compiled.
// Use a dynamic one from the correct pool to parse them.
DynamicMessageFactory factory(pool);
std::unique_ptr<Message> fd_proto(
factory.GetPrototype(fd_proto_descriptor)->New());
{
FileDescriptorProto linkedin_fd_proto;
file->CopyTo(&linkedin_fd_proto);
ABSL_CHECK(
fd_proto->ParseFromString(linkedin_fd_proto.SerializeAsString()));
}
// Now find all the messages used, recursively.
std::vector<const Message*> to_process = {fd_proto.get()};
while (!to_process.empty()) {
const Message& msg = *to_process.back();
to_process.pop_back();
std::vector<const FieldDescriptor*> fields;
const Reflection& reflection = *msg.GetReflection();
reflection.ListFields(msg, &fields);
for (auto* field : fields) {
const auto* field_msg = field->message_type();
if (field_msg == nullptr) continue;
if (field->is_extension()) {
const Descriptor* desc = msg.GetDescriptor();
out[desc->full_name()] = desc;
out[field_msg->full_name()] = field_msg;
}
if (field->is_repeated()) {
for (int i = 0; i < reflection.FieldSize(msg, field); i++) {
to_process.push_back(&reflection.GetRepeatedMessage(msg, field, i));
}
} else {
to_process.push_back(&reflection.GetMessage(msg, field));
}
}
}
}
static std::vector<const Descriptor*>
GetMessagesToPinGloballyForWeakDescriptors(const FileDescriptor* file) {
std::vector<const Descriptor*> out;
GetMessagesToPinGloballyForWeakDescriptors(const FileDescriptor* file,
const Options& options) {
// Sorted map to dedup and to make deterministic.
absl::btree_map<absl::string_view, const Descriptor*> res;
// For simplicity we force pin request/response messages for all
// services. The current implementation of services might not do
@ -1032,11 +1109,22 @@ GetMessagesToPinGloballyForWeakDescriptors(const FileDescriptor* file) {
auto* service = file->service(i);
for (int j = 0; j < service->method_count(); ++j) {
auto* method = service->method(j);
out.push_back(method->input_type());
out.push_back(method->output_type());
res[method->input_type()->full_name()] = method->input_type();
res[method->output_type()->full_name()] = method->output_type();
}
}
// For correctness, we must ensure that all messages used as custom options in
// the descriptor are pinned. Otherwise, we can't properly parse the
// descriptor.
GatherAllCustomOptionTypes(file, res);
std::vector<const Descriptor*> out;
for (auto& p : res) {
// We don't need to pin the bootstrap types. It is wasteful.
if (IsBootstrapProto(options, p.second->file())) continue;
out.push_back(p.second);
}
return out;
}
@ -1266,8 +1354,10 @@ void FileGenerator::GenerateReflectionInitializationCode(io::Printer* p) {
// Descriptor initialization will still be performed lazily when it's needed.
if (!IsLazilyInitializedFile(file_->name())) {
if (UsingImplicitWeakDescriptor(file_, options_)) {
for (auto* pinned : GetMessagesToPinGloballyForWeakDescriptors(file_)) {
static_initializers_.push_back([this, pinned](auto* p) {
for (auto* pinned :
GetMessagesToPinGloballyForWeakDescriptors(file_, options_)) {
static_initializers_[kInitPriority102].push_back([this,
pinned](auto* p) {
p->Emit({{"pin", StrongReferenceToType(pinned, options_)}},
R"cc(
$pin$,
@ -1275,7 +1365,7 @@ void FileGenerator::GenerateReflectionInitializationCode(io::Printer* p) {
});
}
}
static_initializers_.push_back([](auto* p) {
static_initializers_[kInitPriority102].push_back([](auto* p) {
p->Emit(R"cc(
::_pbi::AddDescriptors(&$desc_table$),
)cc");

@ -81,7 +81,8 @@ class PROTOC_EXPORT FileGenerator {
// Generates a static initializers with all the existing values from
// `static_initializers_`.
// They run in `PROTOBUF_ATTRIBUTE_INIT_PRIORITY2` priority.
// They run in `PROTOBUF_ATTRIBUTE_INIT_PRIORITY1` and
// `PROTOBUF_ATTRIBUTE_INIT_PRIORITY2` priority respectively.
void GenerateStaticInitializer(io::Printer* p);
// Shared code between the two header generators.
@ -176,7 +177,7 @@ class PROTOC_EXPORT FileGenerator {
absl::flat_hash_set<const FileDescriptor*> weak_deps_;
std::vector<absl::AnyInvocable<void(io::Printer*)>> static_initializers_;
std::vector<absl::AnyInvocable<void(io::Printer*)>> static_initializers_[2];
const FileDescriptor* file_;
Options options_;

@ -1176,6 +1176,12 @@ bool HasOnDeserializeTracker(const Descriptor* descriptor,
// signature.
bool NeedsPostLoopHandler(const Descriptor* descriptor, const Options& options);
// Priority used for static initializers.
enum InitPriority {
kInitPriority101,
kInitPriority102,
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf

@ -6,6 +6,7 @@
#include "google/protobuf/compiler/java/java_features.pb.h"
#include <algorithm>
#include <type_traits>
#include "google/protobuf/io/coded_stream.h"
#include "google/protobuf/generated_message_tctable_impl.h"
#include "google/protobuf/extension_set.h"
@ -366,12 +367,12 @@ namespace protobuf {
} // namespace protobuf
} // namespace google
// @@protoc_insertion_point(global_scope)
PROTOBUF_ATTRIBUTE_INIT_PRIORITY2
static ::std::false_type _static_init_ PROTOBUF_UNUSED =
(::_pbi::AddDescriptors(&descriptor_table_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto),
::_pbi::ExtensionSet::RegisterMessageExtension(
&::google::protobuf::FeatureSet::default_instance(), 1001, 11,
false, false, &::pb::JavaFeatures::default_instance(),
nullptr, ::_pbi::LazyAnnotation::kUndefined),
::std::false_type{});
PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::std::false_type
_static_init2_ PROTOBUF_UNUSED =
(::_pbi::AddDescriptors(&descriptor_table_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto),
::_pbi::ExtensionSet::RegisterMessageExtension(
&::google::protobuf::FeatureSet::default_instance(), 1001, 11,
false, false, &::pb::JavaFeatures::default_instance(),
nullptr, ::_pbi::LazyAnnotation::kUndefined),
::std::false_type{});
#include "google/protobuf/port_undef.inc"

@ -6,6 +6,7 @@
#include "google/protobuf/compiler/plugin.pb.h"
#include <algorithm>
#include <type_traits>
#include "google/protobuf/io/coded_stream.h"
#include "google/protobuf/generated_message_tctable_impl.h"
#include "google/protobuf/extension_set.h"
@ -1611,8 +1612,8 @@ namespace protobuf {
} // namespace protobuf
} // namespace google
// @@protoc_insertion_point(global_scope)
PROTOBUF_ATTRIBUTE_INIT_PRIORITY2
static ::std::false_type _static_init_ PROTOBUF_UNUSED =
(::_pbi::AddDescriptors(&descriptor_table_google_2fprotobuf_2fcompiler_2fplugin_2eproto),
::std::false_type{});
PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::std::false_type
_static_init2_ PROTOBUF_UNUSED =
(::_pbi::AddDescriptors(&descriptor_table_google_2fprotobuf_2fcompiler_2fplugin_2eproto),
::std::false_type{});
#include "google/protobuf/port_undef.inc"

@ -6,6 +6,7 @@
#include "google/protobuf/cpp_features.pb.h"
#include <algorithm>
#include <type_traits>
#include "google/protobuf/io/coded_stream.h"
#include "google/protobuf/generated_message_tctable_impl.h"
#include "google/protobuf/extension_set.h"

@ -6,6 +6,7 @@
#include "google/protobuf/descriptor.pb.h"
#include <algorithm>
#include <type_traits>
#include "google/protobuf/io/coded_stream.h"
#include "google/protobuf/generated_message_tctable_impl.h"
#include "google/protobuf/extension_set.h"

@ -2,7 +2,7 @@
<testsuites tests="1" name="AllTests">
<testsuite name="EditionsCodegenTests">
<testcase name="third_party/protobuf/editions/golden/simple_proto3.pb.cc" status="run" result="completed" classname="DiffTest">
<failure message="Value of: third_party/protobuf/editions/golden/simple_proto3.pb.cc&#x0A;Expected: &#x0A;// Generated by the protocol buffer compiler. DO NOT EDIT!&#x0A;// NO CHECKED-IN PROTOBUF GENCODE&#x0A;// source: third_party/protobuf/editions/golden/simple_proto3.proto&#x0A;// Protobuf C++ Version: 0.20240110.0&#x0A;&#x0A;#include &quot;third_party/protobuf/editions/golden/simple_proto3.pb.h&quot;&#x0A;&#x0A;#include &lt;algorithm&gt;&#x0A;#include &quot;third_party/protobuf/io/coded_stream.h&quot;&#x0A;#include &quot;third_party/protobuf/generated_message_tctable_impl.h&quot;&#x0A;#include &quot;third_party/protobuf/extension_set.h&quot;&#x0A;#include &quot;third_party/protobuf/wire_format_lite.h&quot;&#x0A;#include &quot;third_party/protobuf/io/zero_copy_stream_impl_lite.h&quot;&#x0A;// @@protoc_insertion_point(includes)&#x0A;&#x0A;// Must be included last.&#x0A;, with the difference:&#x0A;@@ @@&#x0A; ::_pbi::TcParser::GetTable&lt;::protobuf_editions_test::golden::SimpleProto3&gt;(), // to_prefetch&#x0A; #endif // PROTOBUF_PREFETCH_PARSE_TABLE&#x0A; }, {{&#x0A;- // optional int32 int32_field = 1;&#x0A;+ // int32 int32_field = 1;&#x0A; {::_pbi::TcParser::FastV32S1,&#x0A; {8, 0, 0, PROTOBUF_FIELD_OFFSET(SimpleProto3, _impl_.int32_field_)}},&#x0A; }}, {{&#x0A; 65535, 65535&#x0A; }}, {{&#x0A;- // optional int32 int32_field = 1;&#x0A;+ // int32 int32_field = 1;&#x0A; {PROTOBUF_FIELD_OFFSET(SimpleProto3, _impl_.int32_field_), _Internal::kHasBitsOffset + 0, 0,&#x0A; (0 | ::_fl::kFcOptional | ::_fl::kInt32)},&#x0A; }},&#x0A;@@ @@&#x0A; (void)cached_has_bits;&#x0A; &#x0A; cached_has_bits = _impl_._has_bits_[0];&#x0A;- // optional int32 int32_field = 1;&#x0A;+ // int32 int32_field = 1;&#x0A; if (cached_has_bits &amp; 0x00000001u) {&#x0A; target = ::proto2::internal::WireFormatLite::&#x0A; WriteInt32ToArrayWithField&lt;1&gt;(&#x0A;@@ @@&#x0A; // Prevent compiler warnings about cached_has_bits being unused&#x0A; (void) cached_has_bits;&#x0A; &#x0A;- // optional int32 int32_field = 1;&#x0A;+ // int32 int32_field = 1;&#x0A; cached_has_bits = _impl_._has_bits_[0];&#x0A; if (cached_has_bits &amp; 0x00000001u) {&#x0A; total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(" type=""></failure>
<failure message="Value of: third_party/protobuf/editions/golden/simple_proto3.pb.cc&#x0A;Expected: &#x0A;// Generated by the protocol buffer compiler. DO NOT EDIT!&#x0A;// NO CHECKED-IN PROTOBUF GENCODE&#x0A;// source: third_party/protobuf/editions/golden/simple_proto3.proto&#x0A;// Protobuf C++ Version: 0.20240110.0&#x0A;&#x0A;#include &quot;third_party/protobuf/editions/golden/simple_proto3.pb.h&quot;&#x0A;&#x0A;#include &lt;algorithm&gt;&#x0A;#include &lt;type_traits&gt;&#x0A;#include &quot;third_party/protobuf/io/coded_stream.h&quot;&#x0A;#include &quot;third_party/protobuf/generated_message_tctable_impl.h&quot;&#x0A;#include &quot;third_party/protobuf/extension_set.h&quot;&#x0A;#include &quot;third_party/protobuf/wire_format_lite.h&quot;&#x0A;#include &quot;third_party/protobuf/io/zero_copy_stream_impl_lite.h&quot;&#x0A;// @@protoc_insertion_point(includes)&#x0A;&#x0A;// Must be included last.&#x0A;, with the difference:&#x0A;@@ @@&#x0A; ::_pbi::TcParser::GetTable&lt;::protobuf_editions_test::golden::SimpleProto3&gt;(), // to_prefetch&#x0A; #endif // PROTOBUF_PREFETCH_PARSE_TABLE&#x0A; }, {{&#x0A;- // optional int32 int32_field = 1;&#x0A;+ // int32 int32_field = 1;&#x0A; {::_pbi::TcParser::FastV32S1,&#x0A; {8, 0, 0, PROTOBUF_FIELD_OFFSET(SimpleProto3, _impl_.int32_field_)}},&#x0A; }}, {{&#x0A; 65535, 65535&#x0A; }}, {{&#x0A;- // optional int32 int32_field = 1;&#x0A;+ // int32 int32_field = 1;&#x0A; {PROTOBUF_FIELD_OFFSET(SimpleProto3, _impl_.int32_field_), _Internal::kHasBitsOffset + 0, 0,&#x0A; (0 | ::_fl::kFcOptional | ::_fl::kInt32)},&#x0A; }},&#x0A;@@ @@&#x0A; (void)cached_has_bits;&#x0A; &#x0A; cached_has_bits = _impl_._has_bits_[0];&#x0A;- // optional int32 int32_field = 1;&#x0A;+ // int32 int32_field = 1;&#x0A; if (cached_has_bits &amp; 0x00000001u) {&#x0A; target = ::proto2::internal::WireFormatLite::&#x0A; WriteInt32ToArrayWithField&lt;1&gt;(&#x0A;@@ @@&#x0A; // Prevent compiler warnings about cached_has_bits being unused&#x0A; (void) cached_has_bits;&#x0A; &#x0A;- // optional int32 int32_field = 1;&#x0A;+ // int32 int32_field = 1;&#x0A; cached_has_bits = _impl_._has_bits_[0];&#x0A; if (cached_has_bits &amp; 0x00000001u) {&#x0A; total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(" type=""></failure>
</testcase>
<testcase name="third_party/protobuf/editions/golden/simple_proto3.pb.h" status="run" result="completed" classname="DiffTest">
<failure message="Value of: third_party/protobuf/editions/golden/simple_proto3.pb.h&#x0A;Expected: &#x0A;// Generated by the protocol buffer compiler. DO NOT EDIT!&#x0A;// NO CHECKED-IN PROTOBUF GENCODE&#x0A;// source: third_party/protobuf/editions/golden/simple_proto3.proto&#x0A;// Protobuf C++ Version: 0.20240110.0&#x0A;&#x0A;#ifndef GOOGLE_PROTOBUF_INCLUDED_third_5fparty_2fprotobuf_2feditions_2fgolden_2fsimple_5fproto3_2eproto_2epb_2eh&#x0A;#define GOOGLE_PROTOBUF_INCLUDED_third_5fparty_2fprotobuf_2feditions_2fgolden_2fsimple_5fproto3_2eproto_2epb_2eh&#x0A;&#x0A;#include &lt;limits&gt;&#x0A;#include &lt;string&gt;&#x0A;#include &lt;type_traits&gt;&#x0A;#include &lt;utility&gt;&#x0A;&#x0A;#include &quot;third_party/protobuf/runtime_version.h&quot;&#x0A;#if PROTOBUF_VERSION != 20240110&#x0A;#error &quot;Protobuf C++ gencode is built with an incompatible version of&quot;&#x0A;#error &quot;Protobuf C++ headers/runtime. See&quot;&#x0A;#error &quot;https://protobuf.dev/support/cross-version-runtime-guarantee/#cpp&quot;&#x0A;#endif&#x0A;#include &quot;third_party/protobuf/io/coded_stream.h&quot;&#x0A;#include &quot;third_party/protobuf/arena.h&quot;&#x0A;#include &quot;third_party/protobuf/arenastring.h&quot;&#x0A;#include &quot;third_party/protobuf/generated_message_tctable_decl.h&quot;&#x0A;#include &quot;third_party/protobuf/generated_message_util.h&quot;&#x0A;#include &quot;third_party/protobuf/metadata_lite.h&quot;&#x0A;#include &quot;third_party/protobuf/message_lite.h&quot;&#x0A;// @@protoc_insertion_point(includes)&#x0A;&#x0A;// Must be included last.&#x0A;&#x0A;#endif // GOOGLE_PROTOBUF_INCLUDED_third_5fparty_2fprotobuf_2feditions_2fgolden_2fsimple_5fproto3_2eproto_2epb_2eh&#x0A;, with the difference:&#x0A;@@ @@&#x0A; enum : int {&#x0A; kInt32FieldFieldNumber = 1,&#x0A; };&#x0A;- // optional int32 int32_field = 1;&#x0A;+ // int32 int32_field = 1;&#x0A; bool has_int32_field() const;&#x0A; void clear_int32_field() ;&#x0A; ::int32_t int32_field() const;&#x0A;@@ @@&#x0A; &#x0A; // SimpleProto3&#x0A; &#x0A;-// optional int32 int32_field = 1;&#x0A;+// int32 int32_field = 1;&#x0A; inline bool SimpleProto3::has_int32_field() const {&#x0A; bool value = (_impl_._has_bits_[0] &amp; 0x00000001u) != 0;&#x0A; return value;" type=""></failure>

@ -60,6 +60,7 @@ class Reflection; // message.h
class UnknownFieldSet; // unknown_field_set.h
class FeatureSet;
namespace internal {
struct DescriptorTable;
class FieldSkipper; // wire_format_lite.h
class ReflectionVisit; // message_reflection_util.h
class WireFormat;
@ -157,6 +158,14 @@ struct ExtensionInfo {
};
// Reference to a prototype via its DescriptorTable.
// This way we can generate them on the fly if they are missing when Weak
// Descriptor messages are enabled.
struct WeakPrototypeRef {
const internal::DescriptorTable* table;
int index;
};
// An ExtensionFinder is an object which looks up extension definitions. It
// must implement this method:
//
@ -225,6 +234,17 @@ class PROTOBUF_EXPORT ExtensionSet {
LazyEagerVerifyFnType verify_func,
LazyAnnotation is_lazy);
// As RegisterMessageExtension, but for the weak descriptor message mode.
// It will perform the registration in two phases to guarantee we can parse
// descriptors properly.
static void RegisterWeakMessageExtension(internal::WeakPrototypeRef extendee,
int number, FieldType type,
bool is_repeated,
internal::WeakPrototypeRef prototype,
LazyEagerVerifyFnType verify_func,
LazyAnnotation is_lazy,
bool is_preregistration);
// =================================================================
// Add all fields which are currently present to the given vector. This

@ -23,6 +23,7 @@
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/extension_set.h"
#include "google/protobuf/extension_set_inl.h"
#include "google/protobuf/generated_message_reflection.h"
#include "google/protobuf/io/coded_stream.h"
#include "google/protobuf/message.h"
#include "google/protobuf/message_lite.h"
@ -421,6 +422,43 @@ uint8_t* ExtensionSet::SerializeMessageSetWithCachedSizesToArray(
&stream);
}
#if defined(PROTOBUF_DESCRIPTOR_WEAK_MESSAGES_ALLOWED)
// First, register all the extensions that have both messages linked in.
// This will include all messages used as extensions in .proto options.
// In the second phase, we generate the missing prototypes, but that requires
// parsing descriptors, which in turn require the extensions from the first
// phase.
void ExtensionSet::RegisterWeakMessageExtension(
internal::WeakPrototypeRef extendee, int number, FieldType type,
bool is_repeated, internal::WeakPrototypeRef prototype,
LazyEagerVerifyFnType verify_func, LazyAnnotation is_lazy,
bool is_preregistration) {
auto* extendee_msg =
GetPrototypeForWeakDescriptor(extendee.table, extendee.index, false);
auto* prototype_msg =
GetPrototypeForWeakDescriptor(prototype.table, prototype.index, false);
const bool have_both = extendee_msg != nullptr && prototype_msg != nullptr;
if (is_preregistration != have_both) {
// This is done on the other phase.
return;
}
if (extendee_msg == nullptr) {
extendee_msg =
GetPrototypeForWeakDescriptor(extendee.table, extendee.index, true);
}
if (prototype_msg == nullptr) {
prototype_msg =
GetPrototypeForWeakDescriptor(prototype.table, prototype.index, true);
}
ExtensionSet::RegisterMessageExtension(
extendee_msg, number, type, is_repeated,
/*is_packed=*/false, prototype_msg, verify_func, is_lazy);
}
#endif // PROTOBUF_DESCRIPTOR_WEAK_MESSAGES_ALLOWED
} // namespace internal
} // namespace protobuf
} // namespace google

@ -3779,8 +3779,9 @@ bool SplitFieldHasExtraIndirection(const FieldDescriptor* field) {
return field->is_repeated();
}
#if defined(PROTOBUF_DESCRIPTOR_WEAK_MESSAGES_ALLOWED)
const Message* GetPrototypeForWeakDescriptor(const DescriptorTable* table,
int index) {
int index, bool force_build) {
// First, make sure we inject the surviving default instances.
InitProtobufDefaults();
@ -3789,6 +3790,10 @@ const Message* GetPrototypeForWeakDescriptor(const DescriptorTable* table,
return msg;
}
if (!force_build) {
return nullptr;
}
// Fallback to dynamic messages.
// Register the dep and generate the prototype via the generated pool.
AssignDescriptors(table);
@ -3806,6 +3811,7 @@ const Message* GetPrototypeForWeakDescriptor(const DescriptorTable* table,
return MessageFactory::generated_factory()->GetPrototype(descriptor);
}
#endif // PROTOBUF_DESCRIPTOR_WEAK_MESSAGES_ALLOWED
} // namespace internal
} // namespace protobuf

@ -339,9 +339,11 @@ struct PROTOBUF_EXPORT AddDescriptorsRunner {
};
// Retrieves the existing prototype out of a descriptor table.
// If it doesn't exist, asks the generated message factory for one.
// If it doesn't exist:
// - If force_build is true, asks the generated message factory for one.
// - Otherwise, return null
const Message* GetPrototypeForWeakDescriptor(const DescriptorTable* table,
int index);
int index, bool force_build);
struct DenseEnumCacheInfo {
std::atomic<const std::string**> cache;

Loading…
Cancel
Save