| // 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" |