blob: e10514f97f2262405940b1c0df454b03d5f5feba [file]
// 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_OPTION_INTERPRETER_H__
#define GOOGLE_PROTOBUF_OPTION_INTERPRETER_H__
#include <cstdint>
#include <string>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/functional/function_ref.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor_builder.h"
#include "google/protobuf/dynamic_message.h"
#include "google/protobuf/message.h"
#include "google/protobuf/text_format.h"
// Must be included last.
#include "google/protobuf/port_def.inc"
namespace google {
namespace protobuf {
namespace internal {
// OptionInterpreter is a helper class used during the descriptor building
// process to resolve and parse custom options.
//
// During initial parsing of a .proto file, custom options cannot be fully
// parsed because their definitions (which may be extensions defined in other
// files) are not yet known. The parser stores these as raw name-value pairs
// in `UninterpretedOption` messages.
//
// Once the descriptor pool has loaded dependencies and resolved symbols,
// OptionInterpreter is used to "interpret" these options:
// 1. **Name Resolution**: It resolves the path of names in the uninterpreted
// option (e.g., `(my_extension).sub_field`) to a concrete `FieldDescriptor`.
// 2. **Value Validation**: It validates that the value in the uninterpreted
// option matches the type of the resolved field.
// 3. **Storage as Unknown Fields**: It stores the interpreted values in the
// target options message (e.g., `FileOptions`) using `UnknownFieldSet`.
// This deferred binding is necessary because the options message structure
// might not yet have the extension fields registered in its reflection.
// Serializing and deserializing the message later will correctly populate
// the extension fields once they are known.
// 4. **Aggregate Parsing**: It parses aggregate option values (specified
// in text format) using `TextFormat` with a custom resolver
// (`AggregateOptionFinder`) to handle nested extensions and types.
// 5. **Source Code Info Mapping**: It updates `SourceCodeInfo` to map the
// source locations of the original `UninterpretedOption`s to the
// resulting interpreted option fields, ensuring correct location reporting
// for compilers and IDEs.
//
// OptionInterpreter operates in two passes (non-extensions first, then
// extensions) to allow feature resolution to happen in the correct order.
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. If update_source_code_info is false, path tracking and map
// population for UpdateSourceCodeInfo are omitted.
explicit OptionInterpreter(DescriptorBuilder* builder,
bool update_source_code_info);
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);
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,
const SourceCodePath& src_path,
const SourceCodePath& dest_path);
// 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,
const SourceCodePath& src_path,
const SourceCodePath& dest_path);
// Represents the relative source location of a field specified within an
// aggregate option block (e.g., `option (my_opt) = { field_a: 1 }`).
struct AggregateFieldLocation {
// Path to the uninterpreted option in the descriptor (e.g., [options,
// index]). This is used later during UpdateSourceCodeInfo to find the
// absolute source location (line and column) where the aggregate option
// block starts, enabling the calculation of absolute source locations for
// the nested fields within the block.
SourceCodePath uninterpreted_path;
// Destination path for the option field itself.
SourceCodePath field_dest_path;
// Marker for the value type (e.g. kPositiveIntValueFieldNumber, etc.)
int value_marker;
// Relative start and end line/column span of the field name within the
// aggregate string.
TextFormat::ParseLocationRange name_range;
// Relative start and end line/column span of the field value within the
// aggregate string.
TextFormat::ParseLocationRange val_range;
};
// Recursively traverses a parsed aggregate option message and its
// ParseInfoTree to collect relative locations for all populated sub-fields.
void CollectAggregateFieldLocations(const Message& message,
const TextFormat::ParseInfoTree& tree,
const SourceCodePath& uninterpreted_path,
SourceCodePath& dest_path);
// Determines the appropriate UninterpretedOption value field number (e.g.,
// kPositiveIntValueFieldNumber, kStringValueFieldNumber) for a given field.
int GetValueMarker(const FieldDescriptor* field, const Message& message,
int index);
// Translates relative ParseLocationRange coordinates (from within an
// aggregate option string) into absolute .proto file coordinates using base
// line/column.
void SetSpan(SourceCodeInfo_Location* loc,
const SourceCodeInfo_Location& base_loc,
const TextFormat::ParseLocationRange& range);
// 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_;
// Accumulates all sub-field locations gathered during aggregate option
// interpretation.
std::vector<AggregateFieldLocation> aggregate_field_locations_;
// Indicates whether source code info collection is enabled during option
// interpretation. Source code info requires extra bookkeeping and processing,
// and for most production use cases, is not necessary.
bool update_source_code_info_;
};
} // namespace internal
} // namespace protobuf
} // namespace google
#include "google/protobuf/port_undef.inc"
#endif // GOOGLE_PROTOBUF_OPTION_INTERPRETER_H__