Commit 7be985b032d959fd9763088e6b36c7a923895b4e

Authored by Jay Berkenbilt
Committed by GitHub
2 parents 6b80e0f1 b6a2b5d3

Merge pull request #1121 from m-holger/dj

Handle default-constructed JSON objects
libqpdf/JSON.cc
@@ -191,7 +191,7 @@ JSON::JSON_blob::write(Pipeline* p, size_t) const @@ -191,7 +191,7 @@ JSON::JSON_blob::write(Pipeline* p, size_t) const
191 void 191 void
192 JSON::write(Pipeline* p, size_t depth) const 192 JSON::write(Pipeline* p, size_t depth) const
193 { 193 {
194 - if (nullptr == m->value) { 194 + if (!m) {
195 *p << "null"; 195 *p << "null";
196 } else { 196 } else {
197 m->value->write(p, depth); 197 m->value->write(p, depth);
@@ -201,6 +201,9 @@ JSON::write(Pipeline* p, size_t depth) const @@ -201,6 +201,9 @@ JSON::write(Pipeline* p, size_t depth) const
201 std::string 201 std::string
202 JSON::unparse() const 202 JSON::unparse() const
203 { 203 {
  204 + if (!m) {
  205 + return "null";
  206 + }
204 std::string s; 207 std::string s;
205 Pl_String p("unparse", nullptr, s); 208 Pl_String p("unparse", nullptr, s);
206 write(&p, 0); 209 write(&p, 0);
@@ -275,8 +278,8 @@ JSON::makeDictionary() @@ -275,8 +278,8 @@ JSON::makeDictionary()
275 JSON 278 JSON
276 JSON::addDictionaryMember(std::string const& key, JSON const& val) 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 } else { 283 } else {
281 throw std::runtime_error("JSON::addDictionaryMember called on non-dictionary"); 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,15 +288,11 @@ JSON::addDictionaryMember(std::string const&amp; key, JSON const&amp; val)
285 bool 288 bool
286 JSON::checkDictionaryKeySeen(std::string const& key) 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 JSON 298 JSON
@@ -305,16 +304,16 @@ JSON::makeArray() @@ -305,16 +304,16 @@ JSON::makeArray()
305 JSON 304 JSON
306 JSON::addArrayElement(JSON const& val) 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 JSON 319 JSON
@@ -362,19 +361,19 @@ JSON::makeBlob(std::function&lt;void(Pipeline*)&gt; fn) @@ -362,19 +361,19 @@ JSON::makeBlob(std::function&lt;void(Pipeline*)&gt; fn)
362 bool 361 bool
363 JSON::isArray() const 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 bool 367 bool
369 JSON::isDictionary() const 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 bool 373 bool
375 JSON::getString(std::string& utf8) const 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 auto v = dynamic_cast<JSON_string const*>(m->value.get()); 377 auto v = dynamic_cast<JSON_string const*>(m->value.get());
379 utf8 = v->utf8; 378 utf8 = v->utf8;
380 return true; 379 return true;
@@ -385,7 +384,7 @@ JSON::getString(std::string&amp; utf8) const @@ -385,7 +384,7 @@ JSON::getString(std::string&amp; utf8) const
385 bool 384 bool
386 JSON::getNumber(std::string& value) const 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 auto v = dynamic_cast<JSON_number const*>(m->value.get()); 388 auto v = dynamic_cast<JSON_number const*>(m->value.get());
390 value = v->encoded; 389 value = v->encoded;
391 return true; 390 return true;
@@ -396,7 +395,7 @@ JSON::getNumber(std::string&amp; value) const @@ -396,7 +395,7 @@ JSON::getNumber(std::string&amp; value) const
396 bool 395 bool
397 JSON::getBool(bool& value) const 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 auto v = dynamic_cast<JSON_bool const*>(m->value.get()); 399 auto v = dynamic_cast<JSON_bool const*>(m->value.get());
401 value = v->value; 400 value = v->value;
402 return true; 401 return true;
@@ -407,13 +406,13 @@ JSON::getBool(bool&amp; value) const @@ -407,13 +406,13 @@ JSON::getBool(bool&amp; value) const
407 bool 406 bool
408 JSON::isNull() const 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 JSON 412 JSON
414 JSON::getDictItem(std::string const& key) const 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 if (auto it = v->members.find(key); it != v->members.end()) { 416 if (auto it = v->members.find(key); it != v->members.end()) {
418 return it->second; 417 return it->second;
419 } 418 }
@@ -424,39 +423,37 @@ JSON::getDictItem(std::string const&amp; key) const @@ -424,39 +423,37 @@ JSON::getDictItem(std::string const&amp; key) const
424 bool 423 bool
425 JSON::forEachDictItem(std::function<void(std::string const& key, JSON value)> fn) const 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 bool 435 bool
438 JSON::forEachArrayItem(std::function<void(JSON value)> fn) const 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 bool 447 bool
451 JSON::checkSchema(JSON schema, std::list<std::string>& errors) 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 bool 453 bool
457 JSON::checkSchema(JSON schema, unsigned long flags, std::list<std::string>& errors) 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 bool 459 bool
@@ -1374,23 +1371,27 @@ JSON::parse(std::string const&amp; s) @@ -1374,23 +1371,27 @@ JSON::parse(std::string const&amp; s)
1374 void 1371 void
1375 JSON::setStart(qpdf_offset_t start) 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 void 1379 void
1381 JSON::setEnd(qpdf_offset_t end) 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 qpdf_offset_t 1387 qpdf_offset_t
1387 JSON::getStart() const 1388 JSON::getStart() const
1388 { 1389 {
1389 - return m->start; 1390 + return m ? m->start : 0;
1390 } 1391 }
1391 1392
1392 qpdf_offset_t 1393 qpdf_offset_t
1393 JSON::getEnd() const 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,8 +2,10 @@
2 2
3 #include <qpdf/JSON.hh> 3 #include <qpdf/JSON.hh>
4 #include <qpdf/Pipeline.hh> 4 #include <qpdf/Pipeline.hh>
  5 +#include <qpdf/Pl_String.hh>
5 #include <qpdf/QPDF.hh> 6 #include <qpdf/QPDF.hh>
6 #include <qpdf/QPDFObjectHandle.hh> 7 #include <qpdf/QPDFObjectHandle.hh>
  8 +
7 #include <iostream> 9 #include <iostream>
8 10
9 static void 11 static void
@@ -131,6 +133,51 @@ test_main() @@ -131,6 +133,51 @@ test_main()
131 " \"blob\": \"AQIDBAX//v38+w==\",\n" 133 " \"blob\": \"AQIDBAX//v38+w==\",\n"
132 " \"normal\": \"string\"\n" 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 static void 183 static void