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 | |
57 | using namespace QV4; |
58 | |
59 | //#define PARSER_DEBUG |
60 | #ifdef PARSER_DEBUG |
61 | static 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 | |
72 | DEFINE_OBJECT_VTABLE(JsonObject); |
73 | |
74 | static const int nestingLimit = 1024; |
75 | |
76 | |
77 | JsonParser::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 | |
87 | begin-array = ws %x5B ws ; [ left square bracket |
88 | |
89 | begin-object = ws %x7B ws ; { left curly bracket |
90 | |
91 | end-array = ws %x5D ws ; ] right square bracket |
92 | |
93 | end-object = ws %x7D ws ; } right curly bracket |
94 | |
95 | name-separator = ws %x3A ws ; : colon |
96 | |
97 | value-separator = ws %x2C ws ; , comma |
98 | |
99 | Insignificant whitespace is allowed before or after any of the six |
100 | structural characters. |
101 | |
102 | ws = *( |
103 | %x20 / ; Space |
104 | %x09 / ; Horizontal tab |
105 | %x0A / ; Line feed or New line |
106 | %x0D ; Carriage return |
107 | ) |
108 | |
109 | */ |
110 | |
111 | enum { |
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 | |
125 | bool 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 | |
140 | QChar 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 | */ |
165 | ReturnedValue 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 | |
206 | ReturnedValue 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 | */ |
247 | bool 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 | */ |
280 | ReturnedValue 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 | /* |
326 | value = false / null / true / object / array / number / string |
327 | |
328 | */ |
329 | |
330 | bool 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 | |
438 | bool 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 | */ |
525 | static 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 | |
540 | static 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 | |
583 | bool 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 | |
622 | struct 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 | |
648 | static 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 | |
692 | QString 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 | |
763 | QString 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 | |
776 | QString 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 | |
836 | QString 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 | |
882 | void 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 | |
895 | ReturnedValue 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 | |
914 | ReturnedValue 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 | |
966 | ReturnedValue 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 | |
984 | QJsonValue 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 | |
1008 | QV4::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 | |
1021 | QJsonObject 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 | |
1056 | QV4::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 | |
1069 | QJsonArray 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 | |