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