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

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