blob: 9fb5b02cd4780cd33f3c23cc788d47c0ae046dea [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
#include "google/protobuf/option_interpreter.h"
#include <fcntl.h>
#include <limits.h>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iterator>
#include <limits>
#include <memory>
#include <new> // IWYU pragma: keep
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/functional/function_ref.h"
#include "absl/log/absl_check.h"
#include "absl/log/absl_log.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "google/protobuf/any.h"
#include "google/protobuf/cpp_features.pb.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/descriptor_builder.h"
#include "google/protobuf/descriptor_lite.h"
#include "google/protobuf/dynamic_message.h"
#include "google/protobuf/generated_message_util.h"
#include "google/protobuf/io/coded_stream.h"
#include "google/protobuf/io/tokenizer.h"
#include "google/protobuf/json_enumvalue_options.pb.h"
#include "google/protobuf/message.h"
#include "google/protobuf/message_lite.h"
#include "google/protobuf/parse_context.h"
#include "google/protobuf/repeated_ptr_field.h"
#include "google/protobuf/symbol.h"
#include "google/protobuf/symbol_checker.h"
#include "google/protobuf/text_format.h"
#include "google/protobuf/unknown_field_set.h"
// Must be included last.
#include "google/protobuf/port_def.inc"
namespace google {
namespace protobuf {
namespace internal {
OptionInterpreter::OptionInterpreter(DescriptorBuilder* builder,
bool update_source_code_info)
: builder_(builder), update_source_code_info_(update_source_code_info) {
ABSL_CHECK(builder_);
}
OptionInterpreter::~OptionInterpreter() = default;
bool OptionInterpreter::InterpretOptionExtensions(
OptionsToInterpret* options_to_interpret) {
return InterpretOptionsImpl(options_to_interpret, /*skip_extensions=*/false);
}
bool OptionInterpreter::InterpretNonExtensionOptions(
OptionsToInterpret* options_to_interpret) {
return InterpretOptionsImpl(options_to_interpret, /*skip_extensions=*/true);
}
bool OptionInterpreter::InterpretOptionsImpl(
OptionsToInterpret* options_to_interpret, bool skip_extensions) {
// Note that these may be in different pools, so we can't use the same
// descriptor and reflection objects on both.
Message* options = options_to_interpret->options;
const Message* original_options = options_to_interpret->original_options;
bool failed = false;
options_to_interpret_ = options_to_interpret;
// Find the uninterpreted_option field in the mutable copy of the options
// and clear them, since we're about to interpret them.
const FieldDescriptor* uninterpreted_options_field =
options->GetDescriptor()->FindFieldByName("uninterpreted_option");
ABSL_CHECK(uninterpreted_options_field != nullptr)
<< "No field named \"uninterpreted_option\" in the Options proto.";
options->GetReflection()->ClearField(options, uninterpreted_options_field);
SourceCodePath src_path;
if (update_source_code_info_) {
src_path = options_to_interpret->element_path;
src_path.push_back(uninterpreted_options_field->number());
}
// Find the uninterpreted_option field in the original options.
const FieldDescriptor* original_uninterpreted_options_field =
original_options->GetDescriptor()->FindFieldByName(
"uninterpreted_option");
ABSL_CHECK(original_uninterpreted_options_field != nullptr)
<< "No field named \"uninterpreted_option\" in the Options proto.";
const int num_uninterpreted_options =
original_options->GetReflection()->FieldSize(
*original_options, original_uninterpreted_options_field);
for (int i = 0; i < num_uninterpreted_options; ++i) {
if (update_source_code_info_) {
src_path.push_back(i);
}
uninterpreted_option_ = DownCastMessage<UninterpretedOption>(
&original_options->GetReflection()->GetRepeatedMessage(
*original_options, original_uninterpreted_options_field, i));
if (!InterpretSingleOption(options, src_path,
options_to_interpret->element_path,
skip_extensions)) {
// Error already added by InterpretSingleOption().
failed = true;
break;
}
if (update_source_code_info_) {
src_path.pop_back();
}
}
// Reset these, so we don't have any dangling pointers.
uninterpreted_option_ = nullptr;
options_to_interpret_ = nullptr;
if (!failed) {
// InterpretSingleOption() added the interpreted options in the
// UnknownFieldSet, in case the option isn't yet known to us. Now we
// serialize the options message and deserialize it back. That way, any
// option fields that we do happen to know about will get moved from the
// UnknownFieldSet into the real fields, and thus be available right away.
// If they are not known, that's OK too. They will get reparsed into the
// UnknownFieldSet and wait there until the message is parsed by something
// that does know about the options.
// Keep the unparsed options around in case the reparsing fails.
std::unique_ptr<Message> unparsed_options(options->New());
options->GetReflection()->Swap(unparsed_options.get(), options);
std::string buf;
if (!unparsed_options->AppendToString(&buf) ||
!options->ParseFromString(buf)) {
builder_->AddError(
options_to_interpret->element_name, *original_options,
DescriptorPool::ErrorCollector::OTHER, [&] {
return absl::StrCat(
"Some options could not be correctly parsed using the proto "
"descriptors compiled into this binary.\n"
"Unparsed options: ",
unparsed_options->ShortDebugString(),
"\n"
"Parsing attempt: ",
options->ShortDebugString());
});
// Restore the unparsed options.
options->GetReflection()->Swap(unparsed_options.get(), options);
}
}
return !failed;
}
bool OptionInterpreter::InterpretSingleOption(
Message* options, const SourceCodePath& src_path,
const SourceCodePath& options_path, bool skip_extensions) {
// First do some basic validation.
if (uninterpreted_option_->name_size() == 0) {
// This should never happen unless the parser has gone seriously awry or
// someone has manually created the uninterpreted option badly.
if (skip_extensions) {
// Come back to it later.
return true;
}
return AddNameError(
[]() -> std::string { return "Option must have a name."; });
}
if (uninterpreted_option_->name(0).name_part() == "uninterpreted_option") {
if (skip_extensions) {
// Come back to it later.
return true;
}
return AddNameError([]() -> std::string {
return "Option must not use reserved name \"uninterpreted_option\".";
});
}
if (skip_extensions == uninterpreted_option_->name(0).is_extension()) {
// Allow feature and option interpretation to occur in two phases. This is
// necessary because features *are* options and need to be interpreted
// before resolving them. However, options can also *have* features
// attached to them.
return true;
}
const Descriptor* options_descriptor = nullptr;
// Get the options message's descriptor from the builder's pool, so that we
// get the version that knows about any extension options declared in the file
// we're currently building. The descriptor should be there as long as the
// file we're building imported descriptor.proto.
// Note that we use internal::DescriptorBuilder::FindSymbolNotEnforcingDeps(),
// not DescriptorPool::FindMessageTypeByName() because we're already holding
// the pool's mutex, and the latter method locks it again. We don't use
// FindSymbol() because files that use custom options only need to depend on
// the file that defines the option, not descriptor.proto itself.
Symbol symbol = builder_->FindSymbolNotEnforcingDeps(
options->GetDescriptor()->full_name());
options_descriptor = symbol.descriptor();
if (options_descriptor == nullptr) {
// The options message's descriptor was not in the builder's pool, so use
// the standard version from the generated pool. We're not holding the
// generated pool's mutex, so we can search it the straightforward way.
options_descriptor = options->GetDescriptor();
}
ABSL_CHECK(options_descriptor);
// We iterate over the name parts to drill into the submessages until we find
// the leaf field for the option. As we drill down we remember the current
// submessage's descriptor in |descriptor| and the next field in that
// submessage in |field|. We also track the fields we're drilling down
// through in |intermediate_fields|. As we go, we reconstruct the full option
// name in |debug_msg_name|, for use in error messages.
const Descriptor* descriptor = options_descriptor;
const FieldDescriptor* field = nullptr;
std::vector<const FieldDescriptor*> intermediate_fields;
std::string debug_msg_name = "";
SourceCodePath dest_path;
if (update_source_code_info_) {
dest_path = options_path;
}
for (int i = 0; i < uninterpreted_option_->name_size(); ++i) {
builder_->undefine_resolved_name_.clear();
const std::string& name_part = uninterpreted_option_->name(i).name_part();
if (!debug_msg_name.empty()) {
absl::StrAppend(&debug_msg_name, ".");
}
if (uninterpreted_option_->name(i).is_extension()) {
absl::StrAppend(&debug_msg_name, "(", name_part, ")");
// Search for the extension's descriptor as an extension in the builder's
// pool. Note that we use internal::DescriptorBuilder::LookupSymbol(), not
// DescriptorPool::FindExtensionByName(), for two reasons: 1) It allows
// relative lookups, and 2) because we're already holding the pool's
// mutex, and the latter method locks it again.
symbol =
builder_->LookupSymbol(name_part, options_to_interpret_->name_scope);
field = symbol.field_descriptor();
// If we don't find the field then the field's descriptor was not in the
// builder's pool, but there's no point in looking in the generated
// pool. We require that you import the file that defines any extensions
// you use, so they must be present in the builder's pool.
} else {
absl::StrAppend(&debug_msg_name, name_part);
// Search for the field's descriptor as a regular field.
field = descriptor->FindFieldByName(name_part);
}
if (field == nullptr) {
if (DescriptorBuilder::get_allow_unknown(builder_->pool_)) {
// We can't find the option, but AllowUnknownDependencies() is enabled,
// so we will just leave it as uninterpreted.
AddWithoutInterpreting(*uninterpreted_option_, options);
return true;
} else if (!(builder_->undefine_resolved_name_).empty()) {
// Option is resolved to a name which is not defined.
return AddNameError([&] {
return absl::StrCat(
"Option \"", debug_msg_name, "\" is resolved to \"(",
builder_->undefine_resolved_name_,
")\", which is not defined. The innermost scope is searched "
"first "
"in name resolution. Consider using a leading '.'(i.e., \"(.",
debug_msg_name.substr(1),
"\") to start from the outermost scope.");
});
} else {
return AddNameError([&] {
return absl::StrCat("Option \"", debug_msg_name,
"\" unknown. Ensure that your proto",
" definition file imports the proto which "
"defines the option (i.e. via import option "
"after edition 2024).");
});
}
} else if (field->containing_type() != descriptor) {
if (DescriptorBuilder::get_is_placeholder(field->containing_type())) {
// The field is an extension of a placeholder type, so we can't
// reliably verify whether it is a valid extension to use here (e.g.
// we don't know if it is an extension of the correct *Options message,
// or if it has a valid field number, etc.). Just leave it as
// uninterpreted instead.
AddWithoutInterpreting(*uninterpreted_option_, options);
return true;
} else {
// This can only happen if, due to some insane misconfiguration of the
// pools, we find the options message in one pool but the field in
// another. This would probably imply a hefty bug somewhere.
return AddNameError([&] {
return absl::StrCat("Option field \"", debug_msg_name,
"\" is not a field or extension of message \"",
descriptor->name(), "\".");
});
}
} else {
if (update_source_code_info_) {
// accumulate field numbers to form path to interpreted option
dest_path.push_back(field->number());
}
// Special handling to prevent feature use in the same file as the
// definition.
// TODO Add proper support for cases where this can work.
if (field->file() == builder_->file_ &&
uninterpreted_option_->name(0).name_part() == "features" &&
!uninterpreted_option_->name(0).is_extension()) {
return AddNameError([&] {
return absl::StrCat(
"Feature \"", debug_msg_name,
"\" can't be used in the same file it's defined in.");
});
}
if (i < uninterpreted_option_->name_size() - 1) {
if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
return AddNameError([&] {
return absl::StrCat("Option \"", debug_msg_name,
"\" is an atomic type, not a message.");
});
} else if (field->is_repeated()) {
return AddNameError([&] {
return absl::StrCat("Option field \"", debug_msg_name,
"\" is a repeated message. Repeated message "
"options must be initialized using an "
"aggregate value.");
});
} else {
// Drill down into the submessage.
intermediate_fields.push_back(field);
descriptor = field->message_type();
}
}
}
}
// We've found the leaf field. Now we use UnknownFieldSets to set its value
// on the options message. We do so because the message may not yet know
// about its extension fields, so we may not be able to set the fields
// directly. But the UnknownFieldSets will serialize to the same wire-format
// message, so reading that message back in once the extension fields are
// known will populate them correctly.
// First see if the option is already set.
if (!field->is_repeated() &&
!ExamineIfOptionIsSet(
intermediate_fields.begin(), intermediate_fields.end(), field,
debug_msg_name,
options->GetReflection()->GetUnknownFields(*options))) {
return false; // ExamineIfOptionIsSet() already added the error.
}
if (update_source_code_info_) {
// record the element path of the interpreted option
if (field->is_repeated()) {
int index = repeated_option_counts_[dest_path]++;
dest_path.push_back(index);
}
}
// First set the value on the UnknownFieldSet corresponding to the
// innermost message.
std::unique_ptr<UnknownFieldSet> unknown_fields =
std::make_unique<UnknownFieldSet>();
if (!SetOptionValue(field, unknown_fields.get(), options, src_path,
dest_path)) {
return false; // SetOptionValue() already added the error.
}
// Now wrap the UnknownFieldSet with UnknownFieldSets corresponding to all
// the intermediate messages.
for (std::vector<const FieldDescriptor*>::reverse_iterator iter =
intermediate_fields.rbegin();
iter != intermediate_fields.rend(); ++iter) {
std::unique_ptr<UnknownFieldSet> parent_unknown_fields =
std::make_unique<UnknownFieldSet>();
switch ((*iter)->type()) {
case FieldDescriptor::TYPE_MESSAGE: {
std::string outstr;
ABSL_CHECK(unknown_fields->SerializeToString(&outstr))
<< "Unexpected failure while serializing option submessage "
<< debug_msg_name << "\".";
parent_unknown_fields->AddLengthDelimited((*iter)->number(),
std::move(outstr));
break;
}
case FieldDescriptor::TYPE_GROUP: {
parent_unknown_fields->AddGroup((*iter)->number())
->MergeFrom(*unknown_fields);
break;
}
default:
ABSL_LOG(FATAL) << "Invalid wire type for CPPTYPE_MESSAGE: "
<< (*iter)->type();
return false;
}
unknown_fields = std::move(parent_unknown_fields);
}
// Now merge the UnknownFieldSet corresponding to the top-level message into
// the options message.
options->GetReflection()->MutableUnknownFields(options)->MergeFrom(
*unknown_fields);
if (update_source_code_info_) {
interpreted_paths_[src_path] = dest_path;
}
return true;
}
void OptionInterpreter::UpdateSourceCodeInfo(SourceCodeInfo* info) {
if (!update_source_code_info_ || info == nullptr ||
interpreted_paths_.empty()) {
// nothing to do!
return;
}
// Group all aggregate field locations by their uninterpreted option path
// prefix to enable O(1) lookup when mapping aggregate sub-field locations.
// Key is SourceCodePath.
absl::flat_hash_map<absl::Span<const int>,
std::vector<const AggregateFieldLocation*>>
agg_loc_map;
for (const AggregateFieldLocation& afl : aggregate_field_locations_) {
agg_loc_map[afl.uninterpreted_path].push_back(&afl);
}
// We find locations that match keys in interpreted_paths_ and
// 1) replace the path with the corresponding value in interpreted_paths_
// 2) remove any subsequent sub-locations (sub-location is one whose path
// has the parent path as a prefix), except for direct children (like
// option name and value) which are mapped to the interpreted path.
//
// To avoid quadratic behavior of removing interior rows as we go,
// we keep a copy. But we don't actually copy anything until we've
// found the first match (so if the source code info has no locations
// that need to be changed, there is zero copy overhead).
// The original repeated field of source code locations in the file.
RepeatedPtrField<SourceCodeInfo_Location>* locs = info->mutable_location();
// The new repeated field of source code locations being built to replace
// locs.
RepeatedPtrField<SourceCodeInfo_Location> new_locs;
// Indicates whether we have started copying locations to new_locs. To avoid
// unnecessary copying overhead when no locations need modification, copying
// remains false until the first matching uninterpreted option location is
// found.
bool copying = false;
// The uninterpreted option path (e.g., [options, index]) currently being
// matched and replaced.
SourceCodePath match_src;
// The interpreted option path (e.g., [options, custom_option_tag]) that
// replaces match_src.
SourceCodePath match_dest;
// Indicates whether we are currently traversing child locations of an
// uninterpreted option that was matched in a previous iteration. When true,
// child sub-locations are inspected and either remapped or removed.
bool matched = false;
for (RepeatedPtrField<SourceCodeInfo_Location>::iterator loc = locs->begin();
loc != locs->end(); loc++) {
if (matched) {
// see if this location is in the range to remove
bool loc_matches = true;
if (loc->path_size() < static_cast<int64_t>(match_src.size())) {
loc_matches = false;
} else {
for (size_t j = 0; j < match_src.size(); j++) {
if (loc->path(j) != match_src[j]) {
loc_matches = false;
break;
}
}
}
if (loc_matches) {
// TODO: b/168903973 - Remove once we update the format.
// don't copy this row since it is a sub-location that we're removing
// (or we already mapped it if it's a direct child)
continue;
}
matched = false;
}
SourceCodePath curr_path(loc->path().begin(), loc->path().end());
auto entry = interpreted_paths_.find(curr_path);
if (entry == interpreted_paths_.end()) {
// not a match
if (copying) {
*new_locs.Add() = *loc;
}
continue;
}
matched = true;
match_src = std::move(curr_path);
match_dest = entry->second;
if (!copying) {
// initialize the copy we are building
copying = true;
new_locs.Reserve(locs->size());
// Copy all the locations we've seen so far
new_locs.Add(locs->begin(), loc);
}
// add replacement and update its path
SourceCodeInfo_Location* replacement = new_locs.Add();
*replacement = *loc;
replacement->mutable_path()->Assign(entry->second.begin(),
entry->second.end());
}
// if we made a changed copy, put it in place
if (copying) {
*locs = std::move(new_locs);
}
}
void OptionInterpreter::AddWithoutInterpreting(
const UninterpretedOption& uninterpreted_option, Message* options) {
const FieldDescriptor* field =
options->GetDescriptor()->FindFieldByName("uninterpreted_option");
ABSL_CHECK(field != nullptr);
options->GetReflection()
->AddMessage(options, field)
->CopyFrom(uninterpreted_option);
}
bool OptionInterpreter::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) {
// We do linear searches of the UnknownFieldSet and its sub-groups. This
// should be fine since it's unlikely that any one options structure will
// contain more than a handful of options.
if (intermediate_fields_iter == intermediate_fields_end) {
// We're at the innermost submessage.
for (int i = 0; i < unknown_fields.field_count(); i++) {
if (unknown_fields.field(i).number() == innermost_field->number()) {
return AddNameError([&] {
return absl::StrCat("Option \"", debug_msg_name,
"\" was already set.");
});
}
}
return true;
}
for (int i = 0; i < unknown_fields.field_count(); i++) {
if (unknown_fields.field(i).number() ==
(*intermediate_fields_iter)->number()) {
const UnknownField* unknown_field = &unknown_fields.field(i);
FieldDescriptor::Type type = (*intermediate_fields_iter)->type();
// Recurse into the next submessage.
switch (type) {
case FieldDescriptor::TYPE_MESSAGE:
if (unknown_field->type() == UnknownField::TYPE_LENGTH_DELIMITED) {
UnknownFieldSet intermediate_unknown_fields;
if (intermediate_unknown_fields.ParseFromString(
unknown_field->length_delimited()) &&
!ExamineIfOptionIsSet(intermediate_fields_iter + 1,
intermediate_fields_end, innermost_field,
debug_msg_name,
intermediate_unknown_fields)) {
return false; // Error already added.
}
}
break;
case FieldDescriptor::TYPE_GROUP:
if (unknown_field->type() == UnknownField::TYPE_GROUP) {
if (!ExamineIfOptionIsSet(intermediate_fields_iter + 1,
intermediate_fields_end, innermost_field,
debug_msg_name, unknown_field->group())) {
return false; // Error already added.
}
}
break;
default:
ABSL_LOG(FATAL) << "Invalid wire type for CPPTYPE_MESSAGE: " << type;
return false;
}
}
}
return true;
}
namespace {
// Helpers for method below
template <typename T>
std::string ValueOutOfRange(absl::string_view type_name,
absl::string_view option_name) {
return absl::StrFormat("Value out of range, %d to %d, for %s option \"%s\".",
std::numeric_limits<T>::min(),
std::numeric_limits<T>::max(), type_name, option_name);
}
template <typename T>
std::string ValueMustBeInt(absl::string_view type_name,
absl::string_view option_name) {
return absl::StrFormat(
"Value must be integer, from %d to %d, for %s option \"%s\".",
std::numeric_limits<T>::min(), std::numeric_limits<T>::max(), type_name,
option_name);
}
} // namespace
bool OptionInterpreter::SetOptionValue(const FieldDescriptor* option_field,
UnknownFieldSet* unknown_fields,
Message* options,
const SourceCodePath& src_path,
const SourceCodePath& dest_path) {
// We switch on the CppType to validate.
switch (option_field->cpp_type()) {
case FieldDescriptor::CPPTYPE_INT32:
if (uninterpreted_option_->has_positive_int_value()) {
if (uninterpreted_option_->positive_int_value() >
static_cast<uint64_t>(std::numeric_limits<int32_t>::max())) {
return AddValueError([&] {
return ValueOutOfRange<int32_t>("int32", option_field->full_name());
});
} else {
SetInt32(option_field->number(),
uninterpreted_option_->positive_int_value(),
option_field->type(), unknown_fields);
}
} else if (uninterpreted_option_->has_negative_int_value()) {
if (uninterpreted_option_->negative_int_value() <
static_cast<int64_t>(std::numeric_limits<int32_t>::min())) {
return AddValueError([&] {
return ValueOutOfRange<int32_t>("int32", option_field->full_name());
});
} else {
SetInt32(option_field->number(),
uninterpreted_option_->negative_int_value(),
option_field->type(), unknown_fields);
}
} else {
return AddValueError([&] {
return ValueMustBeInt<int32_t>("int32", option_field->full_name());
});
}
break;
case FieldDescriptor::CPPTYPE_INT64:
if (uninterpreted_option_->has_positive_int_value()) {
if (uninterpreted_option_->positive_int_value() >
static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
return AddValueError([&] {
return ValueOutOfRange<int64_t>("int64", option_field->full_name());
});
} else {
SetInt64(option_field->number(),
uninterpreted_option_->positive_int_value(),
option_field->type(), unknown_fields);
}
} else if (uninterpreted_option_->has_negative_int_value()) {
SetInt64(option_field->number(),
uninterpreted_option_->negative_int_value(),
option_field->type(), unknown_fields);
} else {
return AddValueError([&] {
return ValueMustBeInt<int64_t>("int64", option_field->full_name());
});
}
break;
case FieldDescriptor::CPPTYPE_UINT32:
if (uninterpreted_option_->has_positive_int_value()) {
if (uninterpreted_option_->positive_int_value() >
std::numeric_limits<uint32_t>::max()) {
return AddValueError([&] {
return ValueOutOfRange<uint32_t>("uint32",
option_field->full_name());
});
} else {
SetUInt32(option_field->number(),
uninterpreted_option_->positive_int_value(),
option_field->type(), unknown_fields);
}
} else {
return AddValueError([&] {
return ValueMustBeInt<uint32_t>("uint32", option_field->full_name());
});
}
break;
case FieldDescriptor::CPPTYPE_UINT64:
if (uninterpreted_option_->has_positive_int_value()) {
SetUInt64(option_field->number(),
uninterpreted_option_->positive_int_value(),
option_field->type(), unknown_fields);
} else {
return AddValueError([&] {
return ValueMustBeInt<uint64_t>("uint64", option_field->full_name());
});
}
break;
case FieldDescriptor::CPPTYPE_FLOAT: {
float value;
if (uninterpreted_option_->has_double_value()) {
value = uninterpreted_option_->double_value();
} else if (uninterpreted_option_->has_positive_int_value()) {
value = uninterpreted_option_->positive_int_value();
} else if (uninterpreted_option_->has_negative_int_value()) {
value = uninterpreted_option_->negative_int_value();
} else if (uninterpreted_option_->identifier_value() == "inf") {
value = std::numeric_limits<float>::infinity();
} else if (uninterpreted_option_->identifier_value() == "nan") {
value = std::numeric_limits<float>::quiet_NaN();
} else {
return AddValueError([&] {
return absl::StrCat("Value must be number for float option \"",
option_field->full_name(), "\".");
});
}
unknown_fields->AddFixed32(option_field->number(),
internal::WireFormatLite::EncodeFloat(value));
break;
}
case FieldDescriptor::CPPTYPE_DOUBLE: {
double value;
if (uninterpreted_option_->has_double_value()) {
value = uninterpreted_option_->double_value();
} else if (uninterpreted_option_->has_positive_int_value()) {
value = uninterpreted_option_->positive_int_value();
} else if (uninterpreted_option_->has_negative_int_value()) {
value = uninterpreted_option_->negative_int_value();
} else if (uninterpreted_option_->identifier_value() == "inf") {
value = std::numeric_limits<double>::infinity();
} else if (uninterpreted_option_->identifier_value() == "nan") {
value = std::numeric_limits<double>::quiet_NaN();
} else {
return AddValueError([&] {
return absl::StrCat("Value must be number for double option \"",
option_field->full_name(), "\".");
});
}
unknown_fields->AddFixed64(option_field->number(),
internal::WireFormatLite::EncodeDouble(value));
break;
}
case FieldDescriptor::CPPTYPE_BOOL:
uint64_t value;
if (!uninterpreted_option_->has_identifier_value()) {
return AddValueError([&] {
return absl::StrCat("Value must be identifier for boolean option \"",
option_field->full_name(), "\".");
});
}
if (uninterpreted_option_->identifier_value() == "true") {
value = 1;
} else if (uninterpreted_option_->identifier_value() == "false") {
value = 0;
} else {
return AddValueError([&] {
return absl::StrCat(
"Value must be \"true\" or \"false\" for boolean option \"",
option_field->full_name(), "\".");
});
}
unknown_fields->AddVarint(option_field->number(), value);
break;
case FieldDescriptor::CPPTYPE_ENUM: {
if (!uninterpreted_option_->has_identifier_value()) {
return AddValueError([&] {
return absl::StrCat(
"Value must be identifier for enum-valued option \"",
option_field->full_name(), "\".");
});
}
const EnumDescriptor* enum_type = option_field->enum_type();
const std::string& value_name = uninterpreted_option_->identifier_value();
const EnumValueDescriptor* enum_value = nullptr;
if (enum_type->file()->pool() != DescriptorPool::generated_pool()) {
// Note that the enum value's fully-qualified name is a sibling of the
// enum's name, not a child of it.
std::string fully_qualified_name = std::string(enum_type->full_name());
fully_qualified_name.resize(fully_qualified_name.size() -
enum_type->name().size());
fully_qualified_name += value_name;
// Search for the enum value's descriptor in the builder's pool. Note
// that we use
// internal::DescriptorBuilder::FindSymbolNotEnforcingDeps(), not
// DescriptorPool::FindEnumValueByName() because we're already holding
// the pool's mutex, and the latter method locks it again.
Symbol symbol =
builder_->FindSymbolNotEnforcingDeps(fully_qualified_name);
if (auto* candidate_descriptor = symbol.enum_value_descriptor()) {
if (candidate_descriptor->type() != enum_type) {
return AddValueError([&] {
return absl::StrCat(
"Enum type \"", enum_type->full_name(),
"\" has no value named \"", value_name, "\" for option \"",
option_field->full_name(),
"\". This appears to be a value from a sibling type.");
});
} else {
enum_value = candidate_descriptor;
}
}
} else {
// The enum type is in the generated pool, so we can search for the
// value there.
enum_value = enum_type->FindValueByName(value_name);
}
if (enum_value == nullptr) {
return AddValueError([&] {
return absl::StrCat(
"Enum type \"", option_field->enum_type()->full_name(),
"\" has no value named \"", value_name, "\" for option \"",
option_field->full_name(), "\".");
});
} else {
// Sign-extension is not a problem, since we cast directly from int32_t
// to uint64_t, without first going through uint32_t.
unknown_fields->AddVarint(
option_field->number(),
static_cast<uint64_t>(static_cast<int64_t>(enum_value->number())));
}
break;
}
case FieldDescriptor::CPPTYPE_STRING:
if (!uninterpreted_option_->has_string_value()) {
return AddValueError([&] {
return absl::StrCat(
"Value must be quoted string for string option \"",
option_field->full_name(), "\".");
});
}
// The string has already been unquoted and unescaped by the parser.
unknown_fields->AddLengthDelimited(option_field->number(),
uninterpreted_option_->string_value());
break;
case FieldDescriptor::CPPTYPE_MESSAGE:
if (!SetAggregateOption(option_field, unknown_fields, options, src_path,
dest_path)) {
return false;
}
}
return true;
}
class AggregateOptionFinder : public TextFormat::Finder {
public:
DescriptorBuilder* builder_;
const Descriptor* FindAnyType(const Message& /*message*/,
const std::string& prefix,
const std::string& name) const override {
if (prefix != internal::kTypeGoogleApisComPrefix &&
prefix != internal::kTypeGoogleProdComPrefix) {
return nullptr;
}
DescriptorBuilder::assert_mutex_held(builder_->pool_);
return builder_->FindSymbol(name).descriptor();
}
const FieldDescriptor* FindExtension(Message* message,
const std::string& name) const override {
DescriptorBuilder::assert_mutex_held(builder_->pool_);
const Descriptor* descriptor = message->GetDescriptor();
Symbol result =
builder_->LookupSymbolNoPlaceholder(name, descriptor->full_name());
if (auto* field = result.field_descriptor()) {
return field;
} else if (result.type() == Symbol::MESSAGE &&
descriptor->options().message_set_wire_format()) {
const Descriptor* foreign_type = result.descriptor();
// The text format allows MessageSet items to be specified using
// the type name, rather than the extension identifier. If the symbol
// lookup returned a Message, and the enclosing Message has
// message_set_wire_format = true, then return the message set
// extension, if one exists.
for (int i = 0; i < foreign_type->extension_count(); i++) {
const FieldDescriptor* extension = foreign_type->extension(i);
if (extension->containing_type() == descriptor &&
extension->type() == FieldDescriptor::TYPE_MESSAGE &&
extension->label_ == FieldDescriptor::LABEL_OPTIONAL &&
extension->message_type() == foreign_type) {
// Found it.
return extension;
}
}
}
return nullptr;
}
};
// A custom error collector to record any text-format parsing errors
namespace {
class AggregateErrorCollector : public io::ErrorCollector {
public:
std::string error_;
void RecordError(int /* line */, int /* column */,
const absl::string_view message) override {
if (!error_.empty()) {
absl::StrAppend(&error_, "; ");
}
absl::StrAppend(&error_, message);
}
void RecordWarning(int /* line */, int /* column */,
const absl::string_view /* message */) override {
// Ignore warnings
}
};
} // namespace
// We construct a dynamic message of the type corresponding to
// option_field, parse the supplied text-format string into this
// message, and serialize the resulting message to produce the value.
bool OptionInterpreter::SetAggregateOption(const FieldDescriptor* option_field,
UnknownFieldSet* unknown_fields,
Message* options,
const SourceCodePath& src_path,
const SourceCodePath& dest_path) {
if (!uninterpreted_option_->has_aggregate_value()) {
return AddValueError([&] {
return absl::StrCat("Option \"", option_field->full_name(),
"\" is a message. "
"To set the entire message, use syntax like \"",
option_field->name(),
" = { <proto text format> }\". "
"To set fields within it, use syntax like \"",
option_field->name(), ".foo = value\".");
});
}
const Descriptor* type = option_field->message_type();
std::unique_ptr<Message> dynamic(dynamic_factory_.GetPrototype(type)->New());
ABSL_CHECK(dynamic.get() != nullptr)
<< "Could not create an instance of " << option_field->DebugString();
AggregateErrorCollector collector;
AggregateOptionFinder finder;
finder.builder_ = builder_;
TextFormat::Parser parser;
parser.RecordErrorsTo(&collector);
parser.SetFinder(&finder);
TextFormat::ParseInfoTree info_tree;
if (update_source_code_info_) {
parser.WriteLocationsTo(&info_tree);
}
if (!parser.ParseFromString(uninterpreted_option_->aggregate_value(),
dynamic.get())) {
if (DescriptorBuilder::get_allow_unknown(builder_->pool_)) {
// We can't interpret the option, but AllowUnknownDependencies() is
// enabled, so we will just leave it as uninterpreted.
AddWithoutInterpreting(*uninterpreted_option_, options);
return true;
} else {
AddValueError([&] {
return absl::StrCat("Error while parsing option value for \"",
option_field->name(), "\": ", collector.error_);
});
return false;
}
} else {
if (update_source_code_info_) {
SourceCodePath mutable_src_path = src_path;
mutable_src_path.push_back(
UninterpretedOption::kAggregateValueFieldNumber);
SourceCodePath mutable_dest_path = dest_path;
mutable_dest_path.push_back(
UninterpretedOption::kAggregateValueFieldNumber);
CollectAggregateFieldLocations(*dynamic, info_tree, mutable_src_path,
mutable_dest_path);
}
std::string serial;
ABSL_CHECK(dynamic->SerializeToString(&serial)); // Never fails
if (option_field->type() == FieldDescriptor::TYPE_MESSAGE) {
unknown_fields->AddLengthDelimited(option_field->number(), serial);
} else {
ABSL_CHECK_EQ(option_field->type(), FieldDescriptor::TYPE_GROUP);
UnknownFieldSet* group = unknown_fields->AddGroup(option_field->number());
// TODO: Remove this suppression.
(void)group->ParseFromString(serial);
}
return true;
}
}
void OptionInterpreter::CollectAggregateFieldLocations(
const Message& message, const TextFormat::ParseInfoTree& tree,
const SourceCodePath& uninterpreted_path, SourceCodePath& dest_path) {
const Reflection* reflection = message.GetReflection();
std::vector<const FieldDescriptor*> fields;
reflection->ListFields(message, &fields);
for (const FieldDescriptor* field : fields) {
auto record_field_location = [&](const TextFormat::FieldLocation& loc,
size_t v_idx, int curr_idx) {
int val_marker_idx = field->is_repeated() ? curr_idx : -1;
dest_path.push_back(field->number());
if (field->is_repeated()) {
dest_path.push_back(curr_idx);
}
AggregateFieldLocation afl;
afl.uninterpreted_path = uninterpreted_path;
afl.field_dest_path = dest_path;
afl.value_marker = GetValueMarker(field, message, val_marker_idx);
afl.name_range = loc.name;
afl.val_range = loc.values[v_idx];
aggregate_field_locations_.push_back(std::move(afl));
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
const Message& sub_message =
field->is_repeated()
? reflection->GetRepeatedMessage(message, field, curr_idx)
: reflection->GetMessage(message, field);
const TextFormat::ParseInfoTree* sub_tree =
tree.GetTreeForNested(field, val_marker_idx);
if (sub_tree != nullptr) {
dest_path.push_back(UninterpretedOption::kAggregateValueFieldNumber);
CollectAggregateFieldLocations(sub_message, *sub_tree,
uninterpreted_path, dest_path);
dest_path.pop_back();
}
}
if (field->is_repeated()) {
dest_path.pop_back();
}
dest_path.pop_back();
};
if (field->is_repeated()) {
int curr_idx = 0;
// Text format allows two different syntaxes for repeated fields so
// we need to handle potential cases like this:
//
// a: [1, 2]
// a: 3
//
// Here the outer loop for field_occurrence will run for each occureence
// of `a`. While the inner loop will run for each value in the repeated
// field.
for (size_t field_occurrence = 0;; ++field_occurrence) {
absl::StatusOr<TextFormat::FieldLocation> location =
tree.GetFieldLocation(field, field_occurrence);
if (!location.ok()) break;
for (size_t v_idx = 0; v_idx < location->values.size(); ++v_idx) {
record_field_location(*location, v_idx, curr_idx);
++curr_idx;
}
}
} else {
absl::StatusOr<TextFormat::FieldLocation> location =
tree.GetFieldLocation(field);
if (location.ok() && !location->values.empty()) {
record_field_location(*location, 0, -1);
} else {
ABSL_LOG(WARNING) << "No location in textformat found for field: "
<< field->name();
}
}
}
}
int OptionInterpreter::GetValueMarker(const FieldDescriptor* field,
const Message& message, int index) {
const Reflection* reflection = message.GetReflection();
switch (field->cpp_type()) {
case FieldDescriptor::CPPTYPE_INT32: {
int32_t val = field->is_repeated()
? reflection->GetRepeatedInt32(message, field, index)
: reflection->GetInt32(message, field);
return val >= 0 ? UninterpretedOption::kPositiveIntValueFieldNumber
: UninterpretedOption::kNegativeIntValueFieldNumber;
}
case FieldDescriptor::CPPTYPE_INT64: {
int64_t val = field->is_repeated()
? reflection->GetRepeatedInt64(message, field, index)
: reflection->GetInt64(message, field);
return val >= 0 ? UninterpretedOption::kPositiveIntValueFieldNumber
: UninterpretedOption::kNegativeIntValueFieldNumber;
}
case FieldDescriptor::CPPTYPE_UINT32:
case FieldDescriptor::CPPTYPE_UINT64:
return UninterpretedOption::kPositiveIntValueFieldNumber;
case FieldDescriptor::CPPTYPE_BOOL:
case FieldDescriptor::CPPTYPE_ENUM:
return UninterpretedOption::kIdentifierValueFieldNumber;
case FieldDescriptor::CPPTYPE_FLOAT:
case FieldDescriptor::CPPTYPE_DOUBLE:
return UninterpretedOption::kDoubleValueFieldNumber;
case FieldDescriptor::CPPTYPE_STRING:
return UninterpretedOption::kStringValueFieldNumber;
case FieldDescriptor::CPPTYPE_MESSAGE:
return UninterpretedOption::kAggregateValueFieldNumber;
default:
return 0;
}
}
void OptionInterpreter::SetSpan(SourceCodeInfo_Location* loc,
const SourceCodeInfo_Location& base_loc,
const TextFormat::ParseLocationRange& range) {
int base_line = base_loc.span(0);
int base_col = base_loc.span(1);
int start_line = base_line + range.start.line;
int start_col = (range.start.line == 0) ? (base_col + 1 + range.start.column)
: range.start.column;
int end_line = base_line + range.end.line;
int end_col = (range.end.line == 0) ? (base_col + 1 + range.end.column)
: range.end.column;
loc->add_span(start_line);
loc->add_span(start_col);
loc->add_span(end_line);
loc->add_span(end_col);
}
void OptionInterpreter::SetInt32(int number, int32_t value,
FieldDescriptor::Type type,
UnknownFieldSet* unknown_fields) {
switch (type) {
case FieldDescriptor::TYPE_INT32:
unknown_fields->AddVarint(
number, static_cast<uint64_t>(static_cast<int64_t>(value)));
break;
case FieldDescriptor::TYPE_SFIXED32:
unknown_fields->AddFixed32(number, static_cast<uint32_t>(value));
break;
case FieldDescriptor::TYPE_SINT32:
unknown_fields->AddVarint(
number, internal::WireFormatLite::ZigZagEncode32(value));
break;
default:
ABSL_LOG(FATAL) << "Invalid wire type for CPPTYPE_INT32: " << type;
break;
}
}
void OptionInterpreter::SetInt64(int number, int64_t value,
FieldDescriptor::Type type,
UnknownFieldSet* unknown_fields) {
switch (type) {
case FieldDescriptor::TYPE_INT64:
unknown_fields->AddVarint(number, static_cast<uint64_t>(value));
break;
case FieldDescriptor::TYPE_SFIXED64:
unknown_fields->AddFixed64(number, static_cast<uint64_t>(value));
break;
case FieldDescriptor::TYPE_SINT64:
unknown_fields->AddVarint(
number, internal::WireFormatLite::ZigZagEncode64(value));
break;
default:
ABSL_LOG(FATAL) << "Invalid wire type for CPPTYPE_INT64: " << type;
break;
}
}
void OptionInterpreter::SetUInt32(int number, uint32_t value,
FieldDescriptor::Type type,
UnknownFieldSet* unknown_fields) {
switch (type) {
case FieldDescriptor::TYPE_UINT32:
unknown_fields->AddVarint(number, static_cast<uint64_t>(value));
break;
case FieldDescriptor::TYPE_FIXED32:
unknown_fields->AddFixed32(number, static_cast<uint32_t>(value));
break;
default:
ABSL_LOG(FATAL) << "Invalid wire type for CPPTYPE_UINT32: " << type;
break;
}
}
void OptionInterpreter::SetUInt64(int number, uint64_t value,
FieldDescriptor::Type type,
UnknownFieldSet* unknown_fields) {
switch (type) {
case FieldDescriptor::TYPE_UINT64:
unknown_fields->AddVarint(number, value);
break;
case FieldDescriptor::TYPE_FIXED64:
unknown_fields->AddFixed64(number, value);
break;
default:
ABSL_LOG(FATAL) << "Invalid wire type for CPPTYPE_UINT64: " << type;
break;
}
}
} // namespace internal
} // namespace protobuf
} // namespace google
#include "google/protobuf/port_undef.inc"