// Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // https://developers.google.com/protocol-buffers/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef GOOGLE_PROTOBUF_JSON_INTERNAL_DESCRIPTOR_TRAITS_H__ #define GOOGLE_PROTOBUF_JSON_INTERNAL_DESCRIPTOR_TRAITS_H__ #include #include #include #include #include #include #include #include #include "google/protobuf/type.pb.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/dynamic_message.h" #include "google/protobuf/message.h" #include "absl/algorithm/container.h" #include "absl/log/absl_log.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/match.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "google/protobuf/json/internal/lexer.h" #include "google/protobuf/json/internal/untyped_message.h" #include "google/protobuf/stubs/status_macros.h" // Must be included last. #include "google/protobuf/port_def.inc" // Traits for working with descriptor.proto and type.proto generically. namespace google { namespace protobuf { namespace json_internal { enum class MessageType { kNotWellKnown, kAny, kWrapper, kStruct, kList, kValue, kNull, kTimestamp, kDuration, kFieldMask, }; inline MessageType ClassifyMessage(absl::string_view name) { constexpr absl::string_view kWellKnownPkg = "google.protobuf."; if (!absl::StartsWith(name, kWellKnownPkg)) { return MessageType::kNotWellKnown; } name = name.substr(kWellKnownPkg.size()); switch (name.size()) { case 3: if (name == "Any") { return MessageType::kAny; } break; case 5: if (name == "Value") { return MessageType::kValue; } break; case 6: if (name == "Struct") { return MessageType::kStruct; } break; case 8: if (name == "Duration") { return MessageType::kDuration; } break; case 9: if (name == "BoolValue") { return MessageType::kWrapper; } if (name == "NullValue") { return MessageType::kNull; } if (name == "ListValue") { return MessageType::kList; } if (name == "Timestamp") { return MessageType::kTimestamp; } if (name == "FieldMask") { return MessageType::kFieldMask; } break; case 10: if (name == "BytesValue" || name == "FloatValue" || name == "Int32Value" || name == "Int64Value") { return MessageType::kWrapper; } break; case 11: if (name == "DoubleValue" || name == "StringValue" || name == "UInt32Value" || name == "UInt64Value") { return MessageType::kWrapper; } break; default: break; } return MessageType::kNotWellKnown; } // Helper alias templates to avoid needing to write `typename` in function // signatures. template using Field = typename Traits::Field; template using Desc = typename Traits::Desc; // Traits for proto2-ish descriptors. struct Proto2Descriptor { // A descriptor for introspecting the fields of a message type. // // Desc needs to be handled through a const Desc& in most (but not // all, in the case of ResolverTraits) cases, so we do not include the const* // annotation on this type. using Desc = Descriptor; // A field descriptor for introspecting a single field. // // Field is always copyable, so this can be a pointer directly. using Field = const FieldDescriptor*; /// Functions for working with descriptors. /// static absl::string_view TypeName(const Desc& d) { return d.full_name(); } static absl::optional FieldByNumber(const Desc& d, int32_t number) { if (const auto* field = d.FindFieldByNumber(number)) { return field; } return absl::nullopt; } static Field MustHaveField(const Desc& d, int32_t number, JsonLocation::SourceLocation loc = JsonLocation::SourceLocation::current()) { auto f = FieldByNumber(d, number); if (!f.has_value()) { ABSL_LOG(FATAL) << absl::StrFormat( "%s has, by definition, a field numbered %d, but it could not " "be " "looked up; this is a bug", TypeName(d), number); } return *f; } static absl::optional FieldByName(const Desc& d, absl::string_view name) { if (const auto* field = d.FindFieldByCamelcaseName(name)) { return field; } if (const auto* field = d.FindFieldByName(name)) { return field; } for (int i = 0; i < d.field_count(); ++i) { const auto* field = d.field(i); if (field->has_json_name() && field->json_name() == name) { return field; } } return absl::nullopt; } static Field KeyField(const Desc& d) { return d.map_key(); } static Field ValueField(const Desc& d) { return d.map_value(); } static size_t FieldCount(const Desc& d) { return d.field_count(); } static Field FieldByIndex(const Desc& d, size_t idx) { return d.field(idx); } static absl::optional ExtensionByName(const Desc& d, absl::string_view name) { auto* field = d.file()->pool()->FindExtensionByName(name); if (field == nullptr) { return absl::nullopt; } return field; } /// Functions for introspecting fields. /// static absl::string_view FieldName(Field f) { return f->name(); } static absl::string_view FieldJsonName(Field f) { return f->has_json_name() ? f->json_name() : f->camelcase_name(); } static absl::string_view FieldFullName(Field f) { return f->full_name(); } static absl::string_view FieldTypeName(Field f) { if (f->type() == FieldDescriptor::TYPE_MESSAGE) { return f->message_type()->full_name(); } if (f->type() == FieldDescriptor::TYPE_ENUM) { return f->enum_type()->full_name(); } return ""; } static FieldDescriptor::Type FieldType(Field f) { return f->type(); } static int32_t FieldNumber(Field f) { return f->number(); } static bool Is32Bit(Field f) { switch (f->cpp_type()) { case FieldDescriptor::CPPTYPE_UINT32: case FieldDescriptor::CPPTYPE_INT32: case FieldDescriptor::CPPTYPE_ENUM: case FieldDescriptor::CPPTYPE_FLOAT: return true; default: return false; } } static const Desc& ContainingType(Field f) { return *f->containing_type(); } static bool IsMap(Field f) { return f->is_map(); } static bool IsRepeated(Field f) { return f->is_repeated(); } static bool IsOptional(Field f) { return f->has_presence(); } static bool IsImplicitPresence(Field f) { return !f->is_repeated() && !f->has_presence(); } static bool IsExtension(Field f) { return f->is_extension(); } static bool IsOneof(Field f) { return f->containing_oneof() != nullptr; } static absl::StatusOr EnumNumberByName(Field f, absl::string_view name, bool case_insensitive) { if (case_insensitive) { for (int i = 0; i < f->enum_type()->value_count(); ++i) { const auto* ev = f->enum_type()->value(i); if (absl::EqualsIgnoreCase(name, ev->name())) { return ev->number(); } } return absl::InvalidArgumentError( absl::StrFormat("unknown enum value: '%s'", name)); } if (const auto* ev = f->enum_type()->FindValueByName(name)) { return ev->number(); } return absl::InvalidArgumentError( absl::StrFormat("unknown enum value: '%s'", name)); } static absl::StatusOr EnumNameByNumber(Field f, int32_t number) { if (const auto* ev = f->enum_type()->FindValueByNumber(number)) { return ev->name(); } return absl::InvalidArgumentError( absl::StrFormat("unknown enum number: '%d'", number)); } // Looks up the corresponding Desc for `f`'s type, if there is one, and // calls `body` with it. // // This needs to have this funny callback API since whether or not the // Descriptor equivalent is an owning type depends on the trait. template static absl::Status WithFieldType(Field f, F body) { return body(*f->message_type()); } // Like WithFieldType, but using dynamic lookup by type URL. template static absl::Status WithDynamicType(const Desc& desc, const std::string& type_url, F body) { size_t slash = type_url.rfind('/'); if (slash == absl::string_view::npos || slash == 0) { return absl::InvalidArgumentError(absl::StrCat( "@type must contain at least one / and a nonempty host; got: ", type_url)); } absl::string_view type_name(type_url); type_name = type_name.substr(slash + 1); const Descriptor* dyn_desc = desc.file()->pool()->FindMessageTypeByName(type_name); if (dyn_desc == nullptr) { return absl::InvalidArgumentError( absl::StrFormat("could not find @type '%s'", type_url)); } return body(*dyn_desc); } }; // Traits for proto3-ish deserialization. // // See Proto2Descriptor for API docs. struct Proto3Type { using Desc = ResolverPool::Message; using Field = const ResolverPool::Field*; /// Functions for working with descriptors. /// static absl::string_view TypeName(const Desc& d) { return d.proto().name(); } static absl::optional FieldByNumber(const Desc& d, int32_t number) { const auto* f = d.FindField(number); return f == nullptr ? absl::nullopt : absl::make_optional(f); } static Field MustHaveField(const Desc& d, int32_t number, JsonLocation::SourceLocation loc = JsonLocation::SourceLocation::current()) { auto f = FieldByNumber(d, number); if (!f.has_value()) { ABSL_LOG(FATAL) << absl::StrFormat( "%s has, by definition, a field numbered %d, but it could not " "be " "looked up; this is a bug", TypeName(d), number); } return *f; } static absl::optional FieldByName(const Desc& d, absl::string_view name) { const auto* f = d.FindField(name); return f == nullptr ? absl::nullopt : absl::make_optional(f); } static Field KeyField(const Desc& d) { return &d.FieldsByIndex()[0]; } static Field ValueField(const Desc& d) { return &d.FieldsByIndex()[1]; } static size_t FieldCount(const Desc& d) { return d.proto().fields_size(); } static Field FieldByIndex(const Desc& d, size_t idx) { return &d.FieldsByIndex()[idx]; } static absl::optional ExtensionByName(const Desc& d, absl::string_view name) { // type.proto cannot represent extensions, so this function always // fails. return absl::nullopt; } /// Functions for introspecting fields. /// static absl::string_view FieldName(Field f) { return f->proto().name(); } static absl::string_view FieldJsonName(Field f) { return f->proto().json_name(); } static absl::string_view FieldFullName(Field f) { return f->proto().name(); } static absl::string_view FieldTypeName(Field f) { absl::string_view url = f->proto().type_url(); // If there is no slash, `slash` is string_view::npos, which is guaranteed // to be -1. size_t slash = url.rfind('/'); return url.substr(slash + 1); } static FieldDescriptor::Type FieldType(Field f) { // The descriptor.proto and type.proto field type enums are required to be // the same, so we leverage this. return static_cast(f->proto().kind()); } static int32_t FieldNumber(Field f) { return f->proto().number(); } static bool Is32Bit(Field f) { switch (f->proto().kind()) { case google::protobuf::Field::TYPE_INT32: case google::protobuf::Field::TYPE_SINT32: case google::protobuf::Field::TYPE_UINT32: case google::protobuf::Field::TYPE_FIXED32: case google::protobuf::Field::TYPE_SFIXED32: case google::protobuf::Field::TYPE_FLOAT: return true; default: return false; } } static const Desc& ContainingType(Field f) { return f->parent(); } static bool IsMap(Field f) { if (f->proto().kind() != google::protobuf::Field::TYPE_MESSAGE) { return false; } bool value = false; (void)WithFieldType(f, [&value](const Desc& desc) { value = absl::c_any_of(desc.proto().options(), [&](auto& option) { return option.name() == "map_entry"; }); return absl::OkStatus(); }); return value; } static bool IsRepeated(Field f) { return f->proto().cardinality() == google::protobuf::Field::CARDINALITY_REPEATED; } static bool IsOptional(Field f) { // Implicit presence requires this weird check: in proto3, everything is // implicit presence, except for things that are members of oneofs, // which is how proto3 optional is represented. if (f->parent().proto().syntax() == google::protobuf::SYNTAX_PROTO3) { return f->proto().oneof_index() != 0; } return f->proto().cardinality() == google::protobuf::Field::CARDINALITY_OPTIONAL || google::protobuf::Field::CARDINALITY_REQUIRED; } static bool IsImplicitPresence(Field f) { return !IsRepeated(f) && !IsOptional(f); } static bool IsExtension(Field f) { return false; } static bool IsOneof(Field f) { return f->proto().oneof_index() != 0; } static absl::StatusOr EnumNumberByName(Field f, absl::string_view name, bool case_insensitive) { auto e = f->EnumType(); RETURN_IF_ERROR(e.status()); for (const auto& ev : (**e).proto().enumvalue()) { if (case_insensitive) { // Two ifs to avoid doing operator== twice if the names are not equal. if (absl::EqualsIgnoreCase(ev.name(), name)) { return ev.number(); } } else if (ev.name() == name) { return ev.number(); } } return absl::InvalidArgumentError( absl::StrFormat("unknown enum value: '%s'", name)); } static absl::StatusOr EnumNameByNumber(Field f, int32_t number) { auto e = f->EnumType(); RETURN_IF_ERROR(e.status()); for (const auto& ev : (**e).proto().enumvalue()) { if (ev.number() == number) { return ev.name(); } } return absl::InvalidArgumentError( absl::StrFormat("unknown enum number: '%d'", number)); } template static absl::Status WithFieldType(Field f, F body) { auto m = f->MessageType(); RETURN_IF_ERROR(m.status()); return body(**m); } template static absl::Status WithDynamicType(const Desc& desc, const std::string& type_url, F body) { auto dyn_desc = desc.pool()->FindMessage(type_url); RETURN_IF_ERROR(dyn_desc.status()); return body(**dyn_desc); } }; } // namespace json_internal } // namespace protobuf } // namespace google #include "google/protobuf/port_undef.inc" #endif // GOOGLE_PROTOBUF_JSON_INTERNAL_DESCRIPTOR_TRAITS_INTERNAL_H__