1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:dataparser
4#include <qv4jsonobject_p.h>
5#include <qv4objectproto_p.h>
6#include <qv4numberobject_p.h>
7#include <qv4stringobject_p.h>
8#include <qv4booleanobject_p.h>
9#include <qv4objectiterator_p.h>
10#include <qv4scopedvalue_p.h>
11#include <qv4runtime_p.h>
12#include <qv4variantobject_p.h>
13#include "qv4jscall_p.h"
14#include <qv4symbol_p.h>
15
16#include <qstack.h>
17#include <qstringlist.h>
18
19#include <wtf/MathExtras.h>
20
21using namespace QV4;
22
23//#define PARSER_DEBUG
24#ifdef PARSER_DEBUG
25static int indent = 0;
26#define BEGIN qDebug() << QByteArray(4*indent++, ' ').constData()
27#define END --indent
28#define DEBUG qDebug() << QByteArray(4*indent, ' ').constData()
29#else
30#define BEGIN if (1) ; else qDebug()
31#define END do {} while (0)
32#define DEBUG if (1) ; else qDebug()
33#endif
34
35
36DEFINE_OBJECT_VTABLE(JsonObject);
37
38static const int nestingLimit = 1024;
39
40
41JsonParser::JsonParser(ExecutionEngine *engine, const QChar *json, int length)
42 : engine(engine), head(json), json(json), nestingLevel(0), lastError(QJsonParseError::NoError)
43{
44 end = json + length;
45}
46
47
48
49/*
50
51begin-array = ws %x5B ws ; [ left square bracket
52
53begin-object = ws %x7B ws ; { left curly bracket
54
55end-array = ws %x5D ws ; ] right square bracket
56
57end-object = ws %x7D ws ; } right curly bracket
58
59name-separator = ws %x3A ws ; : colon
60
61value-separator = ws %x2C ws ; , comma
62
63Insignificant whitespace is allowed before or after any of the six
64structural characters.
65
66ws = *(
67 %x20 / ; Space
68 %x09 / ; Horizontal tab
69 %x0A / ; Line feed or New line
70 %x0D ; Carriage return
71 )
72
73*/
74
75enum {
76 Space = 0x20,
77 Tab = 0x09,
78 LineFeed = 0x0a,
79 Return = 0x0d,
80 BeginArray = 0x5b,
81 BeginObject = 0x7b,
82 EndArray = 0x5d,
83 EndObject = 0x7d,
84 NameSeparator = 0x3a,
85 ValueSeparator = 0x2c,
86 Quote = 0x22
87};
88
89bool JsonParser::eatSpace()
90{
91 while (json < end) {
92 const char16_t ch = json->unicode();
93 if (ch > Space)
94 break;
95 if (ch != Space &&
96 ch != Tab &&
97 ch != LineFeed &&
98 ch != Return)
99 break;
100 ++json;
101 }
102 return (json < end);
103}
104
105QChar JsonParser::nextToken()
106{
107 if (!eatSpace())
108 return u'\0';
109 QChar token = *json++;
110 switch (token.unicode()) {
111 case BeginArray:
112 case BeginObject:
113 case NameSeparator:
114 case ValueSeparator:
115 case EndArray:
116 case EndObject:
117 eatSpace();
118 break;
119 case Quote:
120 break;
121 default:
122 token = u'\0';
123 break;
124 }
125 return token;
126}
127
128/*
129 JSON-text = object / array
130*/
131ReturnedValue JsonParser::parse(QJsonParseError *error)
132{
133#ifdef PARSER_DEBUG
134 indent = 0;
135 qDebug() << ">>>>> parser begin";
136#endif
137
138 eatSpace();
139
140 Scope scope(engine);
141 ScopedValue v(scope);
142 if (!parseValue(val: v)) {
143#ifdef PARSER_DEBUG
144 qDebug() << ">>>>> parser error";
145#endif
146 if (lastError == QJsonParseError::NoError)
147 lastError = QJsonParseError::IllegalValue;
148 error->offset = json - head;
149 error->error = lastError;
150 return Encode::undefined();
151 }
152
153 // some input left...
154 if (eatSpace()) {
155 lastError = QJsonParseError::IllegalValue;
156 error->offset = json - head;
157 error->error = lastError;
158 return Encode::undefined();
159 }
160
161 END;
162 error->offset = 0;
163 error->error = QJsonParseError::NoError;
164 return v->asReturnedValue();
165}
166
167/*
168 object = begin-object [ member *( value-separator member ) ]
169 end-object
170*/
171
172ReturnedValue JsonParser::parseObject()
173{
174 if (++nestingLevel > nestingLimit) {
175 lastError = QJsonParseError::DeepNesting;
176 return Encode::undefined();
177 }
178
179 BEGIN << "parseObject pos=" << json;
180 Scope scope(engine);
181
182 ScopedObject o(scope, engine->newObject());
183
184 QChar token = nextToken();
185 while (token.unicode() == Quote) {
186 if (!parseMember(o))
187 return Encode::undefined();
188 token = nextToken();
189 if (token.unicode() != ValueSeparator)
190 break;
191 token = nextToken();
192 if (token.unicode() == EndObject) {
193 lastError = QJsonParseError::MissingObject;
194 return Encode::undefined();
195 }
196 }
197
198 DEBUG << "end token=" << token;
199 if (token.unicode() != EndObject) {
200 lastError = QJsonParseError::UnterminatedObject;
201 return Encode::undefined();
202 }
203
204 END;
205
206 --nestingLevel;
207 return o.asReturnedValue();
208}
209
210/*
211 member = string name-separator value
212*/
213bool JsonParser::parseMember(Object *o)
214{
215 BEGIN << "parseMember";
216 Scope scope(engine);
217
218 QString key;
219 if (!parseString(string: &key))
220 return false;
221 QChar token = nextToken();
222 if (token.unicode() != NameSeparator) {
223 lastError = QJsonParseError::MissingNameSeparator;
224 return false;
225 }
226 ScopedValue val(scope);
227 if (!parseValue(val))
228 return false;
229
230 ScopedString s(scope, engine->newString(s: key));
231 PropertyKey skey = s->toPropertyKey();
232 if (skey.isArrayIndex()) {
233 o->put(idx: skey.asArrayIndex(), v: val);
234 } else {
235 // avoid trouble with properties named __proto__
236 o->insertMember(s, v: val);
237 }
238
239 END;
240 return true;
241}
242
243/*
244 array = begin-array [ value *( value-separator value ) ] end-array
245*/
246ReturnedValue JsonParser::parseArray()
247{
248 Scope scope(engine);
249 BEGIN << "parseArray";
250 ScopedArrayObject array(scope, engine->newArrayObject());
251
252 if (++nestingLevel > nestingLimit) {
253 lastError = QJsonParseError::DeepNesting;
254 return Encode::undefined();
255 }
256
257 if (!eatSpace()) {
258 lastError = QJsonParseError::UnterminatedArray;
259 return Encode::undefined();
260 }
261 if (json->unicode() == EndArray) {
262 nextToken();
263 } else {
264 uint index = 0;
265 while (1) {
266 ScopedValue val(scope);
267 if (!parseValue(val))
268 return Encode::undefined();
269 array->arraySet(index, value: val);
270 QChar token = nextToken();
271 if (token.unicode() == EndArray)
272 break;
273 else if (token.unicode() != ValueSeparator) {
274 if (!eatSpace())
275 lastError = QJsonParseError::UnterminatedArray;
276 else
277 lastError = QJsonParseError::MissingValueSeparator;
278 return Encode::undefined();
279 }
280 ++index;
281 }
282 }
283
284 DEBUG << "size =" << array->getLength();
285 END;
286
287 --nestingLevel;
288 return array.asReturnedValue();
289}
290
291/*
292value = false / null / true / object / array / number / string
293
294*/
295
296bool JsonParser::parseValue(Value *val)
297{
298 BEGIN << "parse Value" << *json;
299
300 switch ((json++)->unicode()) {
301 case u'n':
302 if (end - json < 3) {
303 lastError = QJsonParseError::IllegalValue;
304 return false;
305 }
306 if (*json++ == u'u' &&
307 *json++ == u'l' &&
308 *json++ == u'l') {
309 *val = Value::nullValue();
310 DEBUG << "value: null";
311 END;
312 return true;
313 }
314 lastError = QJsonParseError::IllegalValue;
315 return false;
316 case u't':
317 if (end - json < 3) {
318 lastError = QJsonParseError::IllegalValue;
319 return false;
320 }
321 if (*json++ == u'r' &&
322 *json++ == u'u' &&
323 *json++ == u'e') {
324 *val = Value::fromBoolean(b: true);
325 DEBUG << "value: true";
326 END;
327 return true;
328 }
329 lastError = QJsonParseError::IllegalValue;
330 return false;
331 case u'f':
332 if (end - json < 4) {
333 lastError = QJsonParseError::IllegalValue;
334 return false;
335 }
336 if (*json++ == u'a' &&
337 *json++ == u'l' &&
338 *json++ == u's' &&
339 *json++ == u'e') {
340 *val = Value::fromBoolean(b: false);
341 DEBUG << "value: false";
342 END;
343 return true;
344 }
345 lastError = QJsonParseError::IllegalValue;
346 return false;
347 case Quote: {
348 QString value;
349 if (!parseString(string: &value))
350 return false;
351 DEBUG << "value: string";
352 END;
353 *val = Value::fromHeapObject(m: engine->newString(s: value));
354 return true;
355 }
356 case BeginArray: {
357 *val = parseArray();
358 if (val->isUndefined())
359 return false;
360 DEBUG << "value: array";
361 END;
362 return true;
363 }
364 case BeginObject: {
365 *val = parseObject();
366 if (val->isUndefined())
367 return false;
368 DEBUG << "value: object";
369 END;
370 return true;
371 }
372 case EndArray:
373 lastError = QJsonParseError::MissingObject;
374 return false;
375 default:
376 --json;
377 if (!parseNumber(val))
378 return false;
379 DEBUG << "value: number";
380 END;
381 }
382
383 return true;
384}
385
386
387
388
389
390/*
391 number = [ minus ] int [ frac ] [ exp ]
392 decimal-point = %x2E ; .
393 digit1-9 = %x31-39 ; 1-9
394 e = %x65 / %x45 ; e E
395 exp = e [ minus / plus ] 1*DIGIT
396 frac = decimal-point 1*DIGIT
397 int = zero / ( digit1-9 *DIGIT )
398 minus = %x2D ; -
399 plus = %x2B ; +
400 zero = %x30 ; 0
401
402*/
403
404bool JsonParser::parseNumber(Value *val)
405{
406 BEGIN << "parseNumber" << *json;
407
408 const QChar *start = json;
409 bool isInt = true;
410
411 // minus
412 if (json < end && *json == u'-')
413 ++json;
414
415 // int = zero / ( digit1-9 *DIGIT )
416 if (json < end && *json == u'0') {
417 ++json;
418 } else {
419 while (json < end && *json >= u'0' && *json <= u'9')
420 ++json;
421 }
422
423 // frac = decimal-point 1*DIGIT
424 if (json < end && *json == u'.') {
425 isInt = false;
426 ++json;
427 while (json < end && *json >= u'0' && *json <= u'9')
428 ++json;
429 }
430
431 // exp = e [ minus / plus ] 1*DIGIT
432 if (json < end && (*json == u'e' || *json == u'E')) {
433 isInt = false;
434 ++json;
435 if (json < end && (*json == u'-' || *json == u'+'))
436 ++json;
437 while (json < end && *json >= u'0' && *json <= u'9')
438 ++json;
439 }
440
441 QString number(start, json - start);
442 DEBUG << "numberstring" << number;
443
444 if (isInt) {
445 bool ok;
446 int n = number.toInt(ok: &ok);
447 if (ok && n < (1<<25) && n > -(1<<25)) {
448 *val = Value::fromInt32(i: n);
449 END;
450 return true;
451 }
452 }
453
454 bool ok;
455 double d;
456 d = number.toDouble(ok: &ok);
457
458 if (!ok) {
459 lastError = QJsonParseError::IllegalNumber;
460 return false;
461 }
462
463 * val = Value::fromDouble(d);
464
465 END;
466 return true;
467}
468
469/*
470
471 string = quotation-mark *char quotation-mark
472
473 char = unescaped /
474 escape (
475 %x22 / ; " quotation mark U+0022
476 %x5C / ; \ reverse solidus U+005C
477 %x2F / ; / solidus U+002F
478 %x62 / ; b backspace U+0008
479 %x66 / ; f form feed U+000C
480 %x6E / ; n line feed U+000A
481 %x72 / ; r carriage return U+000D
482 %x74 / ; t tab U+0009
483 %x75 4HEXDIG ) ; uXXXX U+XXXX
484
485 escape = %x5C ; \
486
487 quotation-mark = %x22 ; "
488
489 unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
490 */
491static inline bool addHexDigit(QChar digit, uint *result)
492{
493 ushort d = digit.unicode();
494 *result <<= 4;
495 if (d >= u'0' && d <= u'9')
496 *result |= (d - u'0');
497 else if (d >= u'a' && d <= u'f')
498 *result |= (d - u'a') + 10;
499 else if (d >= u'A' && d <= u'F')
500 *result |= (d - u'A') + 10;
501 else
502 return false;
503 return true;
504}
505
506static inline bool scanEscapeSequence(const QChar *&json, const QChar *end, uint *ch)
507{
508 ++json;
509 if (json >= end)
510 return false;
511
512 DEBUG << "scan escape";
513 uint escaped = (json++)->unicode();
514 switch (escaped) {
515 case u'"':
516 *ch = '"'; break;
517 case u'\\':
518 *ch = '\\'; break;
519 case u'/':
520 *ch = '/'; break;
521 case u'b':
522 *ch = 0x8; break;
523 case u'f':
524 *ch = 0xc; break;
525 case u'n':
526 *ch = 0xa; break;
527 case u'r':
528 *ch = 0xd; break;
529 case u't':
530 *ch = 0x9; break;
531 case u'u': {
532 *ch = 0;
533 if (json > end - 4)
534 return false;
535 for (int i = 0; i < 4; ++i) {
536 if (!addHexDigit(digit: *json, result: ch))
537 return false;
538 ++json;
539 }
540 return true;
541 }
542 default:
543 return false;
544 }
545 return true;
546}
547
548
549bool JsonParser::parseString(QString *string)
550{
551 BEGIN << "parse string stringPos=" << json;
552
553 while (json < end) {
554 if (*json == u'"')
555 break;
556 else if (*json == u'\\') {
557 uint ch = 0;
558 if (!scanEscapeSequence(json, end, ch: &ch)) {
559 lastError = QJsonParseError::IllegalEscapeSequence;
560 return false;
561 }
562 if (QChar::requiresSurrogates(ucs4: ch)) {
563 *string += QChar(QChar::highSurrogate(ucs4: ch)) + QChar(QChar::lowSurrogate(ucs4: ch));
564 } else {
565 *string += QChar(ch);
566 }
567 } else {
568 if (json->unicode() <= 0x1f) {
569 lastError = QJsonParseError::IllegalEscapeSequence;
570 return false;
571 }
572 *string += *json;
573 ++json;
574 }
575 }
576 ++json;
577
578 if (json > end) {
579 lastError = QJsonParseError::UnterminatedString;
580 return false;
581 }
582
583 END;
584 return true;
585}
586
587namespace {
588struct Stringify
589{
590 ExecutionEngine *v4;
591 FunctionObject *replacerFunction;
592 QV4::String *propertyList;
593 int propertyListSize;
594 QString gap;
595 QString indent;
596 QStack<Object *> stack;
597
598 bool stackContains(Object *o) {
599 for (int i = 0; i < stack.size(); ++i)
600 if (stack.at(i)->d() == o->d())
601 return true;
602 return false;
603 }
604
605 Stringify(ExecutionEngine *e) : v4(e), replacerFunction(nullptr), propertyList(nullptr), propertyListSize(0) {}
606
607 QString Str(const QString &key, const Value &v);
608 QString JA(Object *a);
609 QString JO(Object *o);
610
611 QString makeMember(const QString &key, const Value &v);
612};
613
614class [[nodiscard]] CallDepthAndCycleChecker
615{
616 Q_DISABLE_COPY_MOVE(CallDepthAndCycleChecker);
617
618public:
619 CallDepthAndCycleChecker(Stringify *stringify, Object *o)
620 : m_callDepthRecorder(stringify->v4)
621 {
622 if (stringify->stackContains(o)) {
623 stringify->v4->throwTypeError(
624 QStringLiteral("Cannot convert circular structure to JSON"));
625 }
626
627 const bool hasOverflow = stringify->v4->checkStackLimits();
628 Q_UNUSED(hasOverflow); // No handling needed. Exception has been thrown already.
629 }
630
631 bool foundProblem() const { return m_callDepthRecorder.ee->hasException; }
632
633private:
634 ExecutionEngineCallDepthRecorder<1> m_callDepthRecorder;
635};
636
637QString quote(const QString &str)
638{
639 QString product;
640 const int length = str.size();
641 product.reserve(asize: length + 2);
642 product += u'"';
643 for (int i = 0; i < length; ++i) {
644 QChar c = str.at(i);
645 switch (c.unicode()) {
646 case u'"':
647 product += QLatin1String("\\\"");
648 break;
649 case u'\\':
650 product += QLatin1String("\\\\");
651 break;
652 case u'\b':
653 product += QLatin1String("\\b");
654 break;
655 case u'\f':
656 product += QLatin1String("\\f");
657 break;
658 case u'\n':
659 product += QLatin1String("\\n");
660 break;
661 case u'\r':
662 product += QLatin1String("\\r");
663 break;
664 case u'\t':
665 product += QLatin1String("\\t");
666 break;
667 default:
668 if (c.unicode() <= 0x1f) {
669 product += QLatin1String("\\u00");
670 product += (c.unicode() > 0xf ? u'1' : u'0') +
671 QLatin1Char("0123456789abcdef"[c.unicode() & 0xf]);
672 } else {
673 product += c;
674 }
675 }
676 }
677 product += u'"';
678 return product;
679}
680
681QString Stringify::Str(const QString &key, const Value &v)
682{
683 Scope scope(v4);
684
685 ScopedValue value(scope, v);
686 ScopedObject o(scope, value);
687 if (o) {
688 ScopedString s(scope, v4->newString(QStringLiteral("toJSON")));
689 ScopedFunctionObject toJSON(scope, o->get(name: s));
690 if (!!toJSON) {
691 JSCallArguments jsCallData(scope, 1);
692 *jsCallData.thisObject = value;
693 jsCallData.args[0] = v4->newString(s: key);
694 value = toJSON->call(data: jsCallData);
695 if (v4->hasException)
696 return QString();
697 }
698 }
699
700 if (replacerFunction) {
701 JSCallArguments jsCallData(scope, 2);
702 jsCallData.args[0] = v4->newString(s: key);
703 jsCallData.args[1] = value;
704
705 if (stack.isEmpty()) {
706 ScopedObject holder(scope, v4->newObject());
707 holder->put(name: scope.engine->id_empty(), v);
708 *jsCallData.thisObject = holder;
709 } else {
710 *jsCallData.thisObject = stack.top();
711 }
712
713 value = replacerFunction->call(data: jsCallData);
714 if (v4->hasException)
715 return QString();
716 }
717
718 o = value->asReturnedValue();
719 if (o) {
720 if (NumberObject *n = o->as<NumberObject>())
721 value = Encode(n->value());
722 else if (StringObject *so = o->as<StringObject>())
723 value = so->d()->string;
724 else if (BooleanObject *b = o->as<BooleanObject>())
725 value = Encode(b->value());
726 }
727
728 if (value->isNull())
729 return QStringLiteral("null");
730 if (value->isBoolean())
731 return value->booleanValue() ? QStringLiteral("true") : QStringLiteral("false");
732 if (value->isString())
733 return quote(str: value->stringValue()->toQString());
734
735 if (value->isNumber()) {
736 double d = value->toNumber();
737 return std::isfinite(x: d) ? value->toQString() : QStringLiteral("null");
738 }
739
740 if (const QV4::VariantObject *v = value->as<QV4::VariantObject>()) {
741 return quote(str: v->d()->data().toString());
742 }
743
744 o = value->asReturnedValue();
745 if (o) {
746 if (!o->as<FunctionObject>()) {
747 if (o->isArrayLike()) {
748 return JA(a: o.getPointer());
749 } else {
750 return JO(o);
751 }
752 }
753 }
754
755 return QString();
756}
757
758QString Stringify::makeMember(const QString &key, const Value &v)
759{
760 QString strP = Str(key, v);
761 if (!strP.isEmpty()) {
762 QString member = quote(str: key) + u':';
763 if (!gap.isEmpty())
764 member += u' ';
765 member += strP;
766 return member;
767 }
768 return QString();
769}
770
771QString Stringify::JO(Object *o)
772{
773 CallDepthAndCycleChecker check(this, o);
774 if (check.foundProblem())
775 return QString();
776
777 Scope scope(v4);
778
779 QString result;
780 stack.push(t: o);
781 QString stepback = indent;
782 indent += gap;
783
784 QStringList partial;
785 if (!propertyListSize) {
786 ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly);
787 ScopedValue name(scope);
788
789 ScopedValue val(scope);
790 while (1) {
791 name = it.nextPropertyNameAsString(value: val);
792 if (name->isNull())
793 break;
794 QString key = name->toQString();
795 QString member = makeMember(key, v: val);
796 if (!member.isEmpty())
797 partial += member;
798 }
799 } else {
800 ScopedValue v(scope);
801 for (int i = 0; i < propertyListSize; ++i) {
802 bool exists;
803 String *s = propertyList + i;
804 if (!s)
805 continue;
806 v = o->get(name: s, hasProperty: &exists);
807 if (!exists)
808 continue;
809 QString member = makeMember(key: s->toQString(), v);
810 if (!member.isEmpty())
811 partial += member;
812 }
813 }
814
815 if (partial.isEmpty()) {
816 result = QStringLiteral("{}");
817 } else if (gap.isEmpty()) {
818 result = u'{' + partial.join(sep: u',') + u'}';
819 } else {
820 QString separator = QLatin1String(",\n") + indent;
821 result = QLatin1String("{\n") + indent + partial.join(sep: separator) + u'\n'
822 + stepback + u'}';
823 }
824
825 indent = stepback;
826 stack.pop();
827 return result;
828}
829
830QString Stringify::JA(Object *a)
831{
832 CallDepthAndCycleChecker check(this, a);
833 if (check.foundProblem())
834 return QString();
835
836 Scope scope(a->engine());
837
838 QString result;
839 stack.push(t: a);
840 QString stepback = indent;
841 indent += gap;
842
843 QStringList partial;
844 uint len = a->getLength();
845 ScopedValue v(scope);
846 for (uint i = 0; i < len; ++i) {
847 bool exists;
848 v = a->get(idx: i, hasProperty: &exists);
849 if (!exists) {
850 partial += QStringLiteral("null");
851 continue;
852 }
853 QString strP = Str(key: QString::number(i), v);
854 if (!strP.isEmpty())
855 partial += strP;
856 else
857 partial += QStringLiteral("null");
858 }
859
860 if (partial.isEmpty()) {
861 result = QStringLiteral("[]");
862 } else if (gap.isEmpty()) {
863 result = u'[' + partial.join(sep: u',') + u']';
864 } else {
865 QString separator = QLatin1String(",\n") + indent;
866 result = QLatin1String("[\n") + indent + partial.join(sep: separator) + u'\n' + stepback + u']';
867 }
868
869 indent = stepback;
870 stack.pop();
871 return result;
872}
873
874} // namespace
875
876void Heap::JsonObject::init()
877{
878 Object::init();
879 Scope scope(internalClass->engine);
880 ScopedObject o(scope, this);
881
882 o->defineDefaultProperty(QStringLiteral("parse"), code: QV4::JsonObject::method_parse, argumentCount: 2);
883 o->defineDefaultProperty(QStringLiteral("stringify"), code: QV4::JsonObject::method_stringify, argumentCount: 3);
884 ScopedString json(scope, scope.engine->newString(QStringLiteral("JSON")));
885 o->defineReadonlyConfigurableProperty(name: scope.engine->symbol_toStringTag(), value: json);
886}
887
888
889ReturnedValue JsonObject::method_parse(const FunctionObject *b, const Value *, const Value *argv, int argc)
890{
891 ExecutionEngine *v4 = b->engine();
892 QString jtext;
893 if (argc > 0)
894 jtext = argv[0].toQString();
895
896 DEBUG << "parsing source = " << jtext;
897 JsonParser parser(v4, jtext.constData(), jtext.size());
898 QJsonParseError error;
899 ReturnedValue result = parser.parse(error: &error);
900 if (error.error != QJsonParseError::NoError) {
901 DEBUG << "parse error" << error.errorString();
902 RETURN_RESULT(v4->throwSyntaxError(QStringLiteral("JSON.parse: Parse error")));
903 }
904
905 return result;
906}
907
908ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value *, const Value *argv, int argc)
909{
910 Scope scope(b);
911 Stringify stringify(scope.engine);
912
913 ScopedObject o(scope, argc > 1 ? argv[1] : Value::undefinedValue());
914 if (o) {
915 stringify.replacerFunction = o->as<FunctionObject>();
916 if (o->isArrayObject()) {
917 int arrayLen = scope.engine->safeForAllocLength(len64: o->getLength());
918 CHECK_EXCEPTION();
919 stringify.propertyList = static_cast<QV4::String *>(scope.alloc(nValues: arrayLen));
920 for (int i = 0; i < arrayLen; ++i) {
921 Value *v = stringify.propertyList + i;
922 *v = o->get(idx: i);
923 if (v->as<NumberObject>() || v->as<StringObject>() || v->isNumber())
924 *v = v->toString(e: scope.engine);
925 if (!v->isString()) {
926 v->setM(nullptr);
927 } else {
928 for (int j = 0; j <i; ++j) {
929 if (stringify.propertyList[j].m() == v->m()) {
930 v->setM(nullptr);
931 break;
932 }
933 }
934 }
935 }
936 }
937 }
938
939 ScopedValue s(scope, argc > 2 ? argv[2] : Value::undefinedValue());
940 if (NumberObject *n = s->as<NumberObject>())
941 s = Encode(n->value());
942 else if (StringObject *so = s->as<StringObject>())
943 s = so->d()->string;
944
945 if (s->isNumber()) {
946 stringify.gap = QString(qMin(a: 10, b: (int)s->toInteger()), u' ');
947 } else if (String *str = s->stringValue()) {
948 stringify.gap = str->toQString().left(n: 10);
949 }
950
951
952 ScopedValue arg0(scope, argc ? argv[0] : Value::undefinedValue());
953 QString result = stringify.Str(key: QString(), v: arg0);
954 if (result.isEmpty() || scope.hasException())
955 RETURN_UNDEFINED();
956 return Encode(scope.engine->newString(s: result));
957}
958
959
960
961ReturnedValue JsonObject::fromJsonValue(ExecutionEngine *engine, const QJsonValue &value)
962{
963 if (value.isString())
964 return engine->newString(s: value.toString())->asReturnedValue();
965 else if (value.isDouble())
966 return Encode(value.toDouble());
967 else if (value.isBool())
968 return Encode(value.toBool());
969 else if (value.isArray())
970 return fromJsonArray(engine, array: value.toArray());
971 else if (value.isObject())
972 return fromJsonObject(engine, object: value.toObject());
973 else if (value.isNull())
974 return Encode::null();
975 else
976 return Encode::undefined();
977}
978
979QJsonValue JsonObject::toJsonValue(const Value &value, V4ObjectSet &visitedObjects)
980{
981 if (value.isNumber())
982 return QJsonValue(value.toNumber());
983 else if (value.isBoolean())
984 return QJsonValue((bool)value.booleanValue());
985 else if (value.isNull())
986 return QJsonValue(QJsonValue::Null);
987 else if (value.isUndefined())
988 return QJsonValue(QJsonValue::Undefined);
989 else if (String *s = value.stringValue())
990 return QJsonValue(s->toQString());
991
992 Q_ASSERT(value.isObject());
993 Scope scope(value.as<Object>()->engine());
994 if (ScopedArrayObject a{ scope, value }) {
995 return toJsonArray(o: a, visitedObjects);
996 } else if (Scoped<QV4::Sequence> a{ scope, value }) {
997 return toJsonArray(o: a, visitedObjects);
998 } else if (Scoped<QmlListWrapper> lw{ scope, value }) {
999 return toJsonArray(o: lw, visitedObjects);
1000 } else if (ScopedObject o{ scope, value }) {
1001 return toJsonObject(o, visitedObjects);
1002 }
1003
1004 return QJsonValue(value.toQString());
1005}
1006
1007QV4::ReturnedValue JsonObject::fromJsonObject(ExecutionEngine *engine, const QJsonObject &object)
1008{
1009 Scope scope(engine);
1010 ScopedObject o(scope, engine->newObject());
1011 ScopedString s(scope);
1012 ScopedValue v(scope);
1013 for (QJsonObject::const_iterator it = object.begin(), cend = object.end(); it != cend; ++it) {
1014 v = fromJsonValue(engine, value: it.value());
1015 o->put(name: (s = engine->newString(s: it.key())), v);
1016 }
1017 return o.asReturnedValue();
1018}
1019
1020QJsonObject JsonObject::toJsonObject(const Object *o, V4ObjectSet &visitedObjects)
1021{
1022 QJsonObject result;
1023 if (!o || o->as<FunctionObject>())
1024 return result;
1025
1026 Scope scope(o->engine());
1027
1028 if (visitedObjects.contains(value: ObjectItem(o))) {
1029 // Avoid recursion.
1030 // For compatibility with QVariant{List,Map} conversion, we return an
1031 // empty object (and no error is thrown).
1032 return result;
1033 }
1034
1035 visitedObjects.insert(value: ObjectItem(o));
1036
1037 ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly);
1038 ScopedValue name(scope);
1039 QV4::ScopedValue val(scope);
1040 while (1) {
1041 name = it.nextPropertyNameAsString(value: val);
1042 if (name->isNull())
1043 break;
1044
1045 QString key = name->toQStringNoThrow();
1046 if (!val->as<FunctionObject>())
1047 result.insert(key, value: toJsonValue(value: val, visitedObjects));
1048 }
1049
1050 visitedObjects.remove(value: ObjectItem(o));
1051
1052 return result;
1053}
1054
1055QV4::ReturnedValue JsonObject::fromJsonArray(ExecutionEngine *engine, const QJsonArray &array)
1056{
1057 Scope scope(engine);
1058 int size = array.size();
1059 ScopedArrayObject a(scope, engine->newArrayObject());
1060 a->arrayReserve(n: size);
1061 ScopedValue v(scope);
1062 for (int i = 0; i < size; i++)
1063 a->arrayPut(index: i, value: (v = fromJsonValue(engine, value: array.at(i))));
1064 a->setArrayLengthUnchecked(size);
1065 return a.asReturnedValue();
1066}
1067
1068QJsonArray JsonObject::toJsonArray(const Object *a, V4ObjectSet &visitedObjects)
1069{
1070 QJsonArray result;
1071 if (!a)
1072 return result;
1073
1074 Scope scope(a->engine());
1075
1076 if (visitedObjects.contains(value: ObjectItem(a))) {
1077 // Avoid recursion.
1078 // For compatibility with QVariant{List,Map} conversion, we return an
1079 // empty array (and no error is thrown).
1080 return result;
1081 }
1082
1083 visitedObjects.insert(value: ObjectItem(a));
1084
1085 ScopedValue v(scope);
1086 quint32 length = a->getLength();
1087 for (quint32 i = 0; i < length; ++i) {
1088 v = a->get(idx: i);
1089 if (v->as<FunctionObject>())
1090 v = Encode::null();
1091 result.append(value: toJsonValue(value: v, visitedObjects));
1092 }
1093
1094 visitedObjects.remove(value: ObjectItem(a));
1095
1096 return result;
1097}
1098

source code of qtdeclarative/src/qml/jsruntime/qv4jsonobject.cpp