Do eager parsing when building descriptors.

Lazy parsing can use reflection to verify consistency, and reflection while building descriptors causes deadlock.

PiperOrigin-RevId: 555238461
pull/13492/head
Protobuf Team Bot 1 year ago committed by Copybara-Service
parent d99134f6f8
commit 783f555ad1
  1. 19
      src/google/protobuf/descriptor.cc
  2. 10
      src/google/protobuf/descriptor.h
  3. 4
      src/google/protobuf/descriptor_database.cc

@ -81,6 +81,7 @@
#include "google/protobuf/generated_message_util.h" #include "google/protobuf/generated_message_util.h"
#include "google/protobuf/io/strtod.h" #include "google/protobuf/io/strtod.h"
#include "google/protobuf/io/tokenizer.h" #include "google/protobuf/io/tokenizer.h"
#include "google/protobuf/parse_context.h"
#include "google/protobuf/port.h" #include "google/protobuf/port.h"
#include "google/protobuf/repeated_ptr_field.h" #include "google/protobuf/repeated_ptr_field.h"
#include "google/protobuf/text_format.h" #include "google/protobuf/text_format.h"
@ -5249,11 +5250,9 @@ typename DescriptorT::OptionsType* DescriptorBuilder::AllocateOptionsImpl(
return nullptr; return nullptr;
} }
// Avoid using MergeFrom()/CopyFrom() in this class to make it -fno-rtti const bool parse_success =
// friendly. Without RTTI, MergeFrom() and CopyFrom() will fallback to the internal::ParseNoReflection(orig_options.SerializeAsString(), *options);
// reflection based method, which requires the Descriptor. However, we are in ABSL_DCHECK(parse_success);
// the middle of building the descriptors, thus the deadlock.
options->ParseFromString(orig_options.SerializeAsString());
// Don't add to options_to_interpret_ unless there were uninterpreted // Don't add to options_to_interpret_ unless there were uninterpreted
// options. This not only avoids unnecessary work, but prevents a // options. This not only avoids unnecessary work, but prevents a
@ -9424,6 +9423,16 @@ void LazyDescriptor::Once(const ServiceDescriptor* service) {
} }
} }
bool ParseNoReflection(absl::string_view from, google::protobuf::MessageLite& to) {
to.Clear();
const char* ptr;
internal::ParseContext ctx(io::CodedInputStream::GetDefaultRecursionLimit(),
false, &ptr, from);
ptr = to._InternalParse(ptr, &ctx);
if (ptr == nullptr || !ctx.EndedAtLimit()) return false;
return to.IsInitializedWithErrors();
}
namespace cpp { namespace cpp {
bool HasPreservingUnknownEnumSemantics(const FieldDescriptor* field) { bool HasPreservingUnknownEnumSemantics(const FieldDescriptor* field) {
if (field->legacy_enum_field_treated_as_closed()) { if (field->legacy_enum_field_treated_as_closed()) {

@ -123,6 +123,9 @@ class UninterpretedOption;
class FeatureSet; class FeatureSet;
class SourceCodeInfo; class SourceCodeInfo;
// Defined in message_lite.h
class MessageLite;
// Defined in message.h // Defined in message.h
class Message; class Message;
class Reflection; class Reflection;
@ -2830,6 +2833,13 @@ struct FieldRangeImpl {
const T* descriptor; const T* descriptor;
}; };
// While building descriptors, we need to avoid using MergeFrom()/CopyFrom() to
// be -fno-rtti friendly. Without RTTI, MergeFrom() and CopyFrom() will fallback
// to the reflection based method, which requires the Descriptor. However, while
// building the descriptors, this causes deadlock. We also must disable lazy
// parsing because that uses reflection to verify consistency.
bool ParseNoReflection(absl::string_view from, google::protobuf::MessageLite& to);
// The context for these functions under `cpp` is "for the C++ implementation". // The context for these functions under `cpp` is "for the C++ implementation".
// In particular, questions like "does this field have a has bit?" have a // In particular, questions like "does this field have a has bit?" have a
// different answer depending on the language. // different answer depending on the language.

@ -888,7 +888,9 @@ bool EncodedDescriptorDatabase::FindAllFileNames(
bool EncodedDescriptorDatabase::MaybeParse( bool EncodedDescriptorDatabase::MaybeParse(
std::pair<const void*, int> encoded_file, FileDescriptorProto* output) { std::pair<const void*, int> encoded_file, FileDescriptorProto* output) {
if (encoded_file.first == nullptr) return false; if (encoded_file.first == nullptr) return false;
return output->ParseFromArray(encoded_file.first, encoded_file.second); absl::string_view source(static_cast<const char*>(encoded_file.first),
encoded_file.second);
return internal::ParseNoReflection(source, *output);
} }
EncodedDescriptorDatabase::EncodedDescriptorDatabase() EncodedDescriptorDatabase::EncodedDescriptorDatabase()

Loading…
Cancel
Save