Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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// 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
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,
82 EndArray = 0x5d,
83 EndObject = 0x7d,
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(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(&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(key));
231 PropertyKey skey = s->toPropertyKey();
232 if (skey.isArrayIndex()) {
233 o->put(skey.asArrayIndex(), val);
234 } else {
235 // avoid trouble with properties named __proto__
236 o->insertMember(s, 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, 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(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(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(&value))
350 return false;
351 DEBUG << "value: string";
352 END;
353 *val = Value::fromHeapObject(engine->newString(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);
447 if (ok && n < (1<<25) && n > -(1<<25)) {
448 *val = Value::fromInt32(n);
449 END;
450 return true;
451 }
452 }
453
454 bool ok;
455 double d;
456 d = number.toDouble(&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(*json, 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)) {
559 lastError = QJsonParseError::IllegalEscapeSequence;
560 return false;
561 }
562 if (QChar::requiresSurrogates(ch)) {
563 *string += QChar(QChar::highSurrogate(ch)) + QChar(QChar::lowSurrogate(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(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(s));
690 if (!!toJSON) {
691 JSCallArguments jsCallData(scope, 1);
692 *jsCallData.thisObject = value;
693 jsCallData.args[0] = v4->newString(key);
694 value = toJSON->call(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(key);
703 jsCallData.args[1] = value;
704
705 if (stack.isEmpty()) {
706 ScopedObject holder(scope, v4->newObject());
707 holder->put(scope.engine->id_empty(), v);
708 *jsCallData.thisObject = holder;
709 } else {
710 *jsCallData.thisObject = stack.top();
711 }
712
713 value = replacerFunction->call(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 (QV4::String *stringValue = value->stringValue())
733 return quote(stringValue->toQString());
734
735 if (value->isNumber()) {
736 double d = value->toNumber();
737 return std::isfinite(d) ? value->toQString() : QStringLiteral("null");
738 }
739
740 if (const QV4::VariantObject *v = value->as<QV4::VariantObject>()) {
741 return quote(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(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(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(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(val);
792 if (name->isNull())
793 break;
794 QString key = name->toQString();
795 QString member = makeMember(key, 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 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 = std::move(stepback);
824 stack.pop();
825 return result;
826}
827
828QString Stringify::JA(Object *a)
829{
830 CallDepthAndCycleChecker check(this, a);
831 if (check.foundProblem())
832 return QString();
833
834 Scope scope(a->engine());
835
836 QString result;
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 = std::move(stepback);
868 stack.pop();
869 return result;
870}
871
872} // namespace
873
874void Heap::JsonObject::init()
875{
876 Object::init();
877 Scope scope(internalClass->engine);
878 ScopedObject o(scope, this);
879
880 o->defineDefaultProperty(QStringLiteral("parse"), QV4::JsonObject::method_parse, 2);
881 o->defineDefaultProperty(QStringLiteral("stringify"), QV4::JsonObject::method_stringify, 3);
882 ScopedString json(scope, scope.engine->newString(QStringLiteral("JSON")));
883 o->defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), json);
884}
885
886
887ReturnedValue JsonObject::method_parse(const FunctionObject *b, const Value *, const Value *argv, int argc)
888{
889 ExecutionEngine *v4 = b->engine();
890 QString jtext;
891 if (argc > 0)
892 jtext = argv[0].toQString();
893
894 DEBUG << "parsing source = " << jtext;
895 JsonParser parser(v4, jtext.constData(), jtext.size());
896 QJsonParseError error;
897 ReturnedValue result = parser.parse(&error);
898 if (error.error != QJsonParseError::NoError) {
899 DEBUG << "parse error" << error.errorString();
900 RETURN_RESULT(v4->throwSyntaxError(QStringLiteral("JSON.parse: Parse error")));
901 }
902
903 return result;
904}
905
906ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value *, const Value *argv, int argc)
907{
908 Scope scope(b);
909 Stringify stringify(scope.engine);
910
911 ScopedObject o(scope, argc > 1 ? argv[1] : Value::undefinedValue());
912 if (o) {
913 stringify.replacerFunction = o->as<FunctionObject>();
914 if (o->isArrayObject()) {
915 int arrayLen = scope.engine->safeForAllocLength(o->getLength());
917 stringify.propertyList = static_cast<QV4::String *>(scope.constructUndefined(arrayLen));
918 for (int i = 0; i < arrayLen; ++i) {
919 Value *v = stringify.propertyList + i;
920 *v = o->get(i);
921 if (v->as<NumberObject>() || v->as<StringObject>() || v->isNumber())
922 *v = v->toString(scope.engine);
923 if (!v->isString()) {
924 v->setM(nullptr);
925 } else {
926 for (int j = 0; j <i; ++j) {
927 if (stringify.propertyList[j].m() == v->m()) {
928 v->setM(nullptr);
929 break;
930 }
931 }
932 }
933 }
934 }
935 }
936
937 ScopedValue s(scope, argc > 2 ? argv[2] : Value::undefinedValue());
938 if (NumberObject *n = s->as<NumberObject>())
939 s = Encode(n->value());
940 else if (StringObject *so = s->as<StringObject>())
941 s = so->d()->string;
942
943 if (s->isNumber()) {
944 stringify.gap = QString(qMin(10, (int)s->toInteger()), u' ');
945 } else if (String *str = s->stringValue()) {
946 stringify.gap = str->toQString().left(10);
947 }
948
949
950 ScopedValue arg0(scope, argc ? argv[0] : Value::undefinedValue());
951 QString result = stringify.Str(QString(), arg0);
952 if (result.isEmpty() || scope.hasException())
954 return Encode(scope.engine->newString(result));
955}
956
957
958
959ReturnedValue JsonObject::fromJsonValue(ExecutionEngine *engine, const QJsonValue &value)
960{
961 if (value.isString())
962 return engine->newString(value.toString())->asReturnedValue();
963 else if (value.isDouble())
964 return Encode(value.toDouble());
965 else if (value.isBool())
966 return Encode(value.toBool());
967 else if (value.isArray())
968 return fromJsonArray(engine, value.toArray());
969 else if (value.isObject())
970 return fromJsonObject(engine, value.toObject());
971 else if (value.isNull())
972 return Encode::null();
973 else
974 return Encode::undefined();
975}
976
977QJsonValue JsonObject::toJsonValue(const Value &value, V4ObjectSet &visitedObjects)
978{
979 if (value.isNumber())
980 return QJsonValue(value.toNumber());
981 else if (value.isBoolean())
982 return QJsonValue((bool)value.booleanValue());
983 else if (value.isNull())
984 return QJsonValue(QJsonValue::Null);
985 else if (value.isUndefined())
986 return QJsonValue(QJsonValue::Undefined);
987 else if (String *s = value.stringValue())
988 return QJsonValue(s->toQString());
989
990 Q_ASSERT(value.isObject());
991 Scope scope(value.as<Object>()->engine());
992 if (ScopedArrayObject a{ scope, value }) {
993 return toJsonArray(a, visitedObjects);
994 } else if (Scoped<QV4::Sequence> a{ scope, value }) {
995 return toJsonArray(a, visitedObjects);
996 } else if (Scoped<QmlListWrapper> lw{ scope, value }) {
997 return toJsonArray(lw, visitedObjects);
998 } else if (ScopedObject o{ scope, value }) {
999 return toJsonObject(o, visitedObjects);
1000 }
1001
1002 return QJsonValue(value.toQString());
1003}
1004
1005QV4::ReturnedValue JsonObject::fromJsonObject(ExecutionEngine *engine, const QJsonObject &object)
1006{
1007 Scope scope(engine);
1008 ScopedObject o(scope, engine->newObject());
1009 ScopedString s(scope);
1010 ScopedValue v(scope);
1011 for (QJsonObject::const_iterator it = object.begin(), cend = object.end(); it != cend; ++it) {
1012 v = fromJsonValue(engine, it.value());
1013 o->put((s = engine->newString(it.key())), v);
1014 }
1015 return o.asReturnedValue();
1016}
1017
1018QJsonObject JsonObject::toJsonObject(const Object *o, V4ObjectSet &visitedObjects)
1019{
1020 QJsonObject result;
1021 if (!o || o->as<FunctionObject>())
1022 return result;
1023
1024 Scope scope(o->engine());
1025
1026 if (visitedObjects.contains(ObjectItem(o))) {
1027 // Avoid recursion.
1028 // For compatibility with QVariant{List,Map} conversion, we return an
1029 // empty object (and no error is thrown).
1030 return result;
1031 }
1032
1033 visitedObjects.insert(ObjectItem(o));
1034
1035 ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly);
1036 ScopedValue name(scope);
1037 QV4::ScopedValue val(scope);
1038 while (1) {
1039 name = it.nextPropertyNameAsString(val);
1040 if (name->isNull())
1041 break;
1042
1043 QString key = name->toQStringNoThrow();
1044 if (!val->as<FunctionObject>())
1045 result.insert(key, toJsonValue(val, visitedObjects));
1046 }
1047
1048 visitedObjects.remove(ObjectItem(o));
1049
1050 return result;
1051}
1052
1053QV4::ReturnedValue JsonObject::fromJsonArray(ExecutionEngine *engine, const QJsonArray &array)
1054{
1055 Scope scope(engine);
1056 int size = array.size();
1057 ScopedArrayObject a(scope, engine->newArrayObject());
1058 a->arrayReserve(size);
1059 ScopedValue v(scope);
1060 for (int i = 0; i < size; i++)
1061 a->arrayPut(i, (v = fromJsonValue(engine, array.at(i))));
1062 a->setArrayLengthUnchecked(size);
1063 return a.asReturnedValue();
1064}
1065
1066QJsonArray JsonObject::toJsonArray(const Object *a, V4ObjectSet &visitedObjects)
1067{
1068 QJsonArray result;
1069 if (!a)
1070 return result;
1071
1072 Scope scope(a->engine());
1073
1074 if (visitedObjects.contains(ObjectItem(a))) {
1075 // Avoid recursion.
1076 // For compatibility with QVariant{List,Map} conversion, we return an
1077 // empty array (and no error is thrown).
1078 return result;
1079 }
1080
1081 visitedObjects.insert(ObjectItem(a));
1082
1083 ScopedValue v(scope);
1084 quint32 length = a->getLength();
1085 for (quint32 i = 0; i < length; ++i) {
1086 v = a->get(i);
1087 if (v->as<FunctionObject>())
1088 v = Encode::null();
1089 result.append(toJsonValue(v, visitedObjects));
1090 }
1091
1092 visitedObjects.remove(ObjectItem(a));
1093
1094 return result;
1095}
Definition qjsvalue.h:23
@ BeginObject
@ LineFeed
@ EndObject
@ BeginArray
@ ValueSeparator
@ Quote
@ Tab
@ Space
@ NameSeparator
@ EndArray
@ Return
#define END(function)
static bool scanEscapeSequence(const QChar *&json, const QChar *end, uint *ch)
static const int nestingLimit
DEFINE_OBJECT_VTABLE(JsonObject)
static bool addHexDigit(QChar digit, uint *result)
#define BEGIN
#define CHECK_EXCEPTION()
#define RETURN_UNDEFINED()
#define RETURN_RESULT(r)