• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 #ifndef GOOGLE_PROTOBUF_JSON_INTERNAL_DESCRIPTOR_TRAITS_H__
9 #define GOOGLE_PROTOBUF_JSON_INTERNAL_DESCRIPTOR_TRAITS_H__
10 
11 #include <array>
12 #include <cfloat>
13 #include <cmath>
14 #include <cstdint>
15 #include <cstring>
16 #include <limits>
17 #include <string>
18 #include <utility>
19 
20 #include "google/protobuf/type.pb.h"
21 #include "absl/algorithm/container.h"
22 #include "absl/log/absl_log.h"
23 #include "absl/status/status.h"
24 #include "absl/status/statusor.h"
25 #include "absl/strings/match.h"
26 #include "absl/strings/str_format.h"
27 #include "absl/strings/string_view.h"
28 #include "absl/types/optional.h"
29 #include "google/protobuf/descriptor.h"
30 #include "google/protobuf/dynamic_message.h"
31 #include "google/protobuf/json/internal/lexer.h"
32 #include "google/protobuf/json/internal/untyped_message.h"
33 #include "google/protobuf/message.h"
34 #include "google/protobuf/stubs/status_macros.h"
35 
36 
37 // Must be included last.
38 #include "google/protobuf/port_def.inc"
39 
40 // Traits for working with descriptor.proto and type.proto generically.
41 
42 namespace google {
43 namespace protobuf {
44 namespace json_internal {
45 enum class MessageType {
46   kNotWellKnown,
47   kAny,
48   kWrapper,
49   kStruct,
50   kList,
51   kValue,
52   kNull,
53   kTimestamp,
54   kDuration,
55   kFieldMask,
56 };
57 
ClassifyMessage(absl::string_view name)58 inline MessageType ClassifyMessage(absl::string_view name) {
59   constexpr absl::string_view kWellKnownPkg = "google.protobuf.";
60   if (!absl::StartsWith(name, kWellKnownPkg)) {
61     return MessageType::kNotWellKnown;
62   }
63   name = name.substr(kWellKnownPkg.size());
64 
65   switch (name.size()) {
66     case 3:
67       if (name == "Any") {
68         return MessageType::kAny;
69       }
70       break;
71     case 5:
72       if (name == "Value") {
73         return MessageType::kValue;
74       }
75       break;
76     case 6:
77       if (name == "Struct") {
78         return MessageType::kStruct;
79       }
80       break;
81     case 8:
82       if (name == "Duration") {
83         return MessageType::kDuration;
84       }
85       break;
86     case 9:
87       if (name == "BoolValue") {
88         return MessageType::kWrapper;
89       }
90       if (name == "NullValue") {
91         return MessageType::kNull;
92       }
93       if (name == "ListValue") {
94         return MessageType::kList;
95       }
96       if (name == "Timestamp") {
97         return MessageType::kTimestamp;
98       }
99       if (name == "FieldMask") {
100         return MessageType::kFieldMask;
101       }
102       break;
103     case 10:
104       if (name == "BytesValue" || name == "FloatValue" ||
105           name == "Int32Value" || name == "Int64Value") {
106         return MessageType::kWrapper;
107       }
108       break;
109     case 11:
110       if (name == "DoubleValue" || name == "StringValue" ||
111           name == "UInt32Value" || name == "UInt64Value") {
112         return MessageType::kWrapper;
113       }
114       break;
115     default:
116       break;
117   }
118 
119   return MessageType::kNotWellKnown;
120 }
121 
122 // Helper alias templates to avoid needing to write `typename` in function
123 // signatures.
124 template <typename Traits>
125 using Field = typename Traits::Field;
126 template <typename Traits>
127 using Desc = typename Traits::Desc;
128 
129 // Traits for proto2-ish descriptors.
130 struct Proto2Descriptor {
131   // A descriptor for introspecting the fields of a message type.
132   //
133   // Desc<Traits> needs to be handled through a const Desc& in most (but not
134   // all, in the case of ResolverTraits) cases, so we do not include the const*
135   // annotation on this type.
136   using Desc = Descriptor;
137 
138   // A field descriptor for introspecting a single field.
139   //
140   // Field<Traits> is always copyable, so this can be a pointer directly.
141   using Field = const FieldDescriptor*;
142 
143   /// Functions for working with descriptors. ///
144 
TypeNameProto2Descriptor145   static absl::string_view TypeName(const Desc& d) { return d.full_name(); }
146 
FieldByNumberProto2Descriptor147   static absl::optional<Field> FieldByNumber(const Desc& d, int32_t number) {
148     if (const auto* field = d.FindFieldByNumber(number)) {
149       return field;
150     }
151     return absl::nullopt;
152   }
153 
154   static Field MustHaveField(const Desc& d, int32_t number,
155                              JsonLocation::SourceLocation loc =
156                                  JsonLocation::SourceLocation::current()) {
157     auto f = FieldByNumber(d, number);
158     if (!f.has_value()) {
159       ABSL_LOG(FATAL)
160           << absl::StrFormat(
161                  "%s has, by definition, a field numbered %d, but it could not "
162                  "be "
163                  "looked up; this is a bug",
164                  TypeName(d), number);
165     }
166     return *f;
167   }
168 
FieldByNameProto2Descriptor169   static absl::optional<Field> FieldByName(const Desc& d,
170                                            absl::string_view name) {
171     if (const auto* field = d.FindFieldByCamelcaseName(name)) {
172       return field;
173     }
174 
175     if (const auto* field = d.FindFieldByName(name)) {
176       return field;
177     }
178 
179     for (int i = 0; i < d.field_count(); ++i) {
180       const auto* field = d.field(i);
181       if (field->has_json_name() && field->json_name() == name) {
182         return field;
183       }
184     }
185 
186     return absl::nullopt;
187   }
188 
KeyFieldProto2Descriptor189   static Field KeyField(const Desc& d) { return d.map_key(); }
190 
ValueFieldProto2Descriptor191   static Field ValueField(const Desc& d) { return d.map_value(); }
192 
FieldCountProto2Descriptor193   static size_t FieldCount(const Desc& d) { return d.field_count(); }
194 
FieldByIndexProto2Descriptor195   static Field FieldByIndex(const Desc& d, size_t idx) { return d.field(idx); }
196 
ExtensionByNameProto2Descriptor197   static absl::optional<Field> ExtensionByName(const Desc& d,
198                                                absl::string_view name) {
199     auto* field = d.file()->pool()->FindExtensionByName(name);
200     if (field == nullptr) {
201       return absl::nullopt;
202     }
203     return field;
204   }
205 
206   /// Functions for introspecting fields. ///
207 
FieldNameProto2Descriptor208   static absl::string_view FieldName(Field f) { return f->name(); }
FieldJsonNameProto2Descriptor209   static absl::string_view FieldJsonName(Field f) {
210     return f->has_json_name() ? f->json_name() : f->camelcase_name();
211   }
FieldFullNameProto2Descriptor212   static absl::string_view FieldFullName(Field f) { return f->full_name(); }
213 
FieldTypeNameProto2Descriptor214   static absl::string_view FieldTypeName(Field f) {
215     if (f->type() == FieldDescriptor::TYPE_MESSAGE) {
216       return f->message_type()->full_name();
217     }
218     if (f->type() == FieldDescriptor::TYPE_ENUM) {
219       return f->enum_type()->full_name();
220     }
221     return "";
222   }
223 
FieldTypeProto2Descriptor224   static FieldDescriptor::Type FieldType(Field f) { return f->type(); }
225 
FieldNumberProto2Descriptor226   static int32_t FieldNumber(Field f) { return f->number(); }
227 
Is32BitProto2Descriptor228   static bool Is32Bit(Field f) {
229     switch (f->cpp_type()) {
230       case FieldDescriptor::CPPTYPE_UINT32:
231       case FieldDescriptor::CPPTYPE_INT32:
232       case FieldDescriptor::CPPTYPE_ENUM:
233       case FieldDescriptor::CPPTYPE_FLOAT:
234         return true;
235       default:
236         return false;
237     }
238   }
239 
ContainingTypeProto2Descriptor240   static const Desc& ContainingType(Field f) { return *f->containing_type(); }
241 
IsMapProto2Descriptor242   static bool IsMap(Field f) { return f->is_map(); }
243 
IsRepeatedProto2Descriptor244   static bool IsRepeated(Field f) { return f->is_repeated(); }
245 
IsExplicitPresenceProto2Descriptor246   static bool IsExplicitPresence(Field f) { return f->has_presence(); }
247 
IsImplicitPresenceProto2Descriptor248   static bool IsImplicitPresence(Field f) {
249     return !f->is_repeated() && !f->has_presence();
250   }
251 
IsExtensionProto2Descriptor252   static bool IsExtension(Field f) { return f->is_extension(); }
253 
IsOneofProto2Descriptor254   static bool IsOneof(Field f) { return f->containing_oneof() != nullptr; }
255 
EnumNumberByNameProto2Descriptor256   static absl::StatusOr<int32_t> EnumNumberByName(Field f,
257                                                   absl::string_view name,
258                                                   bool case_insensitive) {
259     if (case_insensitive) {
260       for (int i = 0; i < f->enum_type()->value_count(); ++i) {
261         const auto* ev = f->enum_type()->value(i);
262         if (absl::EqualsIgnoreCase(name, ev->name())) {
263           return ev->number();
264         }
265       }
266       return absl::InvalidArgumentError(
267           absl::StrFormat("unknown enum value: '%s'", name));
268     }
269 
270     if (const auto* ev = f->enum_type()->FindValueByName(name)) {
271       return ev->number();
272     }
273 
274     return absl::InvalidArgumentError(
275         absl::StrFormat("unknown enum value: '%s'", name));
276   }
277 
EnumNameByNumberProto2Descriptor278   static absl::StatusOr<std::string> EnumNameByNumber(Field f, int32_t number) {
279     if (const auto* ev = f->enum_type()->FindValueByNumber(number)) {
280       return std::string(ev->name());
281     }
282     return absl::InvalidArgumentError(
283         absl::StrFormat("unknown enum number: '%d'", number));
284   }
285 
286   // Looks up the corresponding Desc for `f`'s type, if there is one, and
287   // calls `body` with it.
288   //
289   // This needs to have this funny callback API since whether or not the
290   // Descriptor equivalent is an owning type depends on the trait.
291   template <typename F>
WithFieldTypeProto2Descriptor292   static absl::Status WithFieldType(Field f, F body) {
293     return body(*f->message_type());
294   }
295 
296   // Like WithFieldType, but using dynamic lookup by type URL.
297   template <typename F>
WithDynamicTypeProto2Descriptor298   static absl::Status WithDynamicType(const Desc& desc,
299                                       const std::string& type_url, F body) {
300     size_t slash = type_url.rfind('/');
301     if (slash == absl::string_view::npos || slash == 0) {
302       return absl::InvalidArgumentError(absl::StrCat(
303           "@type must contain at least one / and a nonempty host; got: ",
304           type_url));
305     }
306     absl::string_view type_name(type_url);
307     type_name = type_name.substr(slash + 1);
308 
309     const Descriptor* dyn_desc =
310         desc.file()->pool()->FindMessageTypeByName(type_name);
311     if (dyn_desc == nullptr) {
312       return absl::InvalidArgumentError(
313           absl::StrFormat("could not find @type '%s'", type_url));
314     }
315 
316     return body(*dyn_desc);
317   }
318 };
319 
320 // Traits for proto3-ish deserialization.
321 //
322 // See Proto2Descriptor for API docs.
323 struct Proto3Type {
324   using Desc = ResolverPool::Message;
325   using Field = const ResolverPool::Field*;
326 
327   /// Functions for working with descriptors. ///
TypeNameProto3Type328   static absl::string_view TypeName(const Desc& d) { return d.proto().name(); }
329 
FieldByNumberProto3Type330   static absl::optional<Field> FieldByNumber(const Desc& d, int32_t number) {
331     const auto* f = d.FindField(number);
332     return f == nullptr ? absl::nullopt : absl::make_optional(f);
333   }
334 
335   static Field MustHaveField(const Desc& d, int32_t number,
336                              JsonLocation::SourceLocation loc =
337                                  JsonLocation::SourceLocation::current()) {
338     auto f = FieldByNumber(d, number);
339     if (!f.has_value()) {
340       ABSL_LOG(FATAL)
341           << absl::StrFormat(
342                  "%s has, by definition, a field numbered %d, but it could not "
343                  "be "
344                  "looked up; this is a bug",
345                  TypeName(d), number);
346     }
347     return *f;
348   }
349 
FieldByNameProto3Type350   static absl::optional<Field> FieldByName(const Desc& d,
351                                            absl::string_view name) {
352     const auto* f = d.FindField(name);
353     return f == nullptr ? absl::nullopt : absl::make_optional(f);
354   }
355 
KeyFieldProto3Type356   static Field KeyField(const Desc& d) { return &d.FieldsByIndex()[0]; }
357 
ValueFieldProto3Type358   static Field ValueField(const Desc& d) { return &d.FieldsByIndex()[1]; }
359 
FieldCountProto3Type360   static size_t FieldCount(const Desc& d) { return d.proto().fields_size(); }
361 
FieldByIndexProto3Type362   static Field FieldByIndex(const Desc& d, size_t idx) {
363     return &d.FieldsByIndex()[idx];
364   }
365 
ExtensionByNameProto3Type366   static absl::optional<Field> ExtensionByName(const Desc& d,
367                                                absl::string_view name) {
368     // type.proto cannot represent extensions, so this function always
369     // fails.
370     return absl::nullopt;
371   }
372 
373   /// Functions for introspecting fields. ///
374 
FieldNameProto3Type375   static absl::string_view FieldName(Field f) { return f->proto().name(); }
FieldJsonNameProto3Type376   static absl::string_view FieldJsonName(Field f) {
377     return f->proto().json_name();
378   }
FieldFullNameProto3Type379   static absl::string_view FieldFullName(Field f) { return f->proto().name(); }
380 
FieldTypeNameProto3Type381   static absl::string_view FieldTypeName(Field f) {
382     absl::string_view url = f->proto().type_url();
383 
384     // If there is no slash, `slash` is string_view::npos, which is guaranteed
385     // to be -1.
386     size_t slash = url.rfind('/');
387     return url.substr(slash + 1);
388   }
389 
FieldTypeProto3Type390   static FieldDescriptor::Type FieldType(Field f) {
391     // The descriptor.proto and type.proto field type enums are required to be
392     // the same, so we leverage this.
393     return static_cast<FieldDescriptor::Type>(f->proto().kind());
394   }
395 
FieldNumberProto3Type396   static int32_t FieldNumber(Field f) { return f->proto().number(); }
397 
Is32BitProto3Type398   static bool Is32Bit(Field f) {
399     switch (f->proto().kind()) {
400       case google::protobuf::Field::TYPE_INT32:
401       case google::protobuf::Field::TYPE_SINT32:
402       case google::protobuf::Field::TYPE_UINT32:
403       case google::protobuf::Field::TYPE_FIXED32:
404       case google::protobuf::Field::TYPE_SFIXED32:
405       case google::protobuf::Field::TYPE_FLOAT:
406         return true;
407       default:
408         return false;
409     }
410   }
411 
ContainingTypeProto3Type412   static const Desc& ContainingType(Field f) { return f->parent(); }
IsMapProto3Type413   static bool IsMap(Field f) {
414     if (f->proto().kind() != google::protobuf::Field::TYPE_MESSAGE) {
415       return false;
416     }
417 
418     bool value = false;
419     (void)WithFieldType(f, [&value](const Desc& desc) {
420       value = absl::c_any_of(desc.proto().options(), [&](auto& option) {
421         return option.name() == "map_entry";
422       });
423       return absl::OkStatus();
424     });
425     return value;
426   }
427 
IsRepeatedProto3Type428   static bool IsRepeated(Field f) {
429     return f->proto().cardinality() ==
430            google::protobuf::Field::CARDINALITY_REPEATED;
431   }
432 
IsExplicitPresenceProto3Type433   static bool IsExplicitPresence(Field f) {
434     // Implicit presence requires this weird check: in proto3 the following
435     // cases support presence:
436     // 1) Anything contained in a oneof (including things explicitly declared
437     //    'optional' which are represented as as synthetic oneof in proto3).
438     // 2) Fields that are a message type (but not map fields which are also
439     //    TYPE_MESSAGE here).
440     if (f->parent().proto().syntax() == google::protobuf::SYNTAX_PROTO3) {
441       return f->proto().oneof_index() != 0 ||
442              (f->proto().kind() == google::protobuf::Field::TYPE_MESSAGE &&
443               !IsRepeated(f));
444     }
445 
446     return f->proto().cardinality() ==
447                google::protobuf::Field::CARDINALITY_OPTIONAL ||
448            google::protobuf::Field::CARDINALITY_REQUIRED;
449   }
450 
IsImplicitPresenceProto3Type451   static bool IsImplicitPresence(Field f) {
452     return !IsRepeated(f) && !IsExplicitPresence(f);
453   }
454 
IsExtensionProto3Type455   static bool IsExtension(Field f) { return false; }
456 
IsOneofProto3Type457   static bool IsOneof(Field f) { return f->proto().oneof_index() != 0; }
458 
EnumNumberByNameProto3Type459   static absl::StatusOr<int32_t> EnumNumberByName(Field f,
460                                                   absl::string_view name,
461                                                   bool case_insensitive) {
462     auto e = f->EnumType();
463     RETURN_IF_ERROR(e.status());
464 
465     for (const auto& ev : (**e).proto().enumvalue()) {
466       if (case_insensitive) {
467         // Two ifs to avoid doing operator== twice if the names are not equal.
468         if (absl::EqualsIgnoreCase(ev.name(), name)) {
469           return ev.number();
470         }
471       } else if (ev.name() == name) {
472         return ev.number();
473       }
474     }
475     return absl::InvalidArgumentError(
476         absl::StrFormat("unknown enum value: '%s'", name));
477   }
478 
EnumNameByNumberProto3Type479   static absl::StatusOr<std::string> EnumNameByNumber(Field f, int32_t number) {
480     auto e = f->EnumType();
481     RETURN_IF_ERROR(e.status());
482 
483     for (const auto& ev : (**e).proto().enumvalue()) {
484       if (ev.number() == number) {
485         return ev.name();
486       }
487     }
488     return absl::InvalidArgumentError(
489         absl::StrFormat("unknown enum number: '%d'", number));
490   }
491 
492   template <typename F>
WithFieldTypeProto3Type493   static absl::Status WithFieldType(Field f, F body) {
494     auto m = f->MessageType();
495     RETURN_IF_ERROR(m.status());
496     return body(**m);
497   }
498 
499   template <typename F>
WithDynamicTypeProto3Type500   static absl::Status WithDynamicType(const Desc& desc,
501                                       const std::string& type_url, F body) {
502     auto dyn_desc = desc.pool()->FindMessage(type_url);
503     RETURN_IF_ERROR(dyn_desc.status());
504     return body(**dyn_desc);
505   }
506 };
507 }  // namespace json_internal
508 }  // namespace protobuf
509 }  // namespace google
510 
511 #include "google/protobuf/port_undef.inc"
512 #endif  // GOOGLE_PROTOBUF_JSON_INTERNAL_DESCRIPTOR_TRAITS_INTERNAL_H__
513