aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4jsonobject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsruntime/qv4jsonobject.cpp')
-rw-r--r--src/qml/jsruntime/qv4jsonobject.cpp272
1 files changed, 136 insertions, 136 deletions
diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp
index 936c032fad..d78d09113a 100644
--- a/src/qml/jsruntime/qv4jsonobject.cpp
+++ b/src/qml/jsruntime/qv4jsonobject.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qv4jsonobject_p.h>
#include <qv4objectproto_p.h>
#include <qv4numberobject_p.h>
@@ -45,7 +9,6 @@
#include <qv4scopedvalue_p.h>
#include <qv4runtime_p.h>
#include <qv4variantobject_p.h>
-#include "qv4string_p.h"
#include "qv4jscall_p.h"
#include <qv4symbol_p.h>
@@ -125,12 +88,13 @@ enum {
bool JsonParser::eatSpace()
{
while (json < end) {
- if (*json > Space)
+ const char16_t ch = json->unicode();
+ if (ch > Space)
break;
- if (*json != Space &&
- *json != Tab &&
- *json != LineFeed &&
- *json != Return)
+ if (ch != Space &&
+ ch != Tab &&
+ ch != LineFeed &&
+ ch != Return)
break;
++json;
}
@@ -140,7 +104,7 @@ bool JsonParser::eatSpace()
QChar JsonParser::nextToken()
{
if (!eatSpace())
- return 0;
+ return u'\0';
QChar token = *json++;
switch (token.unicode()) {
case BeginArray:
@@ -150,10 +114,11 @@ QChar JsonParser::nextToken()
case EndArray:
case EndObject:
eatSpace();
+ break;
case Quote:
break;
default:
- token = 0;
+ token = u'\0';
break;
}
return token;
@@ -216,21 +181,21 @@ ReturnedValue JsonParser::parseObject()
ScopedObject o(scope, engine->newObject());
QChar token = nextToken();
- while (token == Quote) {
+ while (token.unicode() == Quote) {
if (!parseMember(o))
return Encode::undefined();
token = nextToken();
- if (token != ValueSeparator)
+ if (token.unicode() != ValueSeparator)
break;
token = nextToken();
- if (token == EndObject) {
+ if (token.unicode() == EndObject) {
lastError = QJsonParseError::MissingObject;
return Encode::undefined();
}
}
DEBUG << "end token=" << token;
- if (token != EndObject) {
+ if (token.unicode() != EndObject) {
lastError = QJsonParseError::UnterminatedObject;
return Encode::undefined();
}
@@ -253,7 +218,7 @@ bool JsonParser::parseMember(Object *o)
if (!parseString(&key))
return false;
QChar token = nextToken();
- if (token != NameSeparator) {
+ if (token.unicode() != NameSeparator) {
lastError = QJsonParseError::MissingNameSeparator;
return false;
}
@@ -292,7 +257,7 @@ ReturnedValue JsonParser::parseArray()
lastError = QJsonParseError::UnterminatedArray;
return Encode::undefined();
}
- if (*json == EndArray) {
+ if (json->unicode() == EndArray) {
nextToken();
} else {
uint index = 0;
@@ -302,9 +267,9 @@ ReturnedValue JsonParser::parseArray()
return Encode::undefined();
array->arraySet(index, val);
QChar token = nextToken();
- if (token == EndArray)
+ if (token.unicode() == EndArray)
break;
- else if (token != ValueSeparator) {
+ else if (token.unicode() != ValueSeparator) {
if (!eatSpace())
lastError = QJsonParseError::UnterminatedArray;
else
@@ -332,14 +297,14 @@ bool JsonParser::parseValue(Value *val)
BEGIN << "parse Value" << *json;
switch ((json++)->unicode()) {
- case 'n':
+ case u'n':
if (end - json < 3) {
lastError = QJsonParseError::IllegalValue;
return false;
}
- if (*json++ == 'u' &&
- *json++ == 'l' &&
- *json++ == 'l') {
+ if (*json++ == u'u' &&
+ *json++ == u'l' &&
+ *json++ == u'l') {
*val = Value::nullValue();
DEBUG << "value: null";
END;
@@ -347,14 +312,14 @@ bool JsonParser::parseValue(Value *val)
}
lastError = QJsonParseError::IllegalValue;
return false;
- case 't':
+ case u't':
if (end - json < 3) {
lastError = QJsonParseError::IllegalValue;
return false;
}
- if (*json++ == 'r' &&
- *json++ == 'u' &&
- *json++ == 'e') {
+ if (*json++ == u'r' &&
+ *json++ == u'u' &&
+ *json++ == u'e') {
*val = Value::fromBoolean(true);
DEBUG << "value: true";
END;
@@ -362,15 +327,15 @@ bool JsonParser::parseValue(Value *val)
}
lastError = QJsonParseError::IllegalValue;
return false;
- case 'f':
+ case u'f':
if (end - json < 4) {
lastError = QJsonParseError::IllegalValue;
return false;
}
- if (*json++ == 'a' &&
- *json++ == 'l' &&
- *json++ == 's' &&
- *json++ == 'e') {
+ if (*json++ == u'a' &&
+ *json++ == u'l' &&
+ *json++ == u's' &&
+ *json++ == u'e') {
*val = Value::fromBoolean(false);
DEBUG << "value: false";
END;
@@ -443,32 +408,32 @@ bool JsonParser::parseNumber(Value *val)
bool isInt = true;
// minus
- if (json < end && *json == '-')
+ if (json < end && *json == u'-')
++json;
// int = zero / ( digit1-9 *DIGIT )
- if (json < end && *json == '0') {
+ if (json < end && *json == u'0') {
++json;
} else {
- while (json < end && *json >= '0' && *json <= '9')
+ while (json < end && *json >= u'0' && *json <= u'9')
++json;
}
// frac = decimal-point 1*DIGIT
- if (json < end && *json == '.') {
+ if (json < end && *json == u'.') {
isInt = false;
++json;
- while (json < end && *json >= '0' && *json <= '9')
+ while (json < end && *json >= u'0' && *json <= u'9')
++json;
}
// exp = e [ minus / plus ] 1*DIGIT
- if (json < end && (*json == 'e' || *json == 'E')) {
+ if (json < end && (*json == u'e' || *json == u'E')) {
isInt = false;
++json;
- if (json < end && (*json == '-' || *json == '+'))
+ if (json < end && (*json == u'-' || *json == u'+'))
++json;
- while (json < end && *json >= '0' && *json <= '9')
+ while (json < end && *json >= u'0' && *json <= u'9')
++json;
}
@@ -526,12 +491,12 @@ static inline bool addHexDigit(QChar digit, uint *result)
{
ushort d = digit.unicode();
*result <<= 4;
- if (d >= '0' && d <= '9')
- *result |= (d - '0');
- else if (d >= 'a' && d <= 'f')
- *result |= (d - 'a') + 10;
- else if (d >= 'A' && d <= 'F')
- *result |= (d - 'A') + 10;
+ if (d >= u'0' && d <= u'9')
+ *result |= (d - u'0');
+ else if (d >= u'a' && d <= u'f')
+ *result |= (d - u'a') + 10;
+ else if (d >= u'A' && d <= u'F')
+ *result |= (d - u'A') + 10;
else
return false;
return true;
@@ -546,23 +511,23 @@ static inline bool scanEscapeSequence(const QChar *&json, const QChar *end, uint
DEBUG << "scan escape";
uint escaped = (json++)->unicode();
switch (escaped) {
- case '"':
+ case u'"':
*ch = '"'; break;
- case '\\':
+ case u'\\':
*ch = '\\'; break;
- case '/':
+ case u'/':
*ch = '/'; break;
- case 'b':
+ case u'b':
*ch = 0x8; break;
- case 'f':
+ case u'f':
*ch = 0xc; break;
- case 'n':
+ case u'n':
*ch = 0xa; break;
- case 'r':
+ case u'r':
*ch = 0xd; break;
- case 't':
+ case u't':
*ch = 0x9; break;
- case 'u': {
+ case u'u': {
*ch = 0;
if (json > end - 4)
return false;
@@ -585,9 +550,9 @@ bool JsonParser::parseString(QString *string)
BEGIN << "parse string stringPos=" << json;
while (json < end) {
- if (*json == '"')
+ if (*json == u'"')
break;
- else if (*json == '\\') {
+ else if (*json == u'\\') {
uint ch = 0;
if (!scanEscapeSequence(json, end, &ch)) {
lastError = QJsonParseError::IllegalEscapeSequence;
@@ -645,47 +610,69 @@ struct Stringify
QString makeMember(const QString &key, const Value &v);
};
+class [[nodiscard]] CallDepthAndCycleChecker
+{
+ Q_DISABLE_COPY_MOVE(CallDepthAndCycleChecker);
+
+public:
+ CallDepthAndCycleChecker(Stringify *stringify, Object *o)
+ : m_callDepthRecorder(stringify->v4)
+ {
+ if (stringify->stackContains(o)) {
+ stringify->v4->throwTypeError(
+ QStringLiteral("Cannot convert circular structure to JSON"));
+ }
+
+ stringify->v4->checkStackLimits();
+ }
+
+ bool foundProblem() const { return m_callDepthRecorder.ee->hasException; }
+
+private:
+ ExecutionEngineCallDepthRecorder<1> m_callDepthRecorder;
+};
+
static QString quote(const QString &str)
{
QString product;
- const int length = str.length();
+ const int length = str.size();
product.reserve(length + 2);
- product += QLatin1Char('"');
+ product += u'"';
for (int i = 0; i < length; ++i) {
QChar c = str.at(i);
switch (c.unicode()) {
- case '"':
+ case u'"':
product += QLatin1String("\\\"");
break;
- case '\\':
+ case u'\\':
product += QLatin1String("\\\\");
break;
- case '\b':
+ case u'\b':
product += QLatin1String("\\b");
break;
- case '\f':
+ case u'\f':
product += QLatin1String("\\f");
break;
- case '\n':
+ case u'\n':
product += QLatin1String("\\n");
break;
- case '\r':
+ case u'\r':
product += QLatin1String("\\r");
break;
- case '\t':
+ case u'\t':
product += QLatin1String("\\t");
break;
default:
if (c.unicode() <= 0x1f) {
product += QLatin1String("\\u00");
- product += (c.unicode() > 0xf ? QLatin1Char('1') : QLatin1Char('0')) +
+ product += (c.unicode() > 0xf ? u'1' : u'0') +
QLatin1Char("0123456789abcdef"[c.unicode() & 0xf]);
} else {
product += c;
}
}
}
- product += QLatin1Char('"');
+ product += u'"';
return product;
}
@@ -699,21 +686,31 @@ QString Stringify::Str(const QString &key, const Value &v)
ScopedString s(scope, v4->newString(QStringLiteral("toJSON")));
ScopedFunctionObject toJSON(scope, o->get(s));
if (!!toJSON) {
- JSCallData jsCallData(scope, 1);
- *jsCallData->thisObject = value;
- jsCallData->args[0] = v4->newString(key);
+ JSCallArguments jsCallData(scope, 1);
+ *jsCallData.thisObject = value;
+ jsCallData.args[0] = v4->newString(key);
value = toJSON->call(jsCallData);
+ if (v4->hasException)
+ return QString();
}
}
if (replacerFunction) {
- ScopedObject holder(scope, v4->newObject());
- holder->put(scope.engine->id_empty(), value);
- JSCallData jsCallData(scope, 2);
- jsCallData->args[0] = v4->newString(key);
- jsCallData->args[1] = value;
- *jsCallData->thisObject = holder;
+ JSCallArguments jsCallData(scope, 2);
+ jsCallData.args[0] = v4->newString(key);
+ jsCallData.args[1] = value;
+
+ if (stack.isEmpty()) {
+ ScopedObject holder(scope, v4->newObject());
+ holder->put(scope.engine->id_empty(), v);
+ *jsCallData.thisObject = holder;
+ } else {
+ *jsCallData.thisObject = stack.top();
+ }
+
value = replacerFunction->call(jsCallData);
+ if (v4->hasException)
+ return QString();
}
o = value->asReturnedValue();
@@ -760,9 +757,9 @@ QString Stringify::makeMember(const QString &key, const Value &v)
{
QString strP = Str(key, v);
if (!strP.isEmpty()) {
- QString member = quote(key) + QLatin1Char(':');
+ QString member = quote(key) + u':';
if (!gap.isEmpty())
- member += QLatin1Char(' ');
+ member += u' ';
member += strP;
return member;
}
@@ -771,10 +768,9 @@ QString Stringify::makeMember(const QString &key, const Value &v)
QString Stringify::JO(Object *o)
{
- if (stackContains(o)) {
- v4->throwTypeError();
+ CallDepthAndCycleChecker check(this, o);
+ if (check.foundProblem())
return QString();
- }
Scope scope(v4);
@@ -817,11 +813,11 @@ QString Stringify::JO(Object *o)
if (partial.isEmpty()) {
result = QStringLiteral("{}");
} else if (gap.isEmpty()) {
- result = QLatin1Char('{') + partial.join(QLatin1Char(',')) + QLatin1Char('}');
+ result = u'{' + partial.join(u',') + u'}';
} else {
QString separator = QLatin1String(",\n") + indent;
- result = QLatin1String("{\n") + indent + partial.join(separator) + QLatin1Char('\n')
- + stepback + QLatin1Char('}');
+ result = QLatin1String("{\n") + indent + partial.join(separator) + u'\n'
+ + stepback + u'}';
}
indent = stepback;
@@ -831,10 +827,9 @@ QString Stringify::JO(Object *o)
QString Stringify::JA(Object *a)
{
- if (stackContains(a)) {
- v4->throwTypeError();
+ CallDepthAndCycleChecker check(this, a);
+ if (check.foundProblem())
return QString();
- }
Scope scope(a->engine());
@@ -863,10 +858,10 @@ QString Stringify::JA(Object *a)
if (partial.isEmpty()) {
result = QStringLiteral("[]");
} else if (gap.isEmpty()) {
- result = QLatin1Char('[') + partial.join(QLatin1Char(',')) + QLatin1Char(']');
+ result = u'[' + partial.join(u',') + u']';
} else {
QString separator = QLatin1String(",\n") + indent;
- result = QLatin1String("[\n") + indent + partial.join(separator) + QLatin1Char('\n') + stepback + QLatin1Char(']');
+ result = QLatin1String("[\n") + indent + partial.join(separator) + u'\n' + stepback + u']';
}
indent = stepback;
@@ -896,7 +891,7 @@ ReturnedValue JsonObject::method_parse(const FunctionObject *b, const Value *, c
jtext = argv[0].toQString();
DEBUG << "parsing source = " << jtext;
- JsonParser parser(v4, jtext.constData(), jtext.length());
+ JsonParser parser(v4, jtext.constData(), jtext.size());
QJsonParseError error;
ReturnedValue result = parser.parse(&error);
if (error.error != QJsonParseError::NoError) {
@@ -916,9 +911,10 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value
if (o) {
stringify.replacerFunction = o->as<FunctionObject>();
if (o->isArrayObject()) {
- uint arrayLen = o->getLength();
+ int arrayLen = scope.engine->safeForAllocLength(o->getLength());
+ CHECK_EXCEPTION();
stringify.propertyList = static_cast<QV4::String *>(scope.alloc(arrayLen));
- for (uint i = 0; i < arrayLen; ++i) {
+ for (int i = 0; i < arrayLen; ++i) {
Value *v = stringify.propertyList + i;
*v = o->get(i);
if (v->as<NumberObject>() || v->as<StringObject>() || v->isNumber())
@@ -926,7 +922,7 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value
if (!v->isString()) {
v->setM(nullptr);
} else {
- for (uint j = 0; j <i; ++j) {
+ for (int j = 0; j <i; ++j) {
if (stringify.propertyList[j].m() == v->m()) {
v->setM(nullptr);
break;
@@ -944,7 +940,7 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value
s = so->d()->string;
if (s->isNumber()) {
- stringify.gap = QString(qMin(10, (int)s->toInteger()), ' ');
+ stringify.gap = QString(qMin(10, (int)s->toInteger()), u' ');
} else if (String *str = s->stringValue()) {
stringify.gap = str->toQString().left(10);
}
@@ -952,7 +948,7 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value
ScopedValue arg0(scope, argc ? argv[0] : Value::undefinedValue());
QString result = stringify.Str(QString(), arg0);
- if (result.isEmpty() || scope.engine->hasException)
+ if (result.isEmpty() || scope.hasException())
RETURN_UNDEFINED();
return Encode(scope.engine->newString(result));
}
@@ -992,12 +988,16 @@ QJsonValue JsonObject::toJsonValue(const Value &value, V4ObjectSet &visitedObjec
Q_ASSERT(value.isObject());
Scope scope(value.as<Object>()->engine());
- ScopedArrayObject a(scope, value);
- if (a)
+ if (ScopedArrayObject a{ scope, value }) {
return toJsonArray(a, visitedObjects);
- ScopedObject o(scope, value);
- if (o)
+ } else if (Scoped<QV4::Sequence> a{ scope, value }) {
+ return toJsonArray(a, visitedObjects);
+ } else if (Scoped<QmlListWrapper> lw{ scope, value }) {
+ return toJsonArray(lw, visitedObjects);
+ } else if (ScopedObject o{ scope, value }) {
return toJsonObject(o, visitedObjects);
+ }
+
return QJsonValue(value.toQString());
}
@@ -1062,7 +1062,7 @@ QV4::ReturnedValue JsonObject::fromJsonArray(ExecutionEngine *engine, const QJso
return a.asReturnedValue();
}
-QJsonArray JsonObject::toJsonArray(const ArrayObject *a, V4ObjectSet &visitedObjects)
+QJsonArray JsonObject::toJsonArray(const Object *a, V4ObjectSet &visitedObjects)
{
QJsonArray result;
if (!a)