Commit b6a2b5d3c184003f259abbb58536f3466b1ee97f

Authored by m-holger
1 parent 6b80e0f1

Handle default-constructed JSON objects

libqpdf/JSON.cc
... ... @@ -191,7 +191,7 @@ JSON::JSON_blob::write(Pipeline* p, size_t) const
191 191 void
192 192 JSON::write(Pipeline* p, size_t depth) const
193 193 {
194   - if (nullptr == m->value) {
  194 + if (!m) {
195 195 *p << "null";
196 196 } else {
197 197 m->value->write(p, depth);
... ... @@ -201,6 +201,9 @@ JSON::write(Pipeline* p, size_t depth) const
201 201 std::string
202 202 JSON::unparse() const
203 203 {
  204 + if (!m) {
  205 + return "null";
  206 + }
204 207 std::string s;
205 208 Pl_String p("unparse", nullptr, s);
206 209 write(&p, 0);
... ... @@ -275,8 +278,8 @@ JSON::makeDictionary()
275 278 JSON
276 279 JSON::addDictionaryMember(std::string const& key, JSON const& val)
277 280 {
278   - if (auto* obj = dynamic_cast<JSON_dictionary*>(m->value.get())) {
279   - return obj->members[encode_string(key)] = val.m->value ? val : makeNull();
  281 + if (auto* obj = m ? dynamic_cast<JSON_dictionary*>(m->value.get()) : nullptr) {
  282 + return obj->members[encode_string(key)] = val.m ? val : makeNull();
280 283 } else {
281 284 throw std::runtime_error("JSON::addDictionaryMember called on non-dictionary");
282 285 }
... ... @@ -285,15 +288,11 @@ JSON::addDictionaryMember(std::string const&amp; key, JSON const&amp; val)
285 288 bool
286 289 JSON::checkDictionaryKeySeen(std::string const& key)
287 290 {
288   - auto* obj = dynamic_cast<JSON_dictionary*>(m->value.get());
289   - if (nullptr == obj) {
290   - throw std::logic_error("JSON::checkDictionaryKey called on non-dictionary");
291   - }
292   - if (obj->parsed_keys.count(key)) {
293   - return true;
  291 + if (auto* obj = m ? dynamic_cast<JSON_dictionary*>(m->value.get()) : nullptr) {
  292 + return !obj->parsed_keys.insert(key).second;
294 293 }
295   - obj->parsed_keys.insert(key);
296   - return false;
  294 + throw std::logic_error("JSON::checkDictionaryKey called on non-dictionary");
  295 + return false; // unreachable
297 296 }
298 297  
299 298 JSON
... ... @@ -305,16 +304,16 @@ JSON::makeArray()
305 304 JSON
306 305 JSON::addArrayElement(JSON const& val)
307 306 {
308   - auto* arr = dynamic_cast<JSON_array*>(m->value.get());
309   - if (nullptr == arr) {
310   - throw std::runtime_error("JSON::addArrayElement called on non-array");
311   - }
312   - if (val.m->value.get()) {
313   - arr->elements.push_back(val);
314   - } else {
315   - arr->elements.push_back(makeNull());
  307 + if (auto* arr = m ? dynamic_cast<JSON_array*>(m->value.get()) : nullptr) {
  308 + if (val.m) {
  309 + arr->elements.push_back(val);
  310 + } else {
  311 + arr->elements.push_back(makeNull());
  312 + }
  313 + return arr->elements.back();
316 314 }
317   - return arr->elements.back();
  315 + throw std::runtime_error("JSON::addArrayElement called on non-array");
  316 + return {}; // unreachable
318 317 }
319 318  
320 319 JSON
... ... @@ -362,19 +361,19 @@ JSON::makeBlob(std::function&lt;void(Pipeline*)&gt; fn)
362 361 bool
363 362 JSON::isArray() const
364 363 {
365   - return m->value->type_code == vt_array;
  364 + return m ? m->value->type_code == vt_array : false;
366 365 }
367 366  
368 367 bool
369 368 JSON::isDictionary() const
370 369 {
371   - return m->value->type_code == vt_dictionary;
  370 + return m && m->value->type_code == vt_dictionary;
372 371 }
373 372  
374 373 bool
375 374 JSON::getString(std::string& utf8) const
376 375 {
377   - if (m->value->type_code == vt_string) {
  376 + if (m && m->value->type_code == vt_string) {
378 377 auto v = dynamic_cast<JSON_string const*>(m->value.get());
379 378 utf8 = v->utf8;
380 379 return true;
... ... @@ -385,7 +384,7 @@ JSON::getString(std::string&amp; utf8) const
385 384 bool
386 385 JSON::getNumber(std::string& value) const
387 386 {
388   - if (m->value->type_code == vt_number) {
  387 + if (m && m->value->type_code == vt_number) {
389 388 auto v = dynamic_cast<JSON_number const*>(m->value.get());
390 389 value = v->encoded;
391 390 return true;
... ... @@ -396,7 +395,7 @@ JSON::getNumber(std::string&amp; value) const
396 395 bool
397 396 JSON::getBool(bool& value) const
398 397 {
399   - if (m->value->type_code == vt_bool) {
  398 + if (m && m->value->type_code == vt_bool) {
400 399 auto v = dynamic_cast<JSON_bool const*>(m->value.get());
401 400 value = v->value;
402 401 return true;
... ... @@ -407,13 +406,13 @@ JSON::getBool(bool&amp; value) const
407 406 bool
408 407 JSON::isNull() const
409 408 {
410   - return m->value->type_code == vt_null;
  409 + return m && m->value->type_code == vt_null;
411 410 }
412 411  
413 412 JSON
414 413 JSON::getDictItem(std::string const& key) const
415 414 {
416   - if (auto v = dynamic_cast<JSON_dictionary const*>(m->value.get())) {
  415 + if (auto v = m ? dynamic_cast<JSON_dictionary const*>(m->value.get()) : nullptr) {
417 416 if (auto it = v->members.find(key); it != v->members.end()) {
418 417 return it->second;
419 418 }
... ... @@ -424,39 +423,37 @@ JSON::getDictItem(std::string const&amp; key) const
424 423 bool
425 424 JSON::forEachDictItem(std::function<void(std::string const& key, JSON value)> fn) const
426 425 {
427   - auto v = dynamic_cast<JSON_dictionary const*>(m->value.get());
428   - if (v == nullptr) {
429   - return false;
430   - }
431   - for (auto const& k: v->members) {
432   - fn(k.first, JSON(k.second));
  426 + if (auto v = m ? dynamic_cast<JSON_dictionary const*>(m->value.get()) : nullptr) {
  427 + for (auto const& [key, value]: v->members) {
  428 + fn(key, value);
  429 + }
  430 + return true;
433 431 }
434   - return true;
  432 + return false;
435 433 }
436 434  
437 435 bool
438 436 JSON::forEachArrayItem(std::function<void(JSON value)> fn) const
439 437 {
440   - auto v = dynamic_cast<JSON_array const*>(m->value.get());
441   - if (v == nullptr) {
442   - return false;
443   - }
444   - for (auto const& i: v->elements) {
445   - fn(JSON(i));
  438 + if (auto v = m ? dynamic_cast<JSON_array const*>(m->value.get()) : nullptr) {
  439 + for (auto const& i: v->elements) {
  440 + fn(JSON(i));
  441 + }
  442 + return true;
446 443 }
447   - return true;
  444 + return false;
448 445 }
449 446  
450 447 bool
451 448 JSON::checkSchema(JSON schema, std::list<std::string>& errors)
452 449 {
453   - return checkSchemaInternal(m->value.get(), schema.m->value.get(), 0, errors, "");
  450 + return m && checkSchemaInternal(m->value.get(), schema.m->value.get(), 0, errors, "");
454 451 }
455 452  
456 453 bool
457 454 JSON::checkSchema(JSON schema, unsigned long flags, std::list<std::string>& errors)
458 455 {
459   - return checkSchemaInternal(m->value.get(), schema.m->value.get(), flags, errors, "");
  456 + return m && checkSchemaInternal(m->value.get(), schema.m->value.get(), flags, errors, "");
460 457 }
461 458  
462 459 bool
... ... @@ -1374,23 +1371,27 @@ JSON::parse(std::string const&amp; s)
1374 1371 void
1375 1372 JSON::setStart(qpdf_offset_t start)
1376 1373 {
1377   - m->start = start;
  1374 + if (m) {
  1375 + m->start = start;
  1376 + }
1378 1377 }
1379 1378  
1380 1379 void
1381 1380 JSON::setEnd(qpdf_offset_t end)
1382 1381 {
1383   - m->end = end;
  1382 + if (m) {
  1383 + m->end = end;
  1384 + }
1384 1385 }
1385 1386  
1386 1387 qpdf_offset_t
1387 1388 JSON::getStart() const
1388 1389 {
1389   - return m->start;
  1390 + return m ? m->start : 0;
1390 1391 }
1391 1392  
1392 1393 qpdf_offset_t
1393 1394 JSON::getEnd() const
1394 1395 {
1395   - return m->end;
  1396 + return m ? m->end : 0;
1396 1397 }
... ...
libtests/json.cc
... ... @@ -2,8 +2,10 @@
2 2  
3 3 #include <qpdf/JSON.hh>
4 4 #include <qpdf/Pipeline.hh>
  5 +#include <qpdf/Pl_String.hh>
5 6 #include <qpdf/QPDF.hh>
6 7 #include <qpdf/QPDFObjectHandle.hh>
  8 +
7 9 #include <iostream>
8 10  
9 11 static void
... ... @@ -131,6 +133,51 @@ test_main()
131 133 " \"blob\": \"AQIDBAX//v38+w==\",\n"
132 134 " \"normal\": \"string\"\n"
133 135 "}");
  136 +
  137 + // Check default constructed JSON object (order as per JSON.hh).
  138 + JSON uninitialized;
  139 + std::string ws;
  140 + auto pl = Pl_String ("", nullptr, ws);
  141 + uninitialized.write(&pl);
  142 + assert(ws == "null");
  143 + assert(uninitialized.unparse() == "null");
  144 + try {
  145 + uninitialized.addDictionaryMember("key", jarr);
  146 + assert(false);
  147 + } catch (std::runtime_error&) {
  148 + }
  149 + assert(jmap.addDictionaryMember("42", uninitialized).isNull());
  150 + try {
  151 + uninitialized.addArrayElement(jarr);
  152 + assert(false);
  153 + } catch (std::runtime_error&) {
  154 + }
  155 + assert(jarr.addArrayElement(uninitialized).isNull());
  156 + assert(!uninitialized.isArray());
  157 + assert(!uninitialized.isDictionary());
  158 + try {
  159 + uninitialized.checkDictionaryKeySeen("key");
  160 + assert(false);
  161 + } catch (std::logic_error&) {
  162 + }
  163 + std::string st_out = "unchanged";
  164 + assert(!uninitialized.getString(st_out));
  165 + assert(!uninitialized.getNumber(st_out));
  166 + bool b_out = true;
  167 + assert(!uninitialized.getBool(b_out));
  168 + assert(b_out && st_out == "unchanged");
  169 + assert(!uninitialized.isNull());
  170 + assert(uninitialized.getDictItem("42").isNull());
  171 + assert(!uninitialized.forEachDictItem([](auto k, auto v) {}));
  172 + assert(!uninitialized.forEachArrayItem([](auto v) {}));
  173 + std::list<std::string> e;
  174 + assert(!uninitialized.checkSchema(JSON(), 0, e));
  175 + assert(!uninitialized.checkSchema(JSON(), e));
  176 + assert(e.empty());
  177 + uninitialized.setStart(0);
  178 + uninitialized.setEnd(0);
  179 + assert(uninitialized.getStart() == 0);
  180 + assert(uninitialized.getEnd() == 0);
134 181 }
135 182  
136 183 static void
... ...