| // Protocol Buffers - Google's data interchange format |
| // Copyright 2008 Google Inc. All rights reserved. |
| // |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file or at |
| // https://developers.google.com/open-source/licenses/bsd |
| |
| #ifndef GOOGLE_PROTOBUF_DESCRIPTOR_BUILDER_H__ |
| #define GOOGLE_PROTOBUF_DESCRIPTOR_BUILDER_H__ |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "absl/status/status.h" |
| #include "absl/strings/string_view.h" |
| |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/container/flat_hash_set.h" |
| #include "absl/functional/function_ref.h" |
| #include "absl/types/optional.h" |
| #include "google/protobuf/descriptor.h" |
| #include "google/protobuf/descriptor.pb.h" |
| #include "google/protobuf/dynamic_message.h" |
| #include "google/protobuf/feature_resolver.h" |
| #include "google/protobuf/internal_feature_helper.h" |
| #include "google/protobuf/message.h" |
| #include "google/protobuf/symbol.h" |
| |
| // Must be included last. |
| #include "google/protobuf/port_def.inc" |
| |
| namespace google { |
| namespace protobuf { |
| namespace internal { |
| |
| // A path through a FileDescriptorProto to a specific location of source code, |
| // e.g. a field name. See SourceCodeInfo.Location.path in descriptor.proto for |
| // full structure of this vector. |
| using SourceCodePath = std::vector<int>; |
| |
| // Represents an options message to interpret. Extension names in the option |
| // name are resolved relative to name_scope. element_name and orig_opt are |
| // used only for error reporting (since the parser records locations against |
| // pointers in the original options, not the mutable copy). The Message must be |
| // one of the Options messages in descriptor.proto. |
| struct OptionsToInterpret { |
| OptionsToInterpret(absl::string_view ns, absl::string_view el, |
| SourceCodePath path, const Message* orig_opt, Message* opt) |
| : name_scope(ns), |
| element_name(el), |
| element_path(path.begin(), path.end()), |
| original_options(orig_opt), |
| options(opt) {} |
| std::string name_scope; |
| std::string element_name; |
| SourceCodePath element_path; |
| const Message* original_options; |
| Message* options; |
| }; |
| |
| // DescriptorBuilder is an internal helper class used by DescriptorPool to |
| // construct a FileDescriptor from a FileDescriptorProto representation. |
| // |
| // The build process is multi-phase and includes: |
| // 1. Building: Constructing descriptor objects (Descriptor, FieldDescriptor, |
| // etc.) and adding them to the pool's symbol table. |
| // 2. Cross-linking: Resolving references between descriptors (e.g. field types |
| // referenced by name). |
| // 3. Option interpretation: Resolving and parsing custom options. |
| // 4. Validation: Verifying that the resulting descriptors satisfy all rules and |
| // constraints. |
| // |
| // This class is short-lived and instantiated for a single BuildFile operation. |
| class DescriptorBuilder { |
| public: |
| static std::unique_ptr<DescriptorBuilder> New( |
| const DescriptorPool* pool, DescriptorPool::Tables* tables, |
| DescriptorPool::DeferredValidation& deferred_validation, |
| DescriptorPool::ErrorCollector* error_collector) { |
| return std::unique_ptr<DescriptorBuilder>(new DescriptorBuilder( |
| pool, tables, deferred_validation, error_collector)); |
| } |
| |
| ~DescriptorBuilder(); |
| |
| const FileDescriptor* BuildFile(const FileDescriptorProto& proto); |
| |
| private: |
| DescriptorBuilder(const DescriptorPool* pool, DescriptorPool::Tables* tables, |
| DescriptorPool::DeferredValidation& deferred_validation, |
| DescriptorPool::ErrorCollector* error_collector); |
| |
| friend class OptionInterpreter; |
| |
| // Non-recursive part of BuildFile functionality. |
| FileDescriptor* BuildFileImpl(const FileDescriptorProto& proto, |
| internal::FlatAllocator& alloc); |
| |
| const DescriptorPool* pool_; |
| DescriptorPool::Tables* tables_; // for convenience |
| DescriptorPool::DeferredValidation& deferred_validation_; |
| DescriptorPool::ErrorCollector* error_collector_; |
| |
| absl::optional<FeatureResolver> feature_resolver_ = absl::nullopt; |
| |
| // As we build descriptors we store copies of the options messages in |
| // them. We put pointers to those copies in this vector, as we build, so we |
| // can later (after cross-linking) interpret those options. |
| std::vector<OptionsToInterpret> options_to_interpret_; |
| |
| bool had_errors_; |
| std::string filename_; |
| FileDescriptor* file_; |
| FileDescriptorTables* file_tables_ = nullptr; |
| absl::flat_hash_set<const FileDescriptor*> dependencies_; |
| absl::flat_hash_set<const FileDescriptor*> option_dependencies_; |
| |
| struct MessageHints { |
| int fields_to_suggest = 0; |
| const Message* first_reason = nullptr; |
| DescriptorPool::ErrorCollector::ErrorLocation first_reason_location = |
| DescriptorPool::ErrorCollector::ErrorLocation::OTHER; |
| |
| void RequestHintOnFieldNumbers( |
| const Message& reason, |
| DescriptorPool::ErrorCollector::ErrorLocation reason_location, |
| int range_start = 0, int range_end = 1) { |
| auto fit = [](int value) { |
| return std::min(std::max(value, 0), FieldDescriptor::kMaxNumber); |
| }; |
| fields_to_suggest = |
| fit(fields_to_suggest + fit(fit(range_end) - fit(range_start))); |
| if (first_reason) return; |
| first_reason = &reason; |
| first_reason_location = reason_location; |
| } |
| }; |
| |
| absl::flat_hash_map<const Descriptor*, MessageHints> message_hints_; |
| |
| // unused_dependency_ is used to record the unused imported files. |
| // Note: public import is not considered. |
| absl::flat_hash_set<const FileDescriptor*> unused_dependency_; |
| |
| // If LookupSymbol() finds a symbol that is in a file which is not a declared |
| // dependency of this file, it will fail, but will set |
| // possible_undeclared_dependency_ to point at that file. This is only used |
| // by AddNotDefinedError() to report a more useful error message. |
| // possible_undeclared_dependency_name_ is the name of the symbol that was |
| // actually found in possible_undeclared_dependency_, which may be a parent |
| // of the symbol actually looked for. |
| const FileDescriptor* possible_undeclared_dependency_; |
| std::string possible_undeclared_dependency_name_; |
| |
| // If LookupSymbol() could resolve a symbol which is not defined, |
| // record the resolved name. This is only used by AddNotDefinedError() |
| // to report a more useful error message. |
| std::string undefine_resolved_name_; |
| |
| // Tracker for current recursion depth to implement recursion protection. |
| // |
| // Counts down to 0 when there is no depth remaining. |
| // |
| // Maximum recursion depth corresponds to 32 nested message declarations. |
| int recursion_depth_ = internal::cpp::MaxMessageDeclarationNestingDepth(); |
| |
| // Note: Both AddError and AddWarning functions are extremely sensitive to |
| // the *caller* stack space used. We call these functions many times in |
| // complex code paths that are hot and likely to be inlined heavily. However, |
| // these calls themselves are cold error paths. But stack space used by the |
| // code that sets up the call in many cases is paid for even when the call |
| // isn't reached. To optimize this, we use `absl::string_view` to reuse |
| // string objects where possible for the inputs and for the error message |
| // itself we use a closure to build the error message inside these routines. |
| // The routines themselves are marked to prevent inlining and this lets us |
| // move the large code sometimes required to produce a useful error message |
| // entirely into a helper closure rather than the immediate caller. |
| // |
| // The `const char*` overload should only be used for string literal messages |
| // where this is a frustrating amount of overhead and there is no harm in |
| // directly using the literal. |
| void AddError(absl::string_view element_name, const Message& descriptor, |
| DescriptorPool::ErrorCollector::ErrorLocation location, |
| absl::FunctionRef<std::string()> make_error); |
| void AddError(absl::string_view element_name, const Message& descriptor, |
| DescriptorPool::ErrorCollector::ErrorLocation location, |
| const char* error); |
| void AddRecursiveImportError(const FileDescriptorProto& proto, int from_here); |
| void AddTwiceListedError(const FileDescriptorProto& proto, |
| absl::string_view import_name); |
| void AddImportError(const FileDescriptorProto& proto, |
| absl::string_view import_name); |
| |
| // Adds an error indicating that undefined_symbol was not defined. Must |
| // only be called after LookupSymbol() fails. |
| void AddNotDefinedError( |
| absl::string_view element_name, const Message& descriptor, |
| DescriptorPool::ErrorCollector::ErrorLocation location, |
| absl::string_view undefined_symbol); |
| |
| void AddWarning(absl::string_view element_name, const Message& descriptor, |
| DescriptorPool::ErrorCollector::ErrorLocation location, |
| absl::FunctionRef<std::string()> make_error); |
| void AddWarning(absl::string_view element_name, const Message& descriptor, |
| DescriptorPool::ErrorCollector::ErrorLocation location, |
| const char* error); |
| |
| // Silly helper which determines if the given file is in the given package. |
| // I.e., either file->package() == package_name or file->package() is a |
| // nested package within package_name. |
| bool IsInPackage(const FileDescriptor* file, absl::string_view package_name); |
| |
| // Helper function which finds all public dependencies of the given file, and |
| // stores them in the dependencies_ set in the builder. |
| void RecordPublicDependencies(const FileDescriptor* file); |
| |
| // Helper function which finds all public option dependencies of the given |
| // file, and stores them in the option_dependencies_ set in the builder. |
| void RecordPublicOptionDependencies(const FileDescriptor* file); |
| |
| // Like tables_->FindSymbol(), but additionally: |
| // - Search the pool's underlay if not found in tables_. |
| // - Insure that the resulting Symbol is from one of the file's declared |
| // dependencies. |
| Symbol FindSymbol(absl::string_view name, bool build_it = true); |
| |
| // Like FindSymbol() but does not require that the symbol is in one of the |
| // file's declared dependencies. |
| Symbol FindSymbolNotEnforcingDeps(absl::string_view name, |
| bool build_it = true); |
| |
| // This implements the body of FindSymbolNotEnforcingDeps(). |
| Symbol FindSymbolNotEnforcingDepsHelper(const DescriptorPool* pool, |
| absl::string_view name, |
| bool build_it = true); |
| |
| // Like FindSymbol(), but looks up the name relative to some other symbol |
| // name. This first searches siblings of relative_to, then siblings of its |
| // parents, etc. For example, LookupSymbol("foo.bar", "baz.moo.corge") makes |
| // the following calls, returning the first non-null result: |
| // FindSymbol("baz.moo.foo.bar"), FindSymbol("baz.foo.bar"), |
| // FindSymbol("foo.bar"). If AllowUnknownDependencies() has been called |
| // on the DescriptorPool, this will generate a placeholder type if |
| // the name is not found (unless the name itself is malformed). The |
| // placeholder_type parameter indicates what kind of placeholder should be |
| // constructed in this case. The resolve_mode parameter determines whether |
| // any symbol is returned, or only symbols that are types. Note, however, |
| // that LookupSymbol may still return a non-type symbol in LOOKUP_TYPES mode, |
| // if it believes that's all it could refer to. The caller should always |
| // check that it receives the type of symbol it was expecting. |
| enum ResolveMode { LOOKUP_ALL, LOOKUP_TYPES }; |
| Symbol LookupSymbol(absl::string_view name, absl::string_view relative_to, |
| DescriptorPool::PlaceholderType placeholder_type = |
| DescriptorPool::PLACEHOLDER_MESSAGE, |
| ResolveMode resolve_mode = LOOKUP_ALL, |
| bool build_it = true); |
| |
| // Like LookupSymbol() but will not return a placeholder even if |
| // AllowUnknownDependencies() has been used. |
| Symbol LookupSymbolNoPlaceholder(absl::string_view name, |
| absl::string_view relative_to, |
| ResolveMode resolve_mode = LOOKUP_ALL, |
| bool build_it = true); |
| |
| // Calls tables_->AddSymbol() and records an error if it fails. Returns |
| // true if successful or false if failed, though most callers can ignore |
| // the return value since an error has already been recorded. |
| bool AddSymbol(absl::string_view full_name, const void* parent, |
| absl::string_view name, const Message& proto, Symbol symbol); |
| |
| // Like AddSymbol(), but succeeds if the symbol is already defined as long |
| // as the existing definition is also a package (because it's OK to define |
| // the same package in two different files). Also adds all parents of the |
| // package to the symbol table (e.g. AddPackage("foo.bar", ...) will add |
| // "foo.bar" and "foo" to the table). |
| void AddPackage(absl::string_view name, const Message& proto, |
| FileDescriptor* file, bool toplevel); |
| |
| // Checks that the symbol name contains only alphanumeric characters and |
| // underscores. Records an error otherwise. |
| void ValidateSymbolName(absl::string_view name, absl::string_view full_name, |
| const Message& proto); |
| |
| // Allocates a copy of orig_options in tables_ and stores it in the |
| // descriptor. Remembers its uninterpreted options, to be interpreted |
| // later. DescriptorT must be one of the Descriptor messages from |
| // descriptor.proto. |
| template <class DescriptorT> |
| void AllocateOptions(const typename DescriptorT::Proto& proto, |
| DescriptorT* descriptor, int options_field_tag, |
| absl::string_view option_name, |
| internal::FlatAllocator& alloc); |
| // Specialization for FileOptions. |
| void AllocateOptions(const FileDescriptorProto& proto, |
| FileDescriptor* descriptor, |
| internal::FlatAllocator& alloc); |
| |
| // Implementation for AllocateOptions(). Don't call this directly. |
| template <class DescriptorT> |
| const typename DescriptorT::OptionsType* AllocateOptionsImpl( |
| absl::string_view name_scope, absl::string_view element_name, |
| const typename DescriptorT::Proto& proto, SourceCodePath options_path, |
| absl::string_view option_name, internal::FlatAllocator& alloc); |
| |
| // Allocates and resolves any feature sets that need to be owned by a given |
| // descriptor. This also strips features out of the mutable options message to |
| // prevent leaking of unresolved features. |
| // Note: This must be used during a pre-order traversal of the |
| // descriptor tree, so that each descriptor's parent has a fully resolved |
| // feature set already. |
| template <class DescriptorT> |
| void ResolveFeatures(const typename DescriptorT::Proto& proto, |
| DescriptorT* descriptor, |
| typename DescriptorT::OptionsType* options, |
| internal::FlatAllocator& alloc); |
| void ResolveFeatures(const FileDescriptorProto& proto, |
| FileDescriptor* descriptor, FileOptions* options, |
| internal::FlatAllocator& alloc); |
| template <class DescriptorT> |
| void ResolveFeaturesImpl( |
| Edition edition, const typename DescriptorT::Proto& proto, |
| DescriptorT* descriptor, typename DescriptorT::OptionsType* options, |
| internal::FlatAllocator& alloc, |
| DescriptorPool::ErrorCollector::ErrorLocation error_location, |
| bool force_merge = false); |
| |
| void PostProcessFieldFeatures(FieldDescriptor& field, |
| const FieldDescriptorProto& proto); |
| |
| // Allocates an array of two strings, the first one is a copy of |
| // `proto_name`, and the second one is the full name. Full proto name is |
| // "scope.proto_name" if scope is non-empty and "proto_name" otherwise. |
| auto AllocateNameStrings(absl::string_view scope, |
| absl::string_view proto_name, const Message& entity, |
| internal::FlatAllocator& alloc); |
| |
| // These methods all have the same signature for the sake of the BUILD_ARRAY |
| // macro, below. |
| void BuildMessage(const DescriptorProto& proto, const Descriptor* parent, |
| Descriptor* result, internal::FlatAllocator& alloc); |
| void BuildFieldOrExtension(const FieldDescriptorProto& proto, |
| Descriptor* parent, FieldDescriptor* result, |
| bool is_extension, internal::FlatAllocator& alloc); |
| void BuildField(const FieldDescriptorProto& proto, Descriptor* parent, |
| FieldDescriptor* result, internal::FlatAllocator& alloc) { |
| BuildFieldOrExtension(proto, parent, result, false, alloc); |
| } |
| void BuildExtension(const FieldDescriptorProto& proto, Descriptor* parent, |
| FieldDescriptor* result, internal::FlatAllocator& alloc) { |
| BuildFieldOrExtension(proto, parent, result, true, alloc); |
| } |
| void BuildExtensionRange(const DescriptorProto::ExtensionRange& proto, |
| const Descriptor* parent, |
| Descriptor::ExtensionRange* result, |
| internal::FlatAllocator& alloc); |
| void BuildReservedRange(const DescriptorProto::ReservedRange& proto, |
| const Descriptor* parent, |
| Descriptor::ReservedRange* result, |
| internal::FlatAllocator& alloc); |
| void BuildReservedRange(const EnumDescriptorProto::EnumReservedRange& proto, |
| const EnumDescriptor* parent, |
| EnumDescriptor::ReservedRange* result, |
| internal::FlatAllocator& alloc); |
| void BuildOneof(const OneofDescriptorProto& proto, Descriptor* parent, |
| OneofDescriptor* result, internal::FlatAllocator& alloc); |
| void BuildEnum(const EnumDescriptorProto& proto, const Descriptor* parent, |
| EnumDescriptor* result, internal::FlatAllocator& alloc); |
| void BuildEnumValue(const EnumValueDescriptorProto& proto, |
| const EnumDescriptor* parent, EnumValueDescriptor* result, |
| internal::FlatAllocator& alloc); |
| void BuildService(const ServiceDescriptorProto& proto, const void* dummy, |
| ServiceDescriptor* result, internal::FlatAllocator& alloc); |
| void BuildMethod(const MethodDescriptorProto& proto, |
| const ServiceDescriptor* parent, MethodDescriptor* result, |
| internal::FlatAllocator& alloc); |
| |
| void CheckFieldJsonNameUniqueness(const DescriptorProto& proto, |
| const Descriptor* result); |
| void CheckFieldJsonNameUniqueness(absl::string_view message_name, |
| const DescriptorProto& message, |
| const Descriptor* descriptor, |
| bool use_custom_names); |
| void CheckEnumValueUniqueness(const EnumDescriptorProto& proto, |
| const EnumDescriptor* result); |
| |
| void CheckEnumCustomStringUniqueness(const EnumDescriptorProto& proto, |
| const EnumDescriptor* result); |
| |
| void LogUnusedDependency(const FileDescriptorProto& proto, |
| const FileDescriptor* result); |
| |
| // Must be run only after building. |
| // |
| // NOTE: Options will not be available during cross-linking, as they |
| // have not yet been interpreted. Defer any handling of options to the |
| // Validate*Options methods. |
| void CrossLinkFile(FileDescriptor* file, const FileDescriptorProto& proto); |
| void CrossLinkMessage(Descriptor* message, const DescriptorProto& proto); |
| void CrossLinkField(FieldDescriptor* field, |
| const FieldDescriptorProto& proto); |
| void CrossLinkService(ServiceDescriptor* service, |
| const ServiceDescriptorProto& proto); |
| void CrossLinkMethod(MethodDescriptor* method, |
| const MethodDescriptorProto& proto); |
| void SuggestFieldNumbers(FileDescriptor* file, |
| const FileDescriptorProto& proto); |
| |
| |
| // Checks that the extension field matches what is declared. |
| void CheckExtensionDeclaration(const FieldDescriptor& field, |
| const FieldDescriptorProto& proto, |
| absl::string_view declared_full_name, |
| absl::string_view declared_type_name, |
| bool is_repeated); |
| // Checks that the extension field type matches the declared type. It also |
| // handles message types that look like non-message types such as "fixed64" vs |
| // ".fixed64". |
| void CheckExtensionDeclarationFieldType(const FieldDescriptor& field, |
| const FieldDescriptorProto& proto, |
| absl::string_view type); |
| |
| // A helper class for interpreting options. |
| class OptionInterpreter { |
| public: |
| // Creates an interpreter that operates in the context of the pool of the |
| // specified builder, which must not be nullptr. We don't take ownership of |
| // the builder. |
| explicit OptionInterpreter(DescriptorBuilder* builder); |
| OptionInterpreter(const OptionInterpreter&) = delete; |
| OptionInterpreter& operator=(const OptionInterpreter&) = delete; |
| |
| ~OptionInterpreter(); |
| |
| // Interprets the uninterpreted options in the specified Options message. |
| // On error, calls AddError() on the underlying builder and returns false. |
| // Otherwise returns true. |
| bool InterpretOptionExtensions(OptionsToInterpret* options_to_interpret); |
| |
| // Interprets the uninterpreted feature options in the specified Options |
| // message. On error, calls AddError() on the underlying builder and returns |
| // false. Otherwise returns true. |
| bool InterpretNonExtensionOptions(OptionsToInterpret* options_to_interpret); |
| |
| // Updates the given source code info by re-writing uninterpreted option |
| // locations to refer to the corresponding interpreted option. |
| void UpdateSourceCodeInfo(SourceCodeInfo* info); |
| |
| class AggregateOptionFinder; |
| |
| private: |
| bool InterpretOptionsImpl(OptionsToInterpret* options_to_interpret, |
| bool skip_extensions); |
| |
| // Interprets uninterpreted_option_ on the specified message, which |
| // must be the mutable copy of the original options message to which |
| // uninterpreted_option_ belongs. The given src_path is the source |
| // location path to the uninterpreted option, and options_path is the |
| // source location path to the options message. The location paths are |
| // recorded and then used in UpdateSourceCodeInfo. |
| // The features boolean controls whether or not we should only interpret |
| // feature options or skip them entirely. |
| bool InterpretSingleOption(Message* options, const SourceCodePath& src_path, |
| const SourceCodePath& options_path, |
| bool skip_extensions); |
| |
| // Adds the uninterpreted_option to the given options message verbatim. |
| // Used when AllowUnknownDependencies() is in effect and we can't find |
| // the option's definition. |
| void AddWithoutInterpreting(const UninterpretedOption& uninterpreted_option, |
| Message* options); |
| |
| // A recursive helper function that drills into the intermediate fields |
| // in unknown_fields to check if field innermost_field is set on the |
| // innermost message. Returns false and sets an error if so. |
| bool ExamineIfOptionIsSet( |
| std::vector<const FieldDescriptor*>::const_iterator |
| intermediate_fields_iter, |
| std::vector<const FieldDescriptor*>::const_iterator |
| intermediate_fields_end, |
| const FieldDescriptor* innermost_field, |
| const std::string& debug_msg_name, |
| const UnknownFieldSet& unknown_fields); |
| |
| // Validates the value for the option field of the currently interpreted |
| // option and then sets it on the unknown_field. |
| bool SetOptionValue(const FieldDescriptor* option_field, |
| UnknownFieldSet* unknown_fields, Message* options); |
| |
| // Parses an aggregate value for a CPPTYPE_MESSAGE option and |
| // saves it into *unknown_fields. |
| bool SetAggregateOption(const FieldDescriptor* option_field, |
| UnknownFieldSet* unknown_fields, Message* options); |
| |
| // Convenience functions to set an int field the right way, depending on |
| // its wire type (a single int CppType can represent multiple wire types). |
| void SetInt32(int number, int32_t value, FieldDescriptor::Type type, |
| UnknownFieldSet* unknown_fields); |
| void SetInt64(int number, int64_t value, FieldDescriptor::Type type, |
| UnknownFieldSet* unknown_fields); |
| void SetUInt32(int number, uint32_t value, FieldDescriptor::Type type, |
| UnknownFieldSet* unknown_fields); |
| void SetUInt64(int number, uint64_t value, FieldDescriptor::Type type, |
| UnknownFieldSet* unknown_fields); |
| |
| // A helper function that adds an error at the specified location of the |
| // option we're currently interpreting, and returns false. |
| bool AddOptionError(DescriptorPool::ErrorCollector::ErrorLocation location, |
| absl::FunctionRef<std::string()> make_error) { |
| builder_->AddError(options_to_interpret_->element_name, |
| *uninterpreted_option_, location, make_error); |
| return false; |
| } |
| |
| // A helper function that adds an error at the location of the option name |
| // and returns false. |
| bool AddNameError(absl::FunctionRef<std::string()> make_error) { |
| #ifdef PROTOBUF_INTERNAL_IGNORE_FIELD_NAME_ERRORS_ |
| return true; |
| #else // PROTOBUF_INTERNAL_IGNORE_FIELD_NAME_ERRORS_ |
| return AddOptionError(DescriptorPool::ErrorCollector::OPTION_NAME, |
| make_error); |
| #endif // PROTOBUF_INTERNAL_IGNORE_FIELD_NAME_ERRORS_ |
| } |
| |
| // A helper function that adds an error at the location of the option name |
| // and returns false. |
| bool AddValueError(absl::FunctionRef<std::string()> make_error) { |
| return AddOptionError(DescriptorPool::ErrorCollector::OPTION_VALUE, |
| make_error); |
| } |
| |
| // We interpret against this builder's pool. Is never nullptr. We don't own |
| // this pointer. |
| DescriptorBuilder* builder_; |
| |
| // The options we're currently interpreting, or nullptr if we're not in a |
| // call to InterpretOptions. |
| const OptionsToInterpret* options_to_interpret_; |
| |
| // The option we're currently interpreting within options_to_interpret_, or |
| // nullptr if we're not in a call to InterpretOptions(). This points to a |
| // submessage of the original option, not the mutable copy. Therefore we |
| // can use it to find locations recorded by the parser. |
| const UninterpretedOption* uninterpreted_option_; |
| |
| // This maps the element path of uninterpreted options to the element path |
| // of the resulting interpreted option. This is used to modify a file's |
| // source code info to account for option interpretation. |
| absl::flat_hash_map<SourceCodePath, SourceCodePath> interpreted_paths_; |
| |
| // This maps the path to a repeated option field to the known number of |
| // elements the field contains. This is used to track the compute the |
| // index portion of the element path when interpreting a single option. |
| absl::flat_hash_map<SourceCodePath, int> repeated_option_counts_; |
| |
| // Factory used to create the dynamic messages we need to parse |
| // any aggregate option values we encounter. |
| DynamicMessageFactory dynamic_factory_; |
| }; |
| |
| // Work-around for broken compilers: According to the C++ standard, |
| // OptionInterpreter should have access to the private members of any class |
| // which has declared DescriptorBuilder as a friend. Unfortunately some old |
| // versions of GCC and other compilers do not implement this correctly. So, |
| // we have to have these intermediate methods to provide access. We also |
| // redundantly declare OptionInterpreter a friend just to make things extra |
| // clear for these bad compilers. |
| friend class OptionInterpreter; |
| friend class OptionInterpreter::AggregateOptionFinder; |
| |
| static bool get_allow_unknown(const DescriptorPool* pool) { |
| return pool->allow_unknown_; |
| } |
| static bool get_enforce_weak(const DescriptorPool* pool) { |
| return pool->enforce_weak_; |
| } |
| static bool get_is_placeholder(const Descriptor* descriptor) { |
| return descriptor != nullptr && descriptor->is_placeholder_; |
| } |
| static void assert_mutex_held(const DescriptorPool* pool) { |
| if (pool->mutex_ != nullptr) { |
| pool->mutex_->AssertHeld(); |
| } |
| } |
| |
| // Must be run only after options have been interpreted. |
| // |
| // NOTE: Validation code must only reference the options in the mutable |
| // descriptors, which are the ones that have been interpreted. The const |
| // proto references are passed in only so they can be provided to calls to |
| // AddError(). Do not look at their options, which have not been interpreted. |
| void ValidateOptions(const FileDescriptor* file, |
| const FileDescriptorProto& proto); |
| void ValidateFileFeatures(const FileDescriptor* file, |
| const FileDescriptorProto& proto); |
| void ValidateOptions(const Descriptor* message, const DescriptorProto& proto); |
| void ValidateOptions(const OneofDescriptor* oneof, |
| const OneofDescriptorProto& proto); |
| void ValidateOptions(const FieldDescriptor* field, |
| const FieldDescriptorProto& proto); |
| void ValidateFieldFeatures(const FieldDescriptor* field, |
| const FieldDescriptorProto& proto); |
| void ValidateOptions(const EnumDescriptor* enm, |
| const EnumDescriptorProto& proto); |
| void ValidateOptions(const EnumValueDescriptor* enum_value, |
| const EnumValueDescriptorProto& proto); |
| void ValidateOptions(const Descriptor::ExtensionRange* range, |
| const DescriptorProto::ExtensionRange& proto) {} |
| void ValidateExtensionRangeOptions(const DescriptorProto& proto, |
| const Descriptor& message); |
| void MaybeAddError(const absl::Status& status, absl::string_view full_name, |
| const Message& descriptor, |
| DescriptorPool::ErrorCollector::ErrorLocation location); |
| void ValidateExtensionDeclaration( |
| absl::string_view full_name, |
| const RepeatedPtrField<ExtensionRangeOptions_Declaration>& declarations, |
| const DescriptorProto_ExtensionRange& proto, |
| absl::flat_hash_set<absl::string_view>& full_name_set); |
| void ValidateOptions(const ServiceDescriptor* service, |
| const ServiceDescriptorProto& proto); |
| void ValidateOptions(const MethodDescriptor* method, |
| const MethodDescriptorProto& proto); |
| void ValidateProto3(const FileDescriptor* file, |
| const FileDescriptorProto& proto); |
| void ValidateProto3Message(const Descriptor* message, |
| const DescriptorProto& proto); |
| void ValidateProto3Field(const FieldDescriptor* field, |
| const FieldDescriptorProto& proto); |
| |
| // Returns true if the map entry message is compatible with the |
| // auto-generated entry message from map fields syntax. |
| bool ValidateMapEntry(const FieldDescriptor* field, |
| const FieldDescriptorProto& proto); |
| |
| // Recursively detects naming conflicts with map entry types for a |
| // better error message. |
| void DetectMapConflicts(const Descriptor* message, |
| const DescriptorProto& proto); |
| |
| void ValidateJSType(const FieldDescriptor* field, |
| const FieldDescriptorProto& proto); |
| |
| template <typename DescriptorT, typename DescriptorProtoT> |
| void ValidateNamingStyle(const DescriptorT*, |
| const DescriptorProtoT&); |
| |
| template <typename DescriptorT> |
| bool IsStyleOrGreater(const DescriptorT* descriptor, |
| FeatureSet::EnforceNamingStyle style) { |
| return internal::InternalFeatureHelper::GetFeatures(*descriptor) |
| .enforce_naming_style() >= style && |
| // Required because STYLE_LEGACY comes after STYLE2024 in enum |
| // definition. |
| internal::InternalFeatureHelper::GetFeatures(*descriptor) |
| .enforce_naming_style() != FeatureSet::STYLE_LEGACY; |
| } |
| |
| // Nothing to validate for extension ranges. This overload only exists |
| // so that VisitDescriptors can be exhaustive. |
| void ValidateNamingStyle(const Descriptor::ExtensionRange* ext_range, |
| const DescriptorProto::ExtensionRange& proto) {} |
| |
| // When called, check the listed descriptor against protobuf limits, such as |
| // max number of fields per message, max number of fields in a oneof, or max |
| // number of values in an enum. This is a feature introduced in Edition 2026. |
| void ValidateProtoLimits(const Descriptor* message, |
| const DescriptorProto& proto); |
| void ValidateProtoLimits(const OneofDescriptor* oneof, |
| const OneofDescriptorProto& proto); |
| void ValidateProtoLimits(const EnumDescriptor* enum_descriptor, |
| const EnumDescriptorProto& proto); |
| |
| // Overloads with nothing to validate. These overload only exist |
| // so that VisitDescriptors can be exhaustive. |
| void ValidateProtoLimits(const FileDescriptor* file, |
| const FileDescriptorProto& proto) {} |
| void ValidateProtoLimits(const FieldDescriptor* field, |
| const FieldDescriptorProto& proto) {} |
| void ValidateProtoLimits(const EnumValueDescriptor* file, |
| const EnumValueDescriptorProto& proto) {} |
| void ValidateProtoLimits(const ServiceDescriptor* file, |
| const ServiceDescriptorProto& proto) {} |
| void ValidateProtoLimits(const MethodDescriptor* file, |
| const MethodDescriptorProto& proto) {} |
| void ValidateProtoLimits(const Descriptor::ExtensionRange* ext_range, |
| const DescriptorProto::ExtensionRange& proto) {} |
| }; |
| |
| } // namespace internal |
| } // namespace protobuf |
| } // namespace google |
| |
| #include "google/protobuf/port_undef.inc" |
| |
| #endif // GOOGLE_PROTOBUF_DESCRIPTOR_BUILDER_H__ |