1 // This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
2 // the main distribution directory for license terms and copyright or visit
3 // https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
4 
5 #include "caf/json_reader.hpp"
6 
7 #include "caf/detail/bounds_checker.hpp"
8 #include "caf/detail/print.hpp"
9 #include "caf/string_algorithms.hpp"
10 
11 namespace {
12 
13 static constexpr const char class_name[] = "caf::json_reader";
14 
pretty_name(caf::json_reader::position pos)15 caf::string_view pretty_name(caf::json_reader::position pos) {
16   switch (pos) {
17     default:
18       return "invalid input";
19     case caf::json_reader::position::value:
20       return "json::value";
21     case caf::json_reader::position::object:
22       return "json::object";
23     case caf::json_reader::position::null:
24       return "null";
25     case caf::json_reader::position::key:
26       return "json::key";
27     case caf::json_reader::position::sequence:
28       return "json::array";
29     case caf::json_reader::position::members:
30       return "json::members";
31   }
32 }
33 
type_clash(caf::string_view want,caf::string_view got)34 std::string type_clash(caf::string_view want, caf::string_view got) {
35   std::string result = "type clash: expected ";
36   result.insert(result.end(), want.begin(), want.end());
37   result += ", got ";
38   result.insert(result.end(), got.begin(), got.end());
39   return result;
40 }
41 
type_clash(caf::string_view want,caf::json_reader::position got)42 std::string type_clash(caf::string_view want, caf::json_reader::position got) {
43   return type_clash(want, pretty_name(got));
44 }
45 
type_clash(caf::json_reader::position want,caf::json_reader::position got)46 std::string type_clash(caf::json_reader::position want,
47                        caf::json_reader::position got) {
48   return type_clash(pretty_name(want), pretty_name(got));
49 }
50 
type_clash(caf::string_view want,const caf::detail::json::value & got)51 std::string type_clash(caf::string_view want,
52                        const caf::detail::json::value& got) {
53   using namespace caf::literals;
54   using caf::detail::json::value;
55   switch (got.data.index()) {
56     case value::integer_index:
57       return type_clash(want, "json::integer"_sv);
58     case value::double_index:
59       return type_clash(want, "json::real"_sv);
60     case value::bool_index:
61       return type_clash(want, "json::boolean"_sv);
62     case value::string_index:
63       return type_clash(want, "json::string"_sv);
64     case value::array_index:
65       return type_clash(want, "json::array"_sv);
66     case value::object_index:
67       return type_clash(want, "json::object"_sv);
68     default:
69       return type_clash(want, "json::null"_sv);
70   }
71 }
72 
73 const caf::detail::json::member*
find_member(const caf::detail::json::object * obj,caf::string_view key)74 find_member(const caf::detail::json::object* obj, caf::string_view key) {
75   for (const auto& member : *obj)
76     if (member.key == key)
77       return &member;
78   return nullptr;
79 }
80 
field_type(const caf::detail::json::object * obj,caf::string_view name,caf::string_view suffix)81 caf::string_view field_type(const caf::detail::json::object* obj,
82                             caf::string_view name, caf::string_view suffix) {
83   namespace json = caf::detail::json;
84   for (const auto& member : *obj) {
85     if (member.val && member.val->data.index() == json::value::string_index
86         && !member.key.empty() && member.key[0] == '@') {
87       auto key = member.key;
88       key.remove_prefix(1);
89       if (caf::starts_with(key, name)) {
90         key.remove_prefix(name.size());
91         if (key == suffix)
92           return std::get<caf::string_view>(member.val->data);
93       }
94     }
95   }
96   return {};
97 }
98 
99 } // namespace
100 
101 #define FN_DECL static constexpr const char* fn = __func__
102 
103 #define INVALID_AND_PAST_THE_END_CASES                                         \
104   case position::invalid:                                                      \
105     emplace_error(sec::runtime_error, class_name, fn,                          \
106                   "found an invalid position");                                \
107     return false;                                                              \
108   case position::past_the_end:                                                 \
109     emplace_error(sec::runtime_error, class_name, fn,                          \
110                   "tried reading past the end");                               \
111     return false;
112 
113 #define SCOPE(expected_position)                                               \
114   if (auto got = pos(); got != expected_position) {                            \
115     emplace_error(sec::runtime_error, class_name, __func__,                    \
116                   type_clash(expected_position, got));                         \
117     return false;                                                              \
118   }
119 
120 namespace caf {
121 
122 // -- constructors, destructors, and assignment operators ----------------------
123 
json_reader()124 json_reader::json_reader() : super() {
125   has_human_readable_format_ = true;
126 }
127 
json_reader(actor_system & sys)128 json_reader::json_reader(actor_system& sys) : super(sys) {
129   has_human_readable_format_ = true;
130 }
131 
json_reader(execution_unit * ctx)132 json_reader::json_reader(execution_unit* ctx) : super(ctx) {
133   has_human_readable_format_ = true;
134 }
135 
~json_reader()136 json_reader::~json_reader() {
137   // nop
138 }
139 
140 // -- modifiers --------------------------------------------------------------
141 
load(string_view json_text)142 bool json_reader::load(string_view json_text) {
143   reset();
144   string_parser_state ps{json_text.begin(), json_text.end()};
145   root_ = detail::json::parse(ps, &buf_);
146   if (ps.code != pec::success) {
147     set_error(make_error(ps));
148     st_ = nullptr;
149     return false;
150   } else {
151     err_.reset();
152     detail::monotonic_buffer_resource::allocator<stack_type> alloc{&buf_};
153     st_ = new (alloc.allocate(1)) stack_type(stack_allocator{&buf_});
154     st_->reserve(16);
155     st_->emplace_back(root_);
156     return true;
157   }
158 }
159 
revert()160 void json_reader::revert() {
161   if (st_) {
162     CAF_ASSERT(root_ != nullptr);
163     err_.reset();
164     st_->clear();
165     st_->emplace_back(root_);
166   }
167 }
168 
reset()169 void json_reader::reset() {
170   buf_.reclaim();
171   st_ = nullptr;
172   err_.reset();
173 }
174 
175 // -- interface functions ------------------------------------------------------
176 
fetch_next_object_type(type_id_t & type)177 bool json_reader::fetch_next_object_type(type_id_t& type) {
178   string_view type_name;
179   if (fetch_next_object_name(type_name)) {
180     if (auto id = query_type_id(type_name); id != invalid_type_id) {
181       type = id;
182       return true;
183     } else {
184       std::string what = "no type ID available for @type: ";
185       what.insert(what.end(), type_name.begin(), type_name.end());
186       emplace_error(sec::runtime_error, class_name, __func__, std::move(what));
187       return false;
188     }
189   } else {
190     return false;
191   }
192 }
193 
fetch_next_object_name(string_view & type_name)194 bool json_reader::fetch_next_object_name(string_view& type_name) {
195   FN_DECL;
196   return consume<false>(fn, [this, &type_name](const detail::json::value& val) {
197     if (val.data.index() == detail::json::value::object_index) {
198       auto& obj = get<detail::json::object>(val.data);
199       if (auto mem_ptr = find_member(&obj, "@type")) {
200         if (mem_ptr->val->data.index() == detail::json::value::string_index) {
201           type_name = std::get<string_view>(mem_ptr->val->data);
202           return true;
203         } else {
204           emplace_error(sec::runtime_error, class_name, fn,
205                         "expected a string argument to @type");
206           return false;
207         }
208       } else {
209         emplace_error(sec::runtime_error, class_name, fn,
210                       "found no @type member");
211         return false;
212       }
213     } else {
214       emplace_error(sec::runtime_error, class_name, fn,
215                     type_clash("json::object", val));
216       return false;
217     }
218   });
219 }
220 
begin_object(type_id_t,string_view)221 bool json_reader::begin_object(type_id_t, string_view) {
222   FN_DECL;
223   return consume<false>(fn, [this](const detail::json::value& val) {
224     if (val.data.index() == detail::json::value::object_index) {
225       push(&get<detail::json::object>(val.data));
226       return true;
227     } else {
228       emplace_error(sec::runtime_error, class_name, fn,
229                     type_clash("json::object", val));
230       return false;
231     }
232   });
233 }
234 
end_object()235 bool json_reader::end_object() {
236   FN_DECL;
237   SCOPE(position::object);
238   pop();
239   auto current_pos = pos();
240   switch (current_pos) {
241     INVALID_AND_PAST_THE_END_CASES
242     case position::value:
243       pop();
244       return true;
245     case position::sequence:
246       top<position::sequence>().advance();
247       return true;
248     default:
249       emplace_error(sec::runtime_error, class_name, __func__,
250                     type_clash("json::value or json::array", current_pos));
251       return false;
252   }
253 }
254 
begin_field(string_view name)255 bool json_reader::begin_field(string_view name) {
256   SCOPE(position::object);
257   if (auto member = find_member(top<position::object>(), name)) {
258     push(member->val);
259     return true;
260   } else {
261     emplace_error(sec::runtime_error, class_name, __func__,
262                   "no such field: " + to_string(name));
263     return false;
264   }
265 }
266 
begin_field(string_view name,bool & is_present)267 bool json_reader::begin_field(string_view name, bool& is_present) {
268   SCOPE(position::object);
269   if (auto member = find_member(top<position::object>(), name);
270       member != nullptr
271       && member->val->data.index() != detail::json::value::null_index) {
272     push(member->val);
273     is_present = true;
274   } else {
275     is_present = false;
276   }
277   return true;
278 }
279 
begin_field(string_view name,span<const type_id_t> types,size_t & index)280 bool json_reader::begin_field(string_view name, span<const type_id_t> types,
281                               size_t& index) {
282   bool is_present = false;
283   if (begin_field(name, is_present, types, index)) {
284     if (is_present) {
285       return true;
286     } else {
287       emplace_error(sec::runtime_error, class_name, __func__,
288                     "mandatory field missing");
289       return false;
290     }
291   } else {
292     return false;
293   }
294 }
295 
begin_field(string_view name,bool & is_present,span<const type_id_t> types,size_t & index)296 bool json_reader::begin_field(string_view name, bool& is_present,
297                               span<const type_id_t> types, size_t& index) {
298   SCOPE(position::object);
299   if (auto member = find_member(top<position::object>(), name);
300       member != nullptr
301       && member->val->data.index() != detail::json::value::null_index) {
302     auto ft = field_type(top<position::object>(), name, field_type_suffix_);
303     if (auto id = query_type_id(ft); id != invalid_type_id) {
304       if (auto i = std::find(types.begin(), types.end(), id);
305           i != types.end()) {
306         index = static_cast<size_t>(std::distance(types.begin(), i));
307         push(member->val);
308         is_present = true;
309         return true;
310       }
311     }
312   }
313   is_present = false;
314   return true;
315 }
316 
end_field()317 bool json_reader::end_field() {
318   SCOPE(position::object);
319   // Note: no pop() here, because the value(s) were already consumed.
320   return true;
321 }
322 
begin_tuple(size_t size)323 bool json_reader::begin_tuple(size_t size) {
324   size_t list_size = 0;
325   if (begin_sequence(list_size)) {
326     if (list_size == size) {
327       return true;
328     } else {
329       std::string msg;
330       msg += "expected tuple of size ";
331       detail::print(msg, size);
332       msg += ", got a list of size ";
333       detail::print(msg, list_size);
334       emplace_error(sec::conversion_failed, class_name, __func__,
335                     std::move(msg));
336       return false;
337     }
338   } else {
339     return false;
340   }
341 }
342 
end_tuple()343 bool json_reader::end_tuple() {
344   return end_sequence();
345 }
346 
begin_key_value_pair()347 bool json_reader::begin_key_value_pair() {
348   SCOPE(position::members);
349   if (auto& xs = top<position::members>(); !xs.at_end()) {
350     auto& current = xs.current();
351     push(current.val);
352     push(current.key);
353     return true;
354   } else {
355     emplace_error(sec::runtime_error, class_name, __func__,
356                   "tried reading a JSON::object sequentially past its end");
357     return false;
358   }
359 }
360 
end_key_value_pair()361 bool json_reader::end_key_value_pair() {
362   SCOPE(position::members);
363   top<position::members>().advance();
364   return true;
365 }
366 
begin_sequence(size_t & size)367 bool json_reader::begin_sequence(size_t& size) {
368   FN_DECL;
369   return consume<false>(fn, [this, &size](const detail::json::value& val) {
370     if (val.data.index() == detail::json::value::array_index) {
371       auto& ls = get<detail::json::array>(val.data);
372       size = ls.size();
373       push(sequence{ls.begin(), ls.end()});
374       return true;
375     } else {
376       emplace_error(sec::runtime_error, class_name, fn,
377                     type_clash("json::array", val));
378       return false;
379     }
380   });
381 }
382 
end_sequence()383 bool json_reader::end_sequence() {
384   SCOPE(position::sequence);
385   if (top<position::sequence>().at_end()) {
386     pop();
387     // We called consume<false> at first, so we need to call it again with
388     // <true> for advancing the position now.
389     return consume<true>(__func__,
390                          [](const detail::json::value&) { return true; });
391   } else {
392     emplace_error(sec::runtime_error, class_name, __func__,
393                   "failed to consume all elements from json::array");
394     return false;
395   }
396 }
397 
begin_associative_array(size_t & size)398 bool json_reader::begin_associative_array(size_t& size) {
399   FN_DECL;
400   return consume<false>(fn, [this, &size](const detail::json::value& val) {
401     if (val.data.index() == detail::json::value::object_index) {
402       auto* obj = std::addressof(get<detail::json::object>(val.data));
403       pop();
404       size = obj->size();
405       push(members{obj->begin(), obj->end()});
406       return true;
407     } else {
408       emplace_error(sec::runtime_error, class_name, fn,
409                     type_clash("json::object", val));
410       return false;
411     }
412   });
413 }
414 
end_associative_array()415 bool json_reader::end_associative_array() {
416   SCOPE(position::members);
417   if (top<position::members>().at_end()) {
418     pop();
419     return true;
420   } else {
421     emplace_error(sec::runtime_error, class_name, __func__,
422                   "failed to consume all elements in an associative array");
423     return false;
424   }
425 }
426 
value(byte & x)427 bool json_reader::value(byte& x) {
428   auto tmp = uint8_t{0};
429   if (value(tmp)) {
430     x = static_cast<byte>(tmp);
431     return true;
432   } else {
433     return false;
434   }
435 }
436 
value(bool & x)437 bool json_reader::value(bool& x) {
438   FN_DECL;
439   return consume<true>(fn, [this, &x](const detail::json::value& val) {
440     if (val.data.index() == detail::json::value::bool_index) {
441       x = std::get<bool>(val.data);
442       return true;
443     } else {
444       emplace_error(sec::runtime_error, class_name, fn,
445                     type_clash("json::boolean", val));
446       return false;
447     }
448   });
449 }
450 
451 template <bool PopOrAdvanceOnSuccess, class F>
consume(const char * fn,F f)452 bool json_reader::consume(const char* fn, F f) {
453   auto current_pos = pos();
454   switch (current_pos) {
455     INVALID_AND_PAST_THE_END_CASES
456     case position::value:
457       if (f(*top<position::value>())) {
458         if constexpr (PopOrAdvanceOnSuccess)
459           pop();
460         return true;
461       } else {
462         return false;
463       }
464     case position::key:
465       if (f(detail::json::value{top<position::key>()})) {
466         if constexpr (PopOrAdvanceOnSuccess)
467           pop();
468         return true;
469       } else {
470         return false;
471       }
472     case position::sequence:
473       if (auto& ls = top<position::sequence>(); !ls.at_end()) {
474         auto& curr = ls.current();
475         if constexpr (PopOrAdvanceOnSuccess)
476           ls.advance();
477         return f(curr);
478       } else {
479         emplace_error(sec::runtime_error, class_name, fn,
480                       "tried reading a json::array past the end");
481         return false;
482       }
483     default:
484       emplace_error(sec::runtime_error, class_name, fn,
485                     type_clash("json::value", current_pos));
486       return false;
487   }
488 }
489 
490 template <class T>
integer(T & x)491 bool json_reader::integer(T& x) {
492   static constexpr const char* fn = "value";
493   return consume<true>(fn, [this, &x](const detail::json::value& val) {
494     if (val.data.index() == detail::json::value::integer_index) {
495       auto i64 = std::get<int64_t>(val.data);
496       if (detail::bounds_checker<T>::check(i64)) {
497         x = static_cast<T>(i64);
498         return true;
499       } else {
500         emplace_error(sec::runtime_error, class_name, fn,
501                       "integer out of bounds");
502         return false;
503       }
504     } else {
505       emplace_error(sec::runtime_error, class_name, fn,
506                     type_clash("json::integer", val));
507       return false;
508     }
509   });
510 }
511 
value(int8_t & x)512 bool json_reader::value(int8_t& x) {
513   return integer(x);
514 }
515 
value(uint8_t & x)516 bool json_reader::value(uint8_t& x) {
517   return integer(x);
518 }
519 
value(int16_t & x)520 bool json_reader::value(int16_t& x) {
521   return integer(x);
522 }
523 
value(uint16_t & x)524 bool json_reader::value(uint16_t& x) {
525   return integer(x);
526 }
527 
value(int32_t & x)528 bool json_reader::value(int32_t& x) {
529   return integer(x);
530 }
531 
value(uint32_t & x)532 bool json_reader::value(uint32_t& x) {
533   return integer(x);
534 }
535 
value(int64_t & x)536 bool json_reader::value(int64_t& x) {
537   return integer(x);
538 }
539 
value(uint64_t & x)540 bool json_reader::value(uint64_t& x) {
541   return integer(x);
542 }
543 
value(float & x)544 bool json_reader::value(float& x) {
545   auto tmp = 0.0;
546   if (value(tmp)) {
547     x = static_cast<float>(tmp);
548     return true;
549   } else {
550     return false;
551   }
552 }
553 
value(double & x)554 bool json_reader::value(double& x) {
555   FN_DECL;
556   return consume<true>(fn, [this, &x](const detail::json::value& val) {
557     if (val.data.index() == detail::json::value::double_index) {
558       x = std::get<double>(val.data);
559       return true;
560     } else {
561       emplace_error(sec::runtime_error, class_name, fn,
562                     type_clash("json::real", val));
563       return false;
564     }
565   });
566 }
567 
value(long double & x)568 bool json_reader::value(long double& x) {
569   auto tmp = 0.0;
570   if (value(tmp)) {
571     x = static_cast<long double>(tmp);
572     return true;
573   } else {
574     return false;
575   }
576 }
577 
value(std::string & x)578 bool json_reader::value(std::string& x) {
579   FN_DECL;
580   return consume<true>(fn, [this, &x](const detail::json::value& val) {
581     if (val.data.index() == detail::json::value::string_index) {
582       detail::print_unescaped(x, std::get<string_view>(val.data));
583       return true;
584     } else {
585       emplace_error(sec::runtime_error, class_name, fn,
586                     type_clash("json::string", val));
587       return false;
588     }
589   });
590 }
591 
value(std::u16string &)592 bool json_reader::value(std::u16string&) {
593   emplace_error(sec::runtime_error, class_name, __func__,
594                 "u16string support not implemented yet");
595   return false;
596 }
597 
value(std::u32string &)598 bool json_reader::value(std::u32string&) {
599   emplace_error(sec::runtime_error, class_name, __func__,
600                 "u32string support not implemented yet");
601   return false;
602 }
603 
value(span<byte>)604 bool json_reader::value(span<byte>) {
605   emplace_error(sec::runtime_error, class_name, __func__,
606                 "byte span support not implemented yet");
607   return false;
608 }
609 
pos() const610 json_reader::position json_reader::pos() const noexcept {
611   if (st_ == nullptr)
612     return position::invalid;
613   else if (st_->empty())
614     return position::past_the_end;
615   else
616     return static_cast<position>(st_->back().index());
617 }
618 
619 } // namespace caf
620