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

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