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