Source code
Revision control
Copy as Markdown
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qjsmanagedvalue.h"
#include <QtQml/private/qv4engine_p.h>
#include <QtQml/qjsmanagedvalue.h>
#include <QtCore/qthread.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
#include <QtCore/qbuffer.h>
#include <QtCore/qvariant.h>
#include <QtCore/qpoint.h>
#include <QtCore/qdatastream.h>
#include <QtCore/qmetasequence.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qwaitcondition.h>
#include <QtCore/qthread.h>
#include <QtTest/qtest.h>
#include <memory>
void tst_QJSManagedValue::ctor_invalid()
{
QJSManagedValue v;
QCOMPARE(v.type(), QJSManagedValue::Undefined);
}
void tst_QJSManagedValue::ctor_undefinedWithEngine()
{
QJSEngine eng;
QJSManagedValue v(eng.toManagedValue(QVariant()));
QCOMPARE(v.type(), QJSManagedValue::Undefined);
}
void tst_QJSManagedValue::ctor_nullWithEngine()
{
QJSEngine eng;
QJSManagedValue v(eng.evaluate(QStringLiteral("null")), &eng);
QCOMPARE(v.type(), QJSManagedValue::Object);
QVERIFY(v.isNull());
}
void tst_QJSManagedValue::ctor_boolWithEngine()
{
QJSEngine eng;
QJSManagedValue v(eng.toManagedValue(false));
QCOMPARE(v.type(), QJSManagedValue::Boolean);
QCOMPARE(v.toBoolean(), false);
}
void tst_QJSManagedValue::ctor_intWithEngine()
{
QJSEngine eng;
QJSManagedValue v(eng.toManagedValue(int(1)));
QCOMPARE(v.type(), QJSManagedValue::Number);
QCOMPARE(v.toNumber(), 1.0);
}
void tst_QJSManagedValue::ctor_uintWithEngine()
{
QJSEngine eng;
QJSManagedValue v(eng.toManagedValue(uint(0x43211234)));
QCOMPARE(v.type(), QJSManagedValue::Number);
QCOMPARE(v.toNumber(), 0x43211234);
}
void tst_QJSManagedValue::ctor_floatWithEngine()
{
QJSEngine eng;
QJSManagedValue v(eng.toManagedValue(12345678910.5));
QCOMPARE(v.type(), QJSManagedValue::Number);
QCOMPARE(v.toNumber(), 12345678910.5);
}
void tst_QJSManagedValue::ctor_stringWithEngine()
{
QJSEngine eng;
QJSManagedValue v(eng.toManagedValue(QStringLiteral("ciao")));
QCOMPARE(v.type(), QJSManagedValue::String);
QCOMPARE(v.toString(), QStringLiteral("ciao"));
}
void tst_QJSManagedValue::ctor_copyAndAssignWithEngine()
{
QJSEngine eng;
// copy constructor, operator=
QJSManagedValue v(eng.toManagedValue(1.0));
QJSManagedValue v2(v.toJSValue(), &eng);
QCOMPARE(v2.strictlyEquals(v), true);
QJSManagedValue v3(v.toJSValue(), &eng);
QCOMPARE(v3.strictlyEquals(v), true);
QCOMPARE(v3.strictlyEquals(v2), true);
QJSManagedValue v4(eng.toManagedValue(2.0));
QCOMPARE(v4.strictlyEquals(v), false);
v3 = QJSManagedValue(v4.toJSValue(), &eng);
QCOMPARE(v3.strictlyEquals(v), false);
QCOMPARE(v3.strictlyEquals(v4), true);
v2 = QJSManagedValue(QJSPrimitiveValue(), &eng);
QCOMPARE(v2.strictlyEquals(v), false);
QCOMPARE(v.toNumber(), 1.0);
QJSManagedValue v5(v.toJSValue(), &eng);
QCOMPARE(v5.strictlyEquals(v), true);
v = QJSManagedValue(QJSPrimitiveValue(), &eng);
QCOMPARE(v5.strictlyEquals(v), false);
QCOMPARE(v5.toNumber(), 1.0);
}
void tst_QJSManagedValue::toString()
{
QJSEngine eng;
{
QJSManagedValue undefined(eng.toManagedValue(QVariant()));
QCOMPARE(undefined.toString(), QStringLiteral("undefined"));
QCOMPARE(qjsvalue_cast<QString>(undefined), QStringLiteral("undefined"));
}
{
QJSManagedValue null(eng.evaluate(QStringLiteral("null")), &eng);
QCOMPARE(null.toString(), QStringLiteral("null"));
QCOMPARE(qjsvalue_cast<QString>(null), QStringLiteral("null"));
}
{
QJSManagedValue falskt(eng.toManagedValue(false));
QCOMPARE(falskt.toString(), QStringLiteral("false"));
QCOMPARE(qjsvalue_cast<QString>(falskt), QStringLiteral("false"));
QJSManagedValue sant(eng.toManagedValue(true));
QCOMPARE(sant.toString(), QStringLiteral("true"));
QCOMPARE(qjsvalue_cast<QString>(sant), QStringLiteral("true"));
}
{
QJSManagedValue number(eng.toManagedValue(123));
QCOMPARE(number.toString(), QStringLiteral("123"));
QCOMPARE(qjsvalue_cast<QString>(number), QStringLiteral("123"));
}
{
QJSManagedValue number(eng.toManagedValue(6.37e-8));
QCOMPARE(number.toString(), QStringLiteral("6.37e-8"));
}
{
QJSManagedValue number(eng.toManagedValue(-6.37e-8));
QCOMPARE(number.toString(), QStringLiteral("-6.37e-8"));
QJSManagedValue str(eng.toManagedValue(QStringLiteral("ciao")));
QCOMPARE(str.toString(), QStringLiteral("ciao"));
QCOMPARE(qjsvalue_cast<QString>(str), QStringLiteral("ciao"));
}
{
QJSManagedValue object(eng.newObject(), &eng);
QCOMPARE(object.toString(), QStringLiteral("[object Object]"));
QCOMPARE(qjsvalue_cast<QString>(object), QStringLiteral("[object Object]"));
}
// toString() that throws exception
{
QVERIFY(!eng.hasError());
QJSManagedValue objectObject(eng.evaluate(QStringLiteral(
"(function(){"
" o = { };"
" o.toString = function() { throw new Error('toString'); };"
" return o;"
"})()")), &eng);
QCOMPARE(objectObject.type(), QJSManagedValue::Object);
QVERIFY(!objectObject.isNull());
QVERIFY(!eng.hasError());
QCOMPARE(objectObject.toString(), QStringLiteral("undefined"));
QVERIFY(eng.hasError());
QJSManagedValue errorObject(eng.catchError(), &eng);
QCOMPARE(errorObject.type(), QJSManagedValue::Object);
QVERIFY(!errorObject.isNull());
QCOMPARE(errorObject.toString(), QStringLiteral("Error: toString"));
QVERIFY(!eng.hasError());
}
{
QVERIFY(!eng.hasError());
QJSManagedValue objectObject(eng.evaluate(QStringLiteral(
"(function(){"
" var f = function() {};"
" f.prototype = Date;"
" return new f;"
"})()")), &eng);
QCOMPARE(objectObject.type(), QJSManagedValue::Object);
QVERIFY(!objectObject.isNull());
QVERIFY(!eng.hasError());
QCOMPARE(objectObject.toString(), QStringLiteral("undefined"));
QVERIFY(eng.hasError());
QJSManagedValue errorObject(eng.catchError(), &eng);
QCOMPARE(errorObject.type(), QJSManagedValue::Object);
QVERIFY(!errorObject.isNull());
QCOMPARE(errorObject.toString(), QStringLiteral("TypeError: Type error"));
QVERIFY(!eng.hasError());
}
QJSManagedValue inv(QJSPrimitiveValue(), &eng);
QCOMPARE(inv.toString(), QStringLiteral("undefined"));
// variant should use internal valueOf(), then fall back to QVariant::toString(),
// then fall back to "QVariant(typename)"
QJSManagedValue variant(eng.toManagedValue(QPoint(10, 20)));
QCOMPARE(variant.type(), QJSManagedValue::Object);
QCOMPARE(variant.toString(), QStringLiteral("QPoint(10, 20)"));
variant = eng.toManagedValue(QUrl());
QCOMPARE(variant.type(), QJSManagedValue::Object);
QVERIFY(variant.toString().isEmpty());
{
QJSManagedValue o(eng.newObject(), &eng);
o.setProperty(QStringLiteral("test"), QJSValue(42));
QCOMPARE(o.toString(), QStringLiteral("[object Object]"));
}
{
QJSManagedValue o(eng.newArray(), &eng);
o.setProperty(0, 1);
o.setProperty(1, 2);
o.setProperty(2, 3);
QCOMPARE(o.toString(), QStringLiteral("1,2,3"));
}
{
QByteArray hello = QByteArrayLiteral("Hello World");
QJSManagedValue jsValue(eng.toManagedValue(hello));
QCOMPARE(jsValue.toString(), QString::fromUtf8(hello));
}
}
void tst_QJSManagedValue::toNumber()
{
QJSEngine eng;
QJSManagedValue undefined(eng.toManagedValue(QVariant()));
QCOMPARE(qIsNaN(undefined.toNumber()), true);
QCOMPARE(qIsNaN(qjsvalue_cast<qreal>(undefined)), true);
QJSManagedValue null(eng.evaluate(QStringLiteral("null")), &eng);
QCOMPARE(null.toNumber(), 0.0);
QCOMPARE(qjsvalue_cast<qreal>(null), 0.0);
{
QJSManagedValue falskt(eng.toManagedValue(false));
QCOMPARE(falskt.toNumber(), 0.0);
QCOMPARE(qjsvalue_cast<qreal>(falskt), 0.0);
QJSManagedValue sant(eng.toManagedValue(true));
QCOMPARE(sant.toNumber(), 1.0);
QCOMPARE(qjsvalue_cast<qreal>(sant), 1.0);
QJSManagedValue number(eng.toManagedValue(123.0));
QCOMPARE(number.toNumber(), 123.0);
QCOMPARE(qjsvalue_cast<qreal>(number), 123.0);
QJSManagedValue str(eng.toManagedValue(QStringLiteral("ciao")));
QCOMPARE(qIsNaN(str.toNumber()), true);
QCOMPARE(qIsNaN(qjsvalue_cast<qreal>(str)), true);
QJSManagedValue str2(eng.toManagedValue(QStringLiteral("123")));
QCOMPARE(str2.toNumber(), 123.0);
QCOMPARE(qjsvalue_cast<qreal>(str2), 123.0);
}
QJSManagedValue object(eng.newObject(), &eng);
QCOMPARE(qIsNaN(object.toNumber()), true);
QCOMPARE(qIsNaN(qjsvalue_cast<qreal>(object)), true);
QJSManagedValue inv(QJSPrimitiveValue(), &eng);
QVERIFY(qIsNaN(inv.toNumber()));
QVERIFY(qIsNaN(qjsvalue_cast<qreal>(inv)));
// V2 constructors
{
QJSManagedValue falskt(QJSPrimitiveValue(false), &eng);
QCOMPARE(falskt.toNumber(), 0.0);
QCOMPARE(qjsvalue_cast<qreal>(falskt), 0.0);
QJSManagedValue sant(QJSPrimitiveValue(true), &eng);
QCOMPARE(sant.toNumber(), 1.0);
QCOMPARE(qjsvalue_cast<qreal>(sant), 1.0);
QJSManagedValue number(QJSPrimitiveValue(123.0), &eng);
QCOMPARE(number.toNumber(), 123.0);
QCOMPARE(qjsvalue_cast<qreal>(number), 123.0);
QJSManagedValue number2(QJSPrimitiveValue(int(0x43211234)), &eng);
QCOMPARE(number2.toNumber(), 1126240820.0);
QJSManagedValue str(QStringLiteral("ciao"), &eng);
QCOMPARE(qIsNaN(str.toNumber()), true);
QCOMPARE(qIsNaN(qjsvalue_cast<qreal>(str)), true);
QJSManagedValue str2(QStringLiteral("123"), &eng);
QCOMPARE(str2.toNumber(), 123.0);
QCOMPARE(qjsvalue_cast<qreal>(str2), 123.0);
}
}
void tst_QJSManagedValue::toBoolean()
{
QJSEngine eng;
QJSManagedValue undefined(eng.toManagedValue(QVariant()));
QCOMPARE(undefined.type(), QJSManagedValue::Undefined);
QCOMPARE(undefined.toBoolean(), false);
QCOMPARE(qjsvalue_cast<bool>(undefined), false);
QJSManagedValue null(eng.evaluate(QStringLiteral("null")), &eng);
QCOMPARE(null.type(), QJSManagedValue::Object);
QVERIFY(null.isNull());
QCOMPARE(null.toBoolean(), false);
QCOMPARE(qjsvalue_cast<bool>(null), false);
{
QJSManagedValue falskt = eng.toManagedValue(false);
QCOMPARE(falskt.type(), QJSManagedValue::Boolean);
QCOMPARE(falskt.toBoolean(), false);
QCOMPARE(qjsvalue_cast<bool>(falskt), false);
QJSManagedValue sant(eng.toManagedValue(true));
QCOMPARE(sant.type(), QJSManagedValue::Boolean);
QCOMPARE(sant.toBoolean(), true);
QCOMPARE(qjsvalue_cast<bool>(sant), true);
QJSManagedValue number(eng.toManagedValue(0.0));
QCOMPARE(number.toBoolean(), false);
QCOMPARE(qjsvalue_cast<bool>(number), false);
QJSManagedValue number2(eng.toManagedValue(qQNaN()));
QCOMPARE(number2.toBoolean(), false);
QCOMPARE(qjsvalue_cast<bool>(number2), false);
QJSManagedValue number3(eng.toManagedValue(123.0));
QCOMPARE(number3.toBoolean(), true);
QCOMPARE(qjsvalue_cast<bool>(number3), true);
QJSManagedValue number4(eng.toManagedValue(-456.0));
QCOMPARE(number4.toBoolean(), true);
QCOMPARE(qjsvalue_cast<bool>(number4), true);
QJSManagedValue str(eng.toManagedValue(QStringLiteral("")));
QCOMPARE(str.toBoolean(), false);
QCOMPARE(qjsvalue_cast<bool>(str), false);
QJSManagedValue str2(eng.toManagedValue(QStringLiteral("123")));
QCOMPARE(str2.toBoolean(), true);
QCOMPARE(qjsvalue_cast<bool>(str2), true);
}
QJSManagedValue object(eng.newObject(), &eng);
QCOMPARE(object.toBoolean(), true);
QCOMPARE(qjsvalue_cast<bool>(object), true);
QJSManagedValue inv(QJSPrimitiveValue(), &eng);
QCOMPARE(inv.toBoolean(), false);
QCOMPARE(qjsvalue_cast<bool>(inv), false);
// V2 constructors
{
QJSManagedValue falskt(QJSPrimitiveValue(false), &eng);
QCOMPARE(falskt.toBoolean(), false);
QCOMPARE(qjsvalue_cast<bool>(falskt), false);
QJSManagedValue sant(QJSPrimitiveValue(true), &eng);
QCOMPARE(sant.toBoolean(), true);
QCOMPARE(qjsvalue_cast<bool>(sant), true);
QJSManagedValue number(QJSPrimitiveValue(0.0), &eng);
QCOMPARE(number.toBoolean(), false);
QCOMPARE(qjsvalue_cast<bool>(number), false);
QJSManagedValue number2(QJSPrimitiveValue(qQNaN()), &eng);
QCOMPARE(number2.toBoolean(), false);
QCOMPARE(qjsvalue_cast<bool>(number2), false);
QJSManagedValue number3(QJSPrimitiveValue(123.0), &eng);
QCOMPARE(number3.toBoolean(), true);
QCOMPARE(qjsvalue_cast<bool>(number3), true);
QJSManagedValue number4(QJSPrimitiveValue(-456.0), &eng);
QCOMPARE(number4.toBoolean(), true);
QCOMPARE(qjsvalue_cast<bool>(number4), true);
QJSManagedValue number5(QJSPrimitiveValue(0x43211234), &eng);
QCOMPARE(number5.toBoolean(), true);
QJSManagedValue str(QJSPrimitiveValue(QStringLiteral("")), &eng);
QCOMPARE(str.toBoolean(), false);
QCOMPARE(qjsvalue_cast<bool>(str), false);
QJSManagedValue str2(QJSPrimitiveValue(QStringLiteral("123")), &eng);
QCOMPARE(str2.toBoolean(), true);
QCOMPARE(qjsvalue_cast<bool>(str2), true);
}
}
void tst_QJSManagedValue::toVariant()
{
QJSEngine eng;
{
QJSManagedValue undefined(eng.toManagedValue(QVariant()));
QCOMPARE(undefined.toVariant(), QVariant());
QCOMPARE(qjsvalue_cast<QVariant>(undefined), QVariant());
}
{
QJSManagedValue null(eng.evaluate(QStringLiteral("null")), &eng);
QCOMPARE(null.toVariant(), QVariant::fromValue(nullptr));
QCOMPARE(qjsvalue_cast<QVariant>(null), QVariant::fromValue(nullptr));
}
{
QJSManagedValue number(eng.toManagedValue(123.0));
QCOMPARE(number.toVariant(), QVariant(123.0));
QCOMPARE(qjsvalue_cast<QVariant>(number), QVariant(123.0));
QJSManagedValue intNumber(eng.toManagedValue(qint32(123)));
QCOMPARE(intNumber.toVariant().typeId(), QVariant((qint32)123).typeId());
QCOMPARE((qjsvalue_cast<QVariant>(intNumber)).typeId(),
QVariant(qint32(123)).typeId());
QJSManagedValue falskt(eng.toManagedValue(false));
QCOMPARE(falskt.toVariant(), QVariant(false));
QCOMPARE(qjsvalue_cast<QVariant>(falskt), QVariant(false));
QJSManagedValue sant(eng.toManagedValue(true));
QCOMPARE(sant.toVariant(), QVariant(true));
QCOMPARE(qjsvalue_cast<QVariant>(sant), QVariant(true));
QJSManagedValue str(eng.toManagedValue(QStringLiteral("ciao")));
QCOMPARE(str.toVariant(), QVariant(QStringLiteral("ciao")));
QCOMPARE(qjsvalue_cast<QVariant>(str), QVariant(QStringLiteral("ciao")));
}
{
QJSManagedValue object(eng.newObject(), &eng);
QVariant retained = object.toVariant();
QCOMPARE(retained.metaType(), QMetaType::fromType<QJSValue>());
QVERIFY(QJSManagedValue(retained.value<QJSValue>(), &eng).strictlyEquals(object));
}
{
QObject temp;
QJSManagedValue qobject(eng.newQObject(&temp), &eng);
QVariant var = qobject.toVariant();
QCOMPARE(var.typeId(), int(QMetaType::QObjectStar));
QCOMPARE(qvariant_cast<QObject*>(var), (QObject *)&temp);
QCOMPARE(qobject.toVariant(), var);
}
{
QDateTime dateTime = QDate(1980, 10, 4).startOfDay();
QJSManagedValue dateObject(eng.toManagedValue(dateTime));
QVariant var = dateObject.toVariant();
QCOMPARE(var, QVariant(dateTime));
QCOMPARE(dateObject.toVariant(), var);
}
{
QRegularExpression rx = QRegularExpression(QStringLiteral("[0-9a-z]+"));
QJSManagedValue rxObject(eng.toManagedValue(rx));
QVERIFY(rxObject.isRegularExpression());
QVariant var = rxObject.toVariant();
QCOMPARE(var, QVariant(rx));
QCOMPARE(rxObject.toVariant(), var);
}
{
QJSManagedValue inv;
QCOMPARE(inv.toVariant(), QVariant());
QCOMPARE(inv.toVariant(), QVariant());
QCOMPARE(qjsvalue_cast<QVariant>(inv), QVariant());
}
// V2 constructors
{
QJSManagedValue number(QJSPrimitiveValue(123.0), &eng);
QCOMPARE(number.toVariant(), QVariant(123.0));
QCOMPARE(number.toVariant(), QVariant(123.0));
QCOMPARE(qjsvalue_cast<QVariant>(number), QVariant(123.0));
QJSManagedValue falskt(QJSPrimitiveValue(false), &eng);
QCOMPARE(falskt.toVariant(), QVariant(false));
QCOMPARE(falskt.toVariant(), QVariant(false));
QCOMPARE(qjsvalue_cast<QVariant>(falskt), QVariant(false));
QJSManagedValue sant(QJSPrimitiveValue(true), &eng);
QCOMPARE(sant.toVariant(), QVariant(true));
QCOMPARE(sant.toVariant(), QVariant(true));
QCOMPARE(qjsvalue_cast<QVariant>(sant), QVariant(true));
QJSManagedValue str(QStringLiteral("ciao"), &eng);
QCOMPARE(str.toVariant(), QVariant(QStringLiteral("ciao")));
QCOMPARE(str.toVariant(), QVariant(QStringLiteral("ciao")));
QCOMPARE(qjsvalue_cast<QVariant>(str), QVariant(QStringLiteral("ciao")));
QJSManagedValue undef(QJSPrimitiveUndefined(), &eng);
QCOMPARE(undef.toVariant(), QVariant());
QCOMPARE(undef.toVariant(), QVariant());
QCOMPARE(qjsvalue_cast<QVariant>(undef), QVariant());
QJSManagedValue nil(QJSPrimitiveNull(), &eng);
QCOMPARE(nil.toVariant(), QVariant::fromValue(nullptr));
QCOMPARE(nil.toVariant(), QVariant::fromValue(nullptr));
QCOMPARE(qjsvalue_cast<QVariant>(nil), QVariant::fromValue(nullptr));
}
// object
{
QVariantMap mapIn;
mapIn[QStringLiteral("a")] = 123;
mapIn[QStringLiteral("b")] = QStringLiteral("hello");
QJSManagedValue object(eng.toManagedValue(mapIn));
QCOMPARE(object.type(), QJSManagedValue::Object);
QVariant retained = object.toVariant();
QCOMPARE(retained.metaType(), QMetaType::fromType<QVariantMap>());
QCOMPARE(retained, mapIn);
QVariantMap mapOut = retained.toMap();
QCOMPARE(mapOut.size(), mapIn.size());
for (auto it = mapOut.begin(), end = mapOut.end(); it != end; ++it)
QCOMPARE(*it, mapIn[it.key()]);
// round-trip conversion
QJSManagedValue object2(eng.toManagedValue(retained));
QCOMPARE(object2.type(), QJSManagedValue::Object);
QVERIFY(object2.property(QStringLiteral("a")).strictlyEquals(object.property(QStringLiteral("a"))));
QVERIFY(object2.property(QStringLiteral("b")).strictlyEquals(object.property(QStringLiteral("b"))));
}
// variant list
{
auto handler = qInstallMessageHandler([](QtMsgType type, const QMessageLogContext &, const QString &) {
if (type == QtMsgType::QtWarningMsg)
QFAIL("Converting QJSManagedValue to QVariant should not cause error messages");
});
auto guard = qScopeGuard([&]() { qInstallMessageHandler(handler); });
QVariantList listIn;
listIn << 123 << QStringLiteral("hello");
QJSManagedValue array(eng.toManagedValue(listIn));
QCOMPARE(array.property(QStringLiteral("length")).toInt(), 2);
QVariant retained = array.toVariant();
QCOMPARE(retained.metaType(), QMetaType::fromType<QVariantList>());
QVariantList listOut = retained.toList();
QCOMPARE(listOut.size(), listIn.size());
for (int i = 0; i < listIn.size(); ++i)
QCOMPARE(listOut.at(i), listIn.at(i));
// round-trip conversion
QJSManagedValue array2(eng.toManagedValue(retained));
QCOMPARE(array2.property(QStringLiteral("length")).toInt(), array.property(QStringLiteral("length")).toInt());
for (int i = 0; i < array.property(QStringLiteral("length")).toInt(); ++i)
QVERIFY(array2.property(i).strictlyEquals(array.property(i)));
}
// function
{
QJSManagedValue func(eng.evaluate(QStringLiteral("(function() { return 5 + 5 })")), &eng);
QVERIFY(func.isFunction());
QCOMPARE(func.call().toInt(), 10);
QVariant funcVar = func.toVariant();
QVERIFY(funcVar.isValid());
QCOMPARE(funcVar.metaType(), QMetaType::fromType<QJSValue>());
QJSManagedValue func2(eng.toManagedValue(funcVar));
QVERIFY(func2.isFunction());
QCOMPARE(func2.call().toInt(), 10);
}
}
void tst_QJSManagedValue::hasProperty_basic()
{
QJSEngine eng;
QJSManagedValue obj(eng.newObject(), &eng);
QVERIFY(obj.hasProperty(QStringLiteral("hasOwnProperty"))); // inherited from Object.prototype
QVERIFY(!obj.hasOwnProperty(QStringLiteral("hasOwnProperty")));
QVERIFY(!obj.hasProperty(QStringLiteral("foo")));
QVERIFY(!obj.hasOwnProperty(QStringLiteral("foo")));
obj.setProperty(QStringLiteral("foo"), 123);
QVERIFY(obj.hasProperty(QStringLiteral("foo")));
QVERIFY(obj.hasOwnProperty(QStringLiteral("foo")));
QVERIFY(!obj.hasProperty(QStringLiteral("bar")));
QVERIFY(!obj.hasOwnProperty(QStringLiteral("bar")));
}
void tst_QJSManagedValue::hasProperty_globalObject()
{
QJSEngine eng;
QJSManagedValue global(eng.globalObject(), &eng);
QVERIFY(global.hasProperty(QStringLiteral("Math")));
QVERIFY(global.hasOwnProperty(QStringLiteral("Math")));
QVERIFY(!global.hasProperty(QStringLiteral("NoSuchStandardProperty")));
QVERIFY(!global.hasOwnProperty(QStringLiteral("NoSuchStandardProperty")));
QVERIFY(!global.hasProperty(QStringLiteral("foo")));
QVERIFY(!global.hasOwnProperty(QStringLiteral("foo")));
global.setProperty(QStringLiteral("foo"), 123);
QVERIFY(global.hasProperty(QStringLiteral("foo")));
QVERIFY(global.hasOwnProperty(QStringLiteral("foo")));
}
void tst_QJSManagedValue::hasProperty_changePrototype()
{
QJSEngine eng;
QJSManagedValue obj(eng.newObject(), &eng);
QJSManagedValue proto(eng.newObject(), &eng);
obj.setPrototype(proto);
QVERIFY(!obj.hasProperty(QStringLiteral("foo")));
QVERIFY(!obj.hasOwnProperty(QStringLiteral("foo")));
proto.setProperty(QStringLiteral("foo"), 123);
QVERIFY(obj.hasProperty(QStringLiteral("foo")));
QVERIFY(!obj.hasOwnProperty(QStringLiteral("foo")));
obj.setProperty(QStringLiteral("foo"), 456); // override prototype property
QVERIFY(obj.hasProperty(QStringLiteral("foo")));
QVERIFY(obj.hasOwnProperty(QStringLiteral("foo")));
}
void tst_QJSManagedValue::hasProperty_QTBUG56830_data()
{
QTest::addColumn<QString>("key");
QTest::addColumn<QString>("lookup");
QTest::newRow("bugreport-1") << QStringLiteral("240000000000") << QStringLiteral("3776798720");
QTest::newRow("bugreport-2") << QStringLiteral("240000000001") << QStringLiteral("3776798721");
QTest::newRow("biggest-ok-before-bug") << QStringLiteral("238609294221") << QStringLiteral("2386092941");
QTest::newRow("smallest-bugged") << QStringLiteral("238609294222") << QStringLiteral("2386092942");
QTest::newRow("biggest-bugged") << QStringLiteral("249108103166") << QStringLiteral("12884901886");
QTest::newRow("smallest-ok-after-bug") << QStringLiteral("249108103167") << QStringLiteral("12884901887");
}
void tst_QJSManagedValue::hasProperty_QTBUG56830()
{
QFETCH(QString, key);
QFETCH(QString, lookup);
QJSEngine eng;
QJSManagedValue value(QJSPrimitiveValue(42), &eng);
QJSManagedValue obj(eng.newObject(), &eng);
obj.setProperty(key, QJSValue(std::move(value)));
QVERIFY(obj.hasProperty(key));
QVERIFY(!obj.hasProperty(lookup));
}
void tst_QJSManagedValue::deleteProperty_basic()
{
QJSEngine eng;
QJSManagedValue obj(eng.newObject(), &eng);
// deleteProperty() behavior matches JS delete operator
obj.deleteProperty(QStringLiteral("foo"));
obj.setProperty(QStringLiteral("foo"), 123);
obj.deleteProperty(QStringLiteral("foo"));
QVERIFY(!obj.hasOwnProperty(QStringLiteral("foo")));
}
void tst_QJSManagedValue::deleteProperty_globalObject()
{
QJSEngine eng;
QJSManagedValue global(eng.globalObject(), &eng);
// deleteProperty() behavior matches JS delete operator
global.deleteProperty(QStringLiteral("foo"));
global.setProperty(QStringLiteral("foo"), 123);
global.deleteProperty(QStringLiteral("foo"));
QVERIFY(!global.hasProperty(QStringLiteral("foo")));
global.deleteProperty(QStringLiteral("Math"));
QVERIFY(!global.hasProperty(QStringLiteral("Math")));
global.deleteProperty(QStringLiteral("NaN")); // read-only
QVERIFY(global.hasProperty(QStringLiteral("NaN")));
}
void tst_QJSManagedValue::deleteProperty_inPrototype()
{
QJSEngine eng;
QJSManagedValue obj(eng.newObject(), &eng);
QJSManagedValue proto(eng.newObject(), &eng);
obj.setPrototype(proto);
proto.setProperty(QStringLiteral("foo"), 123);
QVERIFY(obj.hasProperty(QStringLiteral("foo")));
// deleteProperty() behavior matches JS delete operator
obj.deleteProperty(QStringLiteral("foo"));
QVERIFY(obj.hasProperty(QStringLiteral("foo")));
}
void tst_QJSManagedValue::getSetProperty_propertyRemoval()
{
QJSEngine eng;
QJSManagedValue object(eng.newObject(), &eng);
QJSManagedValue str(eng.toManagedValue(QStringLiteral("bar")));
QJSManagedValue num(eng.toManagedValue(123.0));
object.setProperty(QStringLiteral("foo"), num.toJSValue());
QVERIFY(QJSManagedValue(object.property(QStringLiteral("foo")), &eng).strictlyEquals(num));
object.setProperty(QStringLiteral("bar"), str.toJSValue());
QVERIFY(QJSManagedValue(object.property(QStringLiteral("bar")), &eng).strictlyEquals(str));
object.deleteProperty(QStringLiteral("foo"));
QVERIFY(!object.hasOwnProperty(QStringLiteral("foo")));
QVERIFY(QJSManagedValue(object.property(QStringLiteral("bar")), &eng).strictlyEquals(str));
object.setProperty(QStringLiteral("foo"), num.toJSValue());
QVERIFY(QJSManagedValue(object.property(QStringLiteral("foo")), &eng).strictlyEquals(num));
QVERIFY(QJSManagedValue(object.property(QStringLiteral("bar")), &eng).strictlyEquals(str));
object.deleteProperty(QStringLiteral("bar"));
QVERIFY(!object.hasOwnProperty(QStringLiteral("bar")));
QVERIFY(QJSManagedValue(object.property(QStringLiteral("foo")), &eng).strictlyEquals(num));
object.deleteProperty(QStringLiteral("foo"));
QVERIFY(!object.hasOwnProperty(QStringLiteral("foo")));
eng.globalObject().setProperty(QStringLiteral("object3"), object.toJSValue());
QVERIFY(QJSManagedValue(eng.evaluate(QStringLiteral("object3.hasOwnProperty('foo')")), &eng)
.strictlyEquals(eng.toManagedValue(false)));
object.setProperty(QStringLiteral("foo"), num.toJSValue());
QVERIFY(QJSManagedValue(eng.evaluate(QStringLiteral("object3.hasOwnProperty('foo')")), &eng)
.strictlyEquals(eng.toManagedValue(true)));
QVERIFY(QJSManagedValue(eng.globalObject(), &eng).deleteProperty(QStringLiteral("object3")));
QVERIFY(QJSManagedValue(eng.evaluate(QStringLiteral("this.hasOwnProperty('object3')")), &eng)
.strictlyEquals(eng.toManagedValue(false)));
}
void tst_QJSManagedValue::getSetProperty_resolveMode()
{
// test ResolveMode
QJSEngine eng;
QJSManagedValue object(eng.newObject(), &eng);
QJSManagedValue prototype(eng.newObject(), &eng);
object.setPrototype(prototype);
QJSManagedValue num2(eng.toManagedValue(456.0));
prototype.setProperty(QStringLiteral("propertyInPrototype"), num2.toJSValue());
// default is ResolvePrototype
QVERIFY(QJSManagedValue(object.property(QStringLiteral("propertyInPrototype")), &eng)
.strictlyEquals(num2));
}
void tst_QJSManagedValue::getSetProperty_twoEngines()
{
QJSEngine engine;
QJSManagedValue object(engine.newObject(), &engine);
QJSEngine otherEngine;
QJSManagedValue otherNum(otherEngine.toManagedValue(123));
object.setProperty(QStringLiteral("oof"), QJSValue(std::move(otherNum)));
QVERIFY(object.hasOwnProperty(QStringLiteral("oof"))); // primitive values don't have an engine
QVERIFY(object.property(QStringLiteral("oof")).isNumber());
QJSManagedValue otherString(otherEngine.toManagedValue(QStringLiteral("nope")));
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::setProperty() failed: Value was created in different engine.");
object.setProperty(QStringLiteral("eek"), QJSValue(std::move(otherString)));
QVERIFY(!object.hasOwnProperty(QStringLiteral("eek"))); // strings are managed
QVERIFY(object.property(QStringLiteral("eek")).isUndefined());
}
void tst_QJSManagedValue::getSetProperty_gettersAndSettersThrowErrorJS()
{
// getter/setter that throws an error (from js function)
QJSEngine eng;
QJSManagedValue str(eng.toManagedValue(QStringLiteral("bar")));
eng.evaluate(QStringLiteral(
"o = new Object; "
"o.__defineGetter__('foo', function() { throw new Error('get foo') }); "
"o.__defineSetter__('foo', function() { throw new Error('set foo') }); "));
QJSManagedValue object(eng.evaluate(QStringLiteral("o")), &eng);
QCOMPARE(object.type(), QJSManagedValue::Object);
QVERIFY(!eng.hasError());
QJSManagedValue ret(object.property(QStringLiteral("foo")), &eng);
QCOMPARE(ret.type(), QJSManagedValue::Undefined);
QVERIFY(eng.hasError());
QJSManagedValue error(eng.catchError(), &eng);
QCOMPARE(error.toString(), QStringLiteral("Error: get foo"));
QVERIFY(!eng.hasError());
object.setProperty(QStringLiteral("foo"), QJSValue(std::move(str)));
QVERIFY(eng.hasError());
QCOMPARE(eng.catchError().toString(), QStringLiteral("Error: set foo"));
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::getSetProperty_array()
{
QJSEngine eng;
QJSManagedValue str(eng.toManagedValue(QStringLiteral("bar")));
QJSManagedValue num(eng.toManagedValue(123.0));
QJSManagedValue array(eng.newArray(), &eng);
QVERIFY(array.isArray());
array.setProperty(0, num.toJSValue());
QCOMPARE(array.property(0).toNumber(), num.toNumber());
QCOMPARE(array.property(QStringLiteral("0")).toNumber(), num.toNumber());
QCOMPARE(array.property(QStringLiteral("length")).toUInt(), quint32(1));
array.setProperty(1, str.toJSValue());
QCOMPARE(array.property(1).toString(), str.toString());
QCOMPARE(array.property(QStringLiteral("1")).toString(), str.toString());
QCOMPARE(array.property(QStringLiteral("length")).toUInt(), quint32(2));
array.setProperty(QStringLiteral("length"), eng.toScriptValue(1));
QCOMPARE(array.property(QStringLiteral("length")).toUInt(), quint32(1));
QVERIFY(array.property(1).isUndefined());
}
void tst_QJSManagedValue::getSetProperty()
{
QJSEngine eng;
QJSManagedValue object(eng.newObject(), &eng);
QJSManagedValue str(eng.toManagedValue(QStringLiteral("bar")));
object.setProperty(QStringLiteral("foo"), str.toJSValue());
QCOMPARE(object.property(QStringLiteral("foo")).toString(), str.toString());
QJSManagedValue num(eng.toManagedValue(123.0));
object.setProperty(QStringLiteral("baz"), num.toJSValue());
QCOMPARE(object.property(QStringLiteral("baz")).toNumber(), num.toNumber());
QJSManagedValue strstr(QJSPrimitiveValue("bar"), &eng);
object.setProperty(QStringLiteral("foo"), strstr.toJSValue());
QCOMPARE(object.property(QStringLiteral("foo")).toString(), strstr.toString());
QJSManagedValue numnum(QJSPrimitiveValue(123.0), &eng);
object.setProperty(QStringLiteral("baz"), numnum.toJSValue());
QCOMPARE(object.property(QStringLiteral("baz")).toNumber(), numnum.toNumber());
QJSManagedValue inv;
inv.setProperty(QStringLiteral("foo"), num.toJSValue());
QCOMPARE(inv.property(QStringLiteral("foo")).isUndefined(), true);
eng.globalObject().setProperty(QStringLiteral("object"), object.toJSValue());
// Setting index property on non-Array
object.setProperty(13, num.toJSValue());
QVERIFY(QJSManagedValue(object.property(13), &eng).equals(num));
}
void tst_QJSManagedValue::getSetPrototype_cyclicPrototype()
{
QJSEngine eng;
QJSManagedValue prototype(eng.newObject(), &eng);
QJSManagedValue object(eng.newObject(), &eng);
object.setPrototype(prototype);
QJSManagedValue previousPrototype = prototype.prototype();
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::setPrototype() failed: Prototype cycle detected.");
prototype.setPrototype(prototype);
QCOMPARE(prototype.prototype().strictlyEquals(previousPrototype), true);
object.setPrototype(prototype);
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::setPrototype() failed: Prototype cycle detected.");
prototype.setPrototype(object);
QCOMPARE(prototype.prototype().strictlyEquals(previousPrototype), true);
}
void tst_QJSManagedValue::getSetPrototype_evalCyclicPrototype()
{
QJSEngine eng;
QJSManagedValue ret(eng.evaluate(QStringLiteral("o = { }; p = { }; o.__proto__ = p; p.__proto__ = o")), &eng);
QEXPECT_FAIL("", "QJSEngine::evaluate() catches exceptions.", Abort);
QCOMPARE(ret.type(), QJSManagedValue::Undefined);
QVERIFY(eng.hasError());
QJSManagedValue error(eng.catchError(), &eng);
QCOMPARE(error.toString(), QStringLiteral("TypeError: Could not change prototype."));
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::getSetPrototype_eval()
{
QJSEngine eng;
QJSManagedValue ret(eng.evaluate(QStringLiteral("p = { }; p.__proto__ = { }")), &eng);
QCOMPARE(ret.type(), QJSManagedValue::Object);
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::getSetPrototype_invalidPrototype()
{
QJSEngine eng;
QJSManagedValue inv;
QJSManagedValue object(eng.newObject(), &eng);
QJSManagedValue proto = object.prototype();
QVERIFY(object.prototype().strictlyEquals(proto));
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::setPrototype() failed: Can only set a prototype on an object (excluding null).");
inv.setPrototype(object);
QCOMPARE(inv.prototype().type(), QJSManagedValue::Undefined);
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::setPrototype() failed: Can only set objects (including null) as prototypes.");
object.setPrototype(inv);
QVERIFY(object.prototype().strictlyEquals(proto));
}
void tst_QJSManagedValue::getSetPrototype_twoEngines()
{
QJSEngine eng;
QJSManagedValue prototype(eng.newObject(), &eng);
QJSManagedValue object(eng.newObject(), &eng);
object.setPrototype(prototype);
QJSEngine otherEngine;
QJSManagedValue newPrototype(otherEngine.newObject(), &otherEngine);
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::setPrototype() failed: Prototype was created in differen engine.");
object.setPrototype(newPrototype);
QCOMPARE(object.prototype().strictlyEquals(prototype), true);
}
void tst_QJSManagedValue::getSetPrototype_null()
{
QJSEngine eng;
QJSManagedValue object(eng.newObject(), &eng);
object.setPrototype(QJSManagedValue(QJSPrimitiveNull(), &eng));
QVERIFY(object.prototype().isNull());
QJSManagedValue newProto(eng.newObject(), &eng);
object.setPrototype(newProto);
QVERIFY(object.prototype().equals(newProto));
object.setPrototype(QJSManagedValue(eng.evaluate(QStringLiteral("null")), &eng));
QVERIFY(object.prototype().isNull());
}
void tst_QJSManagedValue::getSetPrototype_notObjectOrNull()
{
QJSEngine eng;
QJSManagedValue object(eng.newObject(), &eng);
QJSManagedValue originalProto = object.prototype();
// bool
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::setPrototype() failed: Can only set objects (including null) as prototypes.");
object.setPrototype({QJSPrimitiveValue(true), &eng});
QVERIFY(object.prototype().equals(originalProto));
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::setPrototype() failed: Can only set objects (including null) as prototypes.");
object.setPrototype(eng.toManagedValue(true));
QVERIFY(object.prototype().equals(originalProto));
// number
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::setPrototype() failed: Can only set objects (including null) as prototypes.");
object.setPrototype({QJSPrimitiveValue(123), &eng});
QVERIFY(object.prototype().equals(originalProto));
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::setPrototype() failed: Can only set objects (including null) as prototypes.");
object.setPrototype(eng.toManagedValue(123));
QVERIFY(object.prototype().equals(originalProto));
// string
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::setPrototype() failed: Can only set objects (including null) as prototypes.");
object.setPrototype({QStringLiteral("foo"), &eng});
QVERIFY(object.prototype().equals(originalProto));
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::setPrototype() failed: Can only set objects (including null) as prototypes.");
object.setPrototype(eng.toManagedValue(QStringLiteral("foo")));
QVERIFY(object.prototype().equals(originalProto));
// undefined
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::setPrototype() failed: Can only set objects (including null) as prototypes.");
object.setPrototype(QJSManagedValue(QJSPrimitiveUndefined(), &eng));
QVERIFY(object.prototype().equals(originalProto));
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::setPrototype() failed: Can only set objects (including null) as prototypes.");
object.setPrototype(eng.toManagedValue(QVariant()));
QVERIFY(object.prototype().equals(originalProto));
}
void tst_QJSManagedValue::getSetPrototype()
{
QJSEngine eng;
QJSManagedValue prototype(eng.newObject(), &eng);
QJSManagedValue object(eng.newObject(), &eng);
object.setPrototype(prototype);
QVERIFY(object.prototype().strictlyEquals(prototype));
}
void tst_QJSManagedValue::call_function()
{
QJSEngine eng;
QJSManagedValue fun(eng.evaluate(QStringLiteral("(function() { return 1; })")), &eng);
QVERIFY(fun.isFunction());
QJSManagedValue result(fun.call(), &eng);
QCOMPARE(result.type(), QJSManagedValue::Number);
QCOMPARE(result.toInteger(), 1);
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::call_object()
{
QJSEngine eng;
QJSManagedValue object(eng.evaluate(QStringLiteral("Object")), &eng);
QCOMPARE(object.isFunction(), true);
QJSManagedValue result(object.callWithInstance(object.toJSValue()), &eng);
QCOMPARE(result.type(), QJSManagedValue::Object);
QVERIFY(!result.isNull());
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::call_newObjects()
{
QJSEngine eng;
// test that call() doesn't construct new objects
QJSManagedValue number(eng.evaluate(QStringLiteral("Number")), &eng);
QJSManagedValue object(eng.evaluate(QStringLiteral("Object")), &eng);
QCOMPARE(object.isFunction(), true);
QJSValueList args;
args << eng.toScriptValue(123);
QJSManagedValue result(number.callWithInstance(object.toJSValue(), args), &eng);
QVERIFY(result.strictlyEquals(QJSManagedValue(args.at(0), &eng)));
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::call_this()
{
QJSEngine eng;
// test that correct "this" object is used
QJSManagedValue fun(eng.evaluate(QStringLiteral("(function() { return this; })")), &eng);
QCOMPARE(fun.isFunction(), true);
QJSManagedValue numberObject(eng.evaluate(QStringLiteral("new Number(123)")), &eng);
QJSManagedValue result(fun.callWithInstance(QJSValue(std::move(numberObject))), &eng);
QCOMPARE(result.type(), QJSManagedValue::Object);
QCOMPARE(result.toNumber(), 123.0);
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::call_arguments()
{
QJSEngine eng;
// test that correct arguments are passed
QJSManagedValue fun(eng.evaluate(QStringLiteral("(function() { return arguments[0]; })")), &eng);
QCOMPARE(fun.isFunction(), true);
{
QJSManagedValue result(fun.callWithInstance(eng.toScriptValue(QVariant())), &eng);
QCOMPARE(result.type(), QJSManagedValue::Undefined);
QVERIFY(!eng.hasError());
}
{
QJSValueList args;
args << eng.toScriptValue(123.0);
QJSManagedValue result(fun.callWithInstance(eng.toScriptValue(QVariant()), args), &eng);
QCOMPARE(result.type(), QJSManagedValue::Number);
QCOMPARE(result.toNumber(), 123.0);
QVERIFY(!eng.hasError());
}
// V2 constructors
{
QJSValueList args;
args << QJSValue(123.0);
QJSManagedValue result(fun.callWithInstance(eng.toScriptValue(QVariant()), args), &eng);
QCOMPARE(result.type(), QJSManagedValue::Number);
QCOMPARE(result.toNumber(), 123.0);
QVERIFY(!eng.hasError());
}
}
void tst_QJSManagedValue::call()
{
QJSEngine eng;
{
QJSManagedValue fun(eng.evaluate(QStringLiteral("(function() { return arguments[1]; })")), &eng);
QCOMPARE(fun.isFunction(), true);
QJSValueList args;
args << eng.toScriptValue(123.0) << eng.toScriptValue(456.0);
QJSManagedValue result(fun.callWithInstance(eng.toScriptValue(QVariant()), args), &eng);
QCOMPARE(result.type(), QJSManagedValue::Number);
QCOMPARE(result.toNumber(), 456.0);
QVERIFY(!eng.hasError());
}
{
QJSManagedValue fun(eng.evaluate(QStringLiteral("(function() { throw new Error('foo'); })")), &eng);
QCOMPARE(fun.isFunction(), true);
QVERIFY(!eng.hasError());
QJSManagedValue result(fun.call(), &eng);
QCOMPARE(result.type(), QJSManagedValue::Undefined);
QVERIFY(eng.hasError());
QJSManagedValue error(eng.catchError(), &eng);
QVERIFY(error.toString().contains("foo"));
}
}
void tst_QJSManagedValue::call_twoEngines()
{
QJSEngine eng;
QJSManagedValue object(eng.evaluate(QStringLiteral("Object")), &eng);
QJSEngine otherEngine;
QJSManagedValue fun(otherEngine.evaluate(QStringLiteral("(function() { return 1; })")), &otherEngine);
QVERIFY(fun.isFunction());
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::callWithInstance() failed: Instance was created in different engine.");
QVERIFY(fun.callWithInstance(QJSValue(std::move(object))).isUndefined());
// Primitive value doesn't need an engine
QVERIFY(!fun.call(QJSValueList() << eng.toScriptValue(123)).isUndefined());
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::call() failed: Argument was created in different engine.");
QVERIFY(fun.call(QJSValueList() << eng.toScriptValue(QStringLiteral("string")))
.isUndefined());
{
QJSManagedValue fun(eng.evaluate(QStringLiteral("Object")), &eng);
QVERIFY(fun.isFunction());
QJSEngine eng2;
QJSManagedValue objectInDifferentEngine(eng2.newObject(), &eng2);
QJSValueList args;
args << QJSValue(std::move(objectInDifferentEngine));
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::call() failed: Argument was created in different engine.");
fun.call(args);
}
}
void tst_QJSManagedValue::call_nonFunction_data()
{
newEngine();
QTest::addColumn<QJSValue>("value");
QTest::newRow("invalid") << QJSValue();
QTest::newRow("bool") << QJSValue(false);
QTest::newRow("int") << QJSValue(123);
QTest::newRow("string") << QJSValue(QStringLiteral("ciao"));
QTest::newRow("undefined") << QJSValue(QJSValue::UndefinedValue);
QTest::newRow("null") << QJSValue(QJSValue::NullValue);
QTest::newRow("bool bound") << engine->toScriptValue(false);
QTest::newRow("int bound") << engine->toScriptValue(123);
QTest::newRow("string bound") << engine->toScriptValue(QStringLiteral("ciao"));
QTest::newRow("undefined bound") << engine->toScriptValue(QVariant());
QTest::newRow("null bound") << engine->evaluate(QStringLiteral("null"));
}
void tst_QJSManagedValue::call_nonFunction()
{
// calling things that are not functions
QFETCH(QJSValue, value);
QVERIFY(QJSManagedValue(std::move(value), engine.data()).call().isUndefined());
QVERIFY(engine->hasError());
QJSManagedValue error(engine->catchError(), engine.data());
QVERIFY(error.toString().contains("TypeError"));
}
void tst_QJSManagedValue::construct_nonFunction_data()
{
call_nonFunction_data();
}
void tst_QJSManagedValue::construct_nonFunction()
{
QFETCH(QJSValue, value);
QVERIFY(QJSManagedValue(std::move(value), engine.data()).callAsConstructor().isUndefined());
QVERIFY(engine->hasError());
QJSManagedValue error(engine->catchError(), engine.data());
QVERIFY(error.toString().contains("TypeError"));
}
void tst_QJSManagedValue::construct_simple()
{
QJSEngine eng;
QJSManagedValue fun(eng.evaluate(QStringLiteral("(function () { this.foo = 123; })")), &eng);
QVERIFY(fun.isFunction());
QJSManagedValue ret(fun.callAsConstructor(), &eng);
QCOMPARE(ret.type(), QJSManagedValue::Object);
QVERIFY(!ret.isNull());
QVERIFY(ret.prototype().strictlyEquals(
QJSManagedValue(fun.property(QStringLiteral("prototype")), &eng)));
QCOMPARE(ret.property(QStringLiteral("foo")).toInt(), 123);
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::construct_newObjectJS()
{
QJSEngine eng;
// returning a different object overrides the default-constructed one
QJSManagedValue fun(eng.evaluate(QStringLiteral("(function () { return { bar: 456 }; })")), &eng);
QVERIFY(fun.isFunction());
QJSManagedValue ret(fun.callAsConstructor(), &eng);
QCOMPARE(ret.type(), QJSManagedValue::Object);
QVERIFY(!ret.isNull());
QVERIFY(!ret.prototype().strictlyEquals(
QJSManagedValue(fun.property(QStringLiteral("prototype")), &eng)));
QCOMPARE(ret.property(QStringLiteral("bar")).toInt(), 456);
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::construct_arg()
{
QJSEngine eng;
QJSManagedValue Number(eng.evaluate(QStringLiteral("Number")), &eng);
QCOMPARE(Number.isFunction(), true);
QJSValueList args;
args << eng.toScriptValue(123);
QJSManagedValue ret(Number.callAsConstructor(args), &eng);
QCOMPARE(ret.type(), QJSManagedValue::Object);
QVERIFY(!ret.isNull());
QCOMPARE(ret.toNumber(), args.at(0).toNumber());
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::construct_proto()
{
QJSEngine eng;
// test that internal prototype is set correctly
QJSManagedValue fun(eng.evaluate(QStringLiteral("(function() { return this.__proto__; })")), &eng);
QCOMPARE(fun.isFunction(), true);
QCOMPARE(fun.property(QStringLiteral("prototype")).isObject(), true);
QJSManagedValue ret(fun.callAsConstructor(), &eng);
QVERIFY(QJSManagedValue(fun.property(QStringLiteral("prototype")), &eng).strictlyEquals(ret));
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::construct_returnInt()
{
QJSEngine eng;
// test that we return the new object even if a non-object value is returned from the function
QJSManagedValue fun(eng.evaluate(QStringLiteral("(function() { return 123; })")), &eng);
QCOMPARE(fun.isFunction(), true);
QJSManagedValue ret(fun.callAsConstructor(), &eng);
QCOMPARE(ret.type(), QJSManagedValue::Object);
QVERIFY(!ret.isNull());
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::construct_throw()
{
QJSEngine eng;
QJSManagedValue fun(eng.evaluate(QStringLiteral("(function() { throw new Error('foo'); })")), &eng);
QCOMPARE(fun.isFunction(), true);
QVERIFY(!eng.hasError());
QJSManagedValue ret(fun.callAsConstructor(), &eng);
QCOMPARE(ret.type(), QJSManagedValue::Undefined);
QVERIFY(eng.hasError());
QJSManagedValue error(eng.catchError(), &eng);
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::construct_twoEngines()
{
QJSEngine engine;
QJSEngine otherEngine;
QJSManagedValue ctor(engine.evaluate(QStringLiteral("(function (a, b) { this.foo = 123; })")), &engine);
QJSManagedValue arg(otherEngine.toManagedValue(124567));
QVERIFY(!ctor.callAsConstructor(QJSValueList() << arg.toJSValue()).isUndefined());
QJSManagedValue arg2(otherEngine.toManagedValue(QStringLiteral("string")));
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::callAsConstructor() failed: Argument was created in different engine.");
QVERIFY(ctor.callAsConstructor(QJSValueList() << arg2.toJSValue()).isUndefined());
QTest::ignoreMessage(QtWarningMsg, "QJSManagedValue::callAsConstructor() failed: Argument was created in different engine.");
QVERIFY(ctor.callAsConstructor(QJSValueList() << arg.toJSValue() << otherEngine.newObject()).isUndefined());
}
void tst_QJSManagedValue::construct_constructorThrowsPrimitive()
{
QJSEngine eng;
QJSManagedValue fun(eng.evaluate(QStringLiteral("(function() { throw 123; })")), &eng);
QVERIFY(fun.isFunction());
// construct(QJSValueList)
{
QJSManagedValue ret(fun.callAsConstructor(), &eng);
QCOMPARE(ret.type(), QJSManagedValue::Undefined);
QVERIFY(eng.hasError());
QJSManagedValue error(eng.catchError(), &eng);
QCOMPARE(error.toNumber(), 123.0);
QVERIFY(!eng.hasError());
}
}
void tst_QJSManagedValue::equals()
{
QJSEngine eng;
QObject temp;
QVERIFY(QJSManagedValue().equals(QJSManagedValue()));
QJSManagedValue num(eng.toManagedValue(123));
QVERIFY(num.equals(eng.toManagedValue(123)));
QVERIFY(!num.equals(eng.toManagedValue(321)));
QVERIFY(num.equals(eng.toManagedValue(QStringLiteral("123"))));
QVERIFY(!num.equals(eng.toManagedValue(QStringLiteral("321"))));
QVERIFY(num.equals({eng.evaluate(QStringLiteral("new Number(123)")), &eng}));
QVERIFY(!num.equals({eng.evaluate(QStringLiteral("new Number(321)")), &eng}));
QVERIFY(num.equals({eng.evaluate(QStringLiteral("new String('123')")), &eng}));
QVERIFY(!num.equals({eng.evaluate(QStringLiteral("new String('321')")), &eng}));
QVERIFY(QJSManagedValue(eng.evaluate(QStringLiteral("new Number(123)")), &eng).equals(num));
QVERIFY(!num.equals(QJSManagedValue()));
QJSManagedValue str(eng.toManagedValue(QStringLiteral("123")));
QVERIFY(str.equals(eng.toManagedValue(QStringLiteral("123"))));
QVERIFY(!str.equals(eng.toManagedValue(QStringLiteral("321"))));
QVERIFY(str.equals(eng.toManagedValue(123)));
QVERIFY(!str.equals(eng.toManagedValue(321)));
QVERIFY(str.equals({eng.evaluate(QStringLiteral("new String('123')")), &eng}));
QVERIFY(!str.equals({eng.evaluate(QStringLiteral("new String('321')")), &eng}));
QVERIFY(str.equals({eng.evaluate(QStringLiteral("new Number(123)")), &eng}));
QVERIFY(!str.equals({eng.evaluate(QStringLiteral("new Number(321)")), &eng}));
QVERIFY(QJSManagedValue(eng.evaluate(QStringLiteral("new String('123')")), &eng).equals(str));
QCOMPARE(str.equals(QJSManagedValue()), false);
QJSManagedValue num2(QJSPrimitiveValue(123), &eng);
QVERIFY(num2.equals(QJSManagedValue(QJSPrimitiveValue(123), &eng)));
QVERIFY(!num2.equals(QJSManagedValue(QJSPrimitiveValue(321), &eng)));
QVERIFY(num2.equals(QJSManagedValue(QStringLiteral("123"), &eng)));
QVERIFY(!num2.equals(QJSManagedValue(QStringLiteral("321"), &eng)));
QVERIFY(!num2.equals(QJSManagedValue()));
QJSManagedValue str2(QStringLiteral("123"), &eng);
QVERIFY(str2.equals(QJSManagedValue(QStringLiteral("123"), &eng)));
QVERIFY(!str2.equals(QJSManagedValue(QStringLiteral("321"), &eng)));
QVERIFY(str2.equals(QJSManagedValue(QJSPrimitiveValue(123), &eng)));
QVERIFY(!str2.equals(QJSManagedValue(QJSPrimitiveValue(321), &eng)));
QVERIFY(!str2.equals(QJSManagedValue()));
QJSManagedValue date1(eng.toManagedValue(QDate(2000, 1, 1).startOfDay()));
QJSManagedValue date2(eng.toManagedValue(QDate(1999, 1, 1).startOfDay()));
QCOMPARE(date1.equals(date2), false);
QCOMPARE(date1.equals(date1), true);
QCOMPARE(date2.equals(date2), true);
QJSManagedValue undefined(eng.toManagedValue(QVariant()));
QJSManagedValue null(eng.evaluate(QStringLiteral("null")), &eng);
QCOMPARE(undefined.equals(undefined), true);
QCOMPARE(null.equals(null), true);
QCOMPARE(undefined.equals(null), true);
QCOMPARE(null.equals(undefined), true);
QVERIFY(undefined.equals(QJSManagedValue()));
QVERIFY(null.equals(QJSManagedValue()));
QVERIFY(!null.equals(num));
QVERIFY(!undefined.equals(num));
QJSManagedValue sant(eng.toManagedValue(true));
QVERIFY(sant.equals(eng.toManagedValue(1)));
QVERIFY(sant.equals(eng.toManagedValue(QStringLiteral("1"))));
QVERIFY(sant.equals(sant));
QVERIFY(sant.equals({eng.evaluate(QStringLiteral("new Number(1)")), &eng}));
QVERIFY(sant.equals({eng.evaluate(QStringLiteral("new String('1')")), &eng}));
QVERIFY(sant.equals({eng.evaluate(QStringLiteral("new Boolean(true)")), &eng}));
QVERIFY(QJSManagedValue(eng.evaluate(QStringLiteral("new Boolean(true)")), &eng).equals(sant));
QVERIFY(!sant.equals(eng.toManagedValue(0)));
QVERIFY(!sant.equals(undefined));
QVERIFY(!sant.equals(null));
QJSManagedValue falskt(eng.toManagedValue(false));
QVERIFY(falskt.equals(eng.toManagedValue(0)));
QVERIFY(falskt.equals(eng.toManagedValue(QStringLiteral("0"))));
QVERIFY(falskt.equals(falskt));
QVERIFY(falskt.equals({eng.evaluate(QStringLiteral("new Number(0)")), &eng}));
QVERIFY(falskt.equals({eng.evaluate(QStringLiteral("new String('0')")), &eng}));
QVERIFY(falskt.equals({eng.evaluate(QStringLiteral("new Boolean(false)")), &eng}));
QVERIFY(QJSManagedValue(eng.evaluate(QStringLiteral("new Boolean(false)")), &eng).equals(falskt));
QVERIFY(!falskt.equals(sant));
QVERIFY(!falskt.equals(undefined));
QVERIFY(!falskt.equals(null));
QJSManagedValue obj1(eng.newObject(), &eng);
QJSManagedValue obj2(eng.newObject(), &eng);
QCOMPARE(obj1.equals(obj2), false);
QCOMPARE(obj2.equals(obj1), false);
QCOMPARE(obj1.equals(obj1), true);
QCOMPARE(obj2.equals(obj2), true);
QJSManagedValue qobj1(eng.newQObject(&temp), &eng);
QJSManagedValue qobj2(eng.newQObject(&temp), &eng);
QJSManagedValue qobj3(eng.newQObject(nullptr), &eng);
// FIXME: No ScriptOwnership: QJSManagedValue qobj4(eng.newQObject(new QObject(), QScriptEngine::ScriptOwnership), &eng);
QJSManagedValue qobj4(eng.newQObject(new QObject()), &eng);
QVERIFY(qobj1.equals(qobj2)); // compares the QObject pointers
QVERIFY(!qobj2.equals(qobj4)); // compares the QObject pointers
QVERIFY(!qobj2.equals(obj2)); // compares the QObject pointers
QJSManagedValue compareFun(eng.evaluate(QStringLiteral("(function(a, b) { return a == b; })")), &eng);
QVERIFY(compareFun.isFunction());
{
QJSManagedValue ret(compareFun.call(QJSValueList() << qobj1.toJSValue() << qobj2.toJSValue()), &eng);
QCOMPARE(ret.type(), QJSManagedValue::Boolean);
ret = QJSManagedValue(compareFun.call(QJSValueList() << qobj1.toJSValue() << qobj3.toJSValue()), &eng);
QCOMPARE(ret.type(), QJSManagedValue::Boolean);
QVERIFY(!ret.toBoolean());
ret = QJSManagedValue(compareFun.call(QJSValueList() << qobj1.toJSValue() << qobj4.toJSValue()), &eng);
QCOMPARE(ret.type(), QJSManagedValue::Boolean);
QVERIFY(!ret.toBoolean());
ret = QJSManagedValue(compareFun.call(QJSValueList() << qobj1.toJSValue() << obj1.toJSValue()), &eng);
QCOMPARE(ret.type(), QJSManagedValue::Boolean);
QVERIFY(!ret.toBoolean());
}
{
QJSManagedValue var1(eng.toManagedValue(QVariant::fromValue(QPoint(1, 2))));
QJSManagedValue var2(eng.toManagedValue(QVariant::fromValue(QPoint(1, 2))));
QVERIFY(var1.equals(var2));
}
{
QJSManagedValue var1(eng.toManagedValue(QVariant::fromValue(QPoint(1, 2))));
QJSManagedValue var2(eng.toManagedValue(QVariant::fromValue(QPoint(3, 4))));
QVERIFY(!var1.equals(var2));
}
}
void tst_QJSManagedValue::strictlyEquals()
{
QJSEngine eng;
QObject temp;
QVERIFY(QJSManagedValue().strictlyEquals(QJSManagedValue()));
QJSManagedValue num(eng.toManagedValue(123));
QVERIFY(num.strictlyEquals(eng.toManagedValue(123)));
QVERIFY(!num.strictlyEquals(eng.toManagedValue(321)));
QVERIFY(!num.strictlyEquals(eng.toManagedValue(QStringLiteral("123"))));
QVERIFY(!num.strictlyEquals(eng.toManagedValue(QStringLiteral("321"))));
QVERIFY(!num.strictlyEquals({eng.evaluate(QStringLiteral("new Number(123)")), &eng}));
QVERIFY(!num.strictlyEquals({eng.evaluate(QStringLiteral("new Number(321)")), &eng}));
QVERIFY(!num.strictlyEquals({eng.evaluate(QStringLiteral("new String('123')")), &eng}));
QVERIFY(!num.strictlyEquals({eng.evaluate(QStringLiteral("new String('321')")), &eng}));
QVERIFY(!QJSManagedValue(eng.evaluate(QStringLiteral("new Number(123)")), &eng).strictlyEquals(num));
QVERIFY(!num.strictlyEquals(QJSManagedValue()));
QVERIFY(!QJSManagedValue().strictlyEquals(num));
QJSManagedValue str(eng.toManagedValue(QStringLiteral("123")));
QVERIFY(str.strictlyEquals(eng.toManagedValue(QStringLiteral("123"))));
QVERIFY(!str.strictlyEquals(eng.toManagedValue(QStringLiteral("321"))));
QVERIFY(!str.strictlyEquals(eng.toManagedValue(123)));
QVERIFY(!str.strictlyEquals(eng.toManagedValue(321)));
QVERIFY(!str.strictlyEquals({eng.evaluate(QStringLiteral("new String('123')")), &eng}));
QVERIFY(!str.strictlyEquals({eng.evaluate(QStringLiteral("new String('321')")), &eng}));
QVERIFY(!str.strictlyEquals({eng.evaluate(QStringLiteral("new Number(123)")), &eng}));
QVERIFY(!str.strictlyEquals({eng.evaluate(QStringLiteral("new Number(321)")), &eng}));
QVERIFY(!QJSManagedValue(eng.evaluate(QStringLiteral("new String('123')")), &eng).strictlyEquals(str));
QVERIFY(!str.strictlyEquals(QJSManagedValue()));
QJSManagedValue num2(QJSPrimitiveValue(123), &eng);
QVERIFY(num2.strictlyEquals({QJSPrimitiveValue(123), &eng}));
QVERIFY(!num2.strictlyEquals({QJSPrimitiveValue(321), &eng}));
QVERIFY(!num2.strictlyEquals({QStringLiteral("123"), &eng}));
QVERIFY(!num2.strictlyEquals({QStringLiteral("321"), &eng}));
QVERIFY(!num2.strictlyEquals(QJSManagedValue()));
QJSManagedValue str2(QStringLiteral("123"), &eng);
QVERIFY(str2.strictlyEquals({QStringLiteral("123"), &eng}));
QVERIFY(!str2.strictlyEquals({QStringLiteral("321"), &eng}));
QVERIFY(!str2.strictlyEquals({QJSPrimitiveValue(123), &eng}));
QVERIFY(!str2.strictlyEquals({QJSPrimitiveValue(321), &eng}));
QVERIFY(!str2.strictlyEquals(QJSManagedValue()));
QJSManagedValue date1(eng.toManagedValue(QDate(2000, 1, 1).startOfDay()));
QJSManagedValue date2(eng.toManagedValue(QDate(1999, 1, 1).startOfDay()));
QCOMPARE(date1.strictlyEquals(date2), false);
QCOMPARE(date1.strictlyEquals(date1), true);
QCOMPARE(date2.strictlyEquals(date2), true);
QVERIFY(!date1.strictlyEquals(QJSManagedValue()));
QJSManagedValue undefined(eng.toManagedValue(QVariant()));
QJSManagedValue null(eng.evaluate(QStringLiteral("null")), &eng);
QCOMPARE(undefined.strictlyEquals(undefined), true);
QCOMPARE(null.strictlyEquals(null), true);
QCOMPARE(undefined.strictlyEquals(null), false);
QCOMPARE(null.strictlyEquals(undefined), false);
QVERIFY(!null.strictlyEquals(QJSManagedValue()));
QJSManagedValue sant(eng.toManagedValue(true));
QVERIFY(!sant.strictlyEquals(eng.toManagedValue(1)));
QVERIFY(!sant.strictlyEquals(eng.toManagedValue(QStringLiteral("1"))));
QVERIFY(sant.strictlyEquals(sant));
QVERIFY(!sant.strictlyEquals({eng.evaluate(QStringLiteral("new Number(1)")), &eng}));
QVERIFY(!sant.strictlyEquals({eng.evaluate(QStringLiteral("new String('1')")), &eng}));
QVERIFY(!sant.strictlyEquals({eng.evaluate(QStringLiteral("new Boolean(true)")), &eng}));
QVERIFY(!QJSManagedValue(eng.evaluate(QStringLiteral("new Boolean(true)")), &eng).strictlyEquals(sant));
QVERIFY(!sant.strictlyEquals(eng.toManagedValue(0)));
QVERIFY(!sant.strictlyEquals(undefined));
QVERIFY(!sant.strictlyEquals(null));
QVERIFY(!sant.strictlyEquals(QJSManagedValue()));
QJSManagedValue falskt(eng.toManagedValue(false));
QVERIFY(!falskt.strictlyEquals(eng.toManagedValue(0)));
QVERIFY(!falskt.strictlyEquals(eng.toManagedValue(QStringLiteral("0"))));
QVERIFY(falskt.strictlyEquals(falskt));
QVERIFY(!falskt.strictlyEquals({eng.evaluate(QStringLiteral("new Number(0)")), &eng}));
QVERIFY(!falskt.strictlyEquals({eng.evaluate(QStringLiteral("new String('0')")), &eng}));
QVERIFY(!falskt.strictlyEquals({eng.evaluate(QStringLiteral("new Boolean(false)")), &eng}));
QVERIFY(!QJSManagedValue(eng.evaluate(QStringLiteral("new Boolean(false)")), &eng).strictlyEquals(falskt));
QVERIFY(!falskt.strictlyEquals(sant));
QVERIFY(!falskt.strictlyEquals(undefined));
QVERIFY(!falskt.strictlyEquals(null));
QVERIFY(!falskt.strictlyEquals(QJSManagedValue()));
QVERIFY(!QJSManagedValue(QJSPrimitiveValue(false), &eng).strictlyEquals({QJSPrimitiveValue(123), &eng}));
QVERIFY(!QJSManagedValue(QJSPrimitiveUndefined(), &eng).strictlyEquals({QJSPrimitiveValue(123), &eng}));
QVERIFY(!QJSManagedValue(QJSPrimitiveNull(), &eng).strictlyEquals({QJSPrimitiveValue(123), &eng}));
QVERIFY(!QJSManagedValue(QJSPrimitiveValue(false), &eng).strictlyEquals({QStringLiteral("ciao"), &eng}));
QVERIFY(!QJSManagedValue(QJSPrimitiveUndefined(), &eng).strictlyEquals({QStringLiteral("ciao"), &eng}));
QVERIFY(!QJSManagedValue(QJSPrimitiveNull(), &eng).strictlyEquals({QStringLiteral("ciao"), &eng}));
QVERIFY(eng.toManagedValue(QStringLiteral("ciao")).strictlyEquals(QJSManagedValue(QStringLiteral("ciao"), &eng)));
QVERIFY(QJSManagedValue(QStringLiteral("ciao"), &eng).strictlyEquals(eng.toManagedValue(QStringLiteral("ciao"))));
QVERIFY(!QJSManagedValue(QStringLiteral("ciao"), &eng).strictlyEquals({QJSPrimitiveValue(123), &eng}));
QVERIFY(!QJSManagedValue(QStringLiteral("ciao"), &eng).strictlyEquals(eng.toManagedValue(123)));
QVERIFY(!QJSManagedValue(QJSPrimitiveValue(123), &eng).strictlyEquals({QStringLiteral("ciao"), &eng}));
QVERIFY(!QJSManagedValue(QJSPrimitiveValue(123), &eng).strictlyEquals(eng.toManagedValue(QStringLiteral("ciao"))));
QVERIFY(!eng.toManagedValue(123).strictlyEquals(QJSManagedValue(QStringLiteral("ciao"), &eng)));
QJSManagedValue obj1(eng.newObject(), &eng);
QJSManagedValue obj2(eng.newObject(), &eng);
QCOMPARE(obj1.strictlyEquals(obj2), false);
QCOMPARE(obj2.strictlyEquals(obj1), false);
QCOMPARE(obj1.strictlyEquals(obj1), true);
QCOMPARE(obj2.strictlyEquals(obj2), true);
QVERIFY(!obj1.strictlyEquals(QJSManagedValue()));
QJSManagedValue qobj1(eng.newQObject(&temp), &eng);
QJSManagedValue qobj2(eng.newQObject(&temp), &eng);
QVERIFY(qobj1.strictlyEquals(qobj2));
{
QJSManagedValue var1(eng.toManagedValue(QVariant(QStringList() << QStringLiteral("a"))));
QJSManagedValue var2(eng.toManagedValue(QVariant(QStringList() << QStringLiteral("a"))));
QVERIFY(!var1.strictlyEquals(var2));
}
{
QJSManagedValue var1(eng.toManagedValue(QVariant(QStringList() << QStringLiteral("a"))));
QJSManagedValue var2(eng.toManagedValue(QVariant(QStringList() << QStringLiteral("b"))));
QVERIFY(!var1.strictlyEquals(var2));
}
{
QJSManagedValue var1(eng.toManagedValue(QVariant::fromValue(QPoint(1, 2))));
QJSManagedValue var2(eng.toManagedValue(QVariant::fromValue(QPoint(1, 2))));
QVERIFY(var1.strictlyEquals(var2));
}
{
QJSManagedValue var1(eng.toManagedValue(QVariant::fromValue(QPoint(1, 2))));
QJSManagedValue var2(eng.toManagedValue(QVariant::fromValue(QPoint(3, 4))));
QVERIFY(!var1.strictlyEquals(var2));
}
{
// Import QtQml to trigger the registration of QStringList, which makes it a sequence
// type, rather than a generic JS array.
QQmlEngine qmlEngine;
QQmlComponent c(&qmlEngine);
c.setData("import QtQml\nQtObject {}", QUrl());
QScopedPointer<QObject> obj(c.create());
QVERIFY(!obj.isNull());
QJSManagedValue var1(qmlEngine.toManagedValue(QVariant(QStringList() << QStringLiteral("a"))));
QJSManagedValue var2(qmlEngine.toManagedValue(QVariant(QStringList() << QStringLiteral("a"))));
QVERIFY(!var1.isArray());
QVERIFY(!var2.isArray());
QVERIFY(!var1.strictlyEquals(var2));
}
}
void tst_QJSManagedValue::castToPointer()
{
QJSEngine eng;
{
QRect c(123, 210, 231, 10);
QJSManagedValue v(eng.toManagedValue(&c));
QRect *cp = qjsvalue_cast<QRect*>(v);
QVERIFY(cp != nullptr);
QCOMPARE(*cp, c);
QPoint *bp = qjsvalue_cast<QPoint*>(v);
QVERIFY(!bp);
QJSManagedValue v2(eng.toManagedValue(QVariant::fromValue(cp)));
QCOMPARE(qjsvalue_cast<QRect*>(v2), cp);
}
}
void tst_QJSManagedValue::engineDeleted()
{
std::unique_ptr<QJSEngine> eng = std::make_unique<QJSEngine>();
QObject *temp = new QObject(); // Owned by JS engine, as newQObject() sets JS ownership explicitly
QJSManagedValue v1(eng->toManagedValue(123));
QCOMPARE(v1.type(), QJSManagedValue::Number);
QJSManagedValue v2(eng->toManagedValue(QStringLiteral("ciao")));
QCOMPARE(v2.type(), QJSManagedValue::String);
QJSManagedValue v3(eng->newObject(), eng.get());
QCOMPARE(v3.type(), QJSManagedValue::Object);
QVERIFY(!v3.isNull());
QJSManagedValue v4(eng->newQObject(temp), eng.get());
QCOMPARE(v4.type(), QJSManagedValue::Object);
QVERIFY(!v4.isNull());
QJSManagedValue v5(QStringLiteral("Hello"), eng.get());
QCOMPARE(v2.type(), QJSManagedValue::String);
eng.reset();
// You can still check the type, but anything involving the engine is obviously prohibited.
QCOMPARE(v1.type(), QJSManagedValue::Undefined);
QCOMPARE(v2.type(), QJSManagedValue::Undefined);
QCOMPARE(v3.type(), QJSManagedValue::Undefined);
QCOMPARE(v4.type(), QJSManagedValue::Undefined);
QCOMPARE(v5.type(), QJSManagedValue::Undefined);
}
void tst_QJSManagedValue::valueOfWithClosure()
{
QJSEngine eng;
// valueOf()
{
QJSManagedValue obj(eng.evaluate(QStringLiteral("o = {}; (function(foo) { o.valueOf = function() { return foo; } })(123); o")), &eng);
QCOMPARE(obj.type(), QJSManagedValue::Object);
QVERIFY(!obj.isNull());
QCOMPARE(obj.toNumber(), 123);
}
// toString()
{
QJSManagedValue obj(eng.evaluate(QStringLiteral("o = {}; (function(foo) { o.toString = function() { return foo; } })('ciao'); o")), &eng);
QCOMPARE(obj.type(), QJSManagedValue::Object);
QVERIFY(!obj.isNull());
QCOMPARE(obj.toString(), QStringLiteral("ciao"));
}
}
static int instanceCount = 0;
struct MyType
{
MyType(int n = 0, const char *t=nullptr): number(n), text(t)
{
++instanceCount;
}
MyType(const MyType &other)
: number(other.number), text(other.text)
{
++instanceCount;
}
~MyType()
{
--instanceCount;
}
int number;
const char *text;
};
Q_DECLARE_METATYPE(MyType)
Q_DECLARE_METATYPE(MyType*)
void tst_QJSManagedValue::jsvalueArrayToSequenceType()
{
QCOMPARE(instanceCount, 0);
{
QJSEngine eng {};
QJSManagedValue testObject(eng.newObject(), &eng);
testObject.setProperty(QStringLiteral("test"), 42);
testObject.setProperty(QStringLiteral("mytypeobject"), eng.toScriptValue(QVariant::fromValue(MyType {42, "hello"})));
auto array = eng.newArray(4);
array.setProperty(0, QStringLiteral("Hello World"));
array.setProperty(1, 42);
array.setProperty(2, QJSValue());
array.setProperty(3, QJSValue(std::move(testObject)));
auto asVariant = QVariant::fromValue(array);
QVERIFY(asVariant.canConvert<QVariantList>());
auto asIterable = asVariant.value<QMetaSequence::Iterable>();
for (auto it = asIterable.begin(); it != asIterable.end(); ++it) {
Q_UNUSED(*it);
}
int i = 0;
for (QVariant myVariant: asIterable) {
QCOMPARE(myVariant.isValid(), i != 2);
++i;
}
QVERIFY(asIterable.at(2).value<QVariant>().isNull());
QCOMPARE(asIterable.at(3).value<QVariantMap>().find(QStringLiteral("mytypeobject"))->value<MyType>().number, 42);
QCOMPARE(asIterable.at(0).value<QVariant>().toString(), QStringLiteral("Hello World"));
auto it1 = asIterable.begin();
auto it2 = asIterable.begin();
QCOMPARE((*it1).value<QVariant>().toString(), (*it2).value<QVariant>().toString());
QCOMPARE((*it1).value<QVariant>().toString(), QStringLiteral("Hello World"));
++it2;
QCOMPARE((*it1).value<QVariant>().toString(), QStringLiteral("Hello World"));
QCOMPARE((*it2).value<QVariant>().toInt(), 42);
}
// tests need to be done after engine has been destroyed, else it will hold a reference until
// the gc decides to collect it
QCOMPARE(instanceCount, 0);
}
static QLoggingCategory::CategoryFilter oldFilter;
void logFilter(QLoggingCategory *category)
{
if (qstrcmp(category->categoryName(), "qt.qml.managedvalue") == 0)
category->setEnabled(QtDebugMsg, true);
else if (oldFilter)
oldFilter(category);
}
void tst_QJSManagedValue::stringAndUrl()
{
QJSEngine engine;
const QUrl url(string);
const QJSManagedValue urlValue(engine.toManagedValue(url));
QVERIFY(urlValue.isUrl());
QCOMPARE(urlValue.toString(), string);
QCOMPARE(engine.fromManagedValue<QUrl>(urlValue), url);
const QJSManagedValue stringValue(engine.toManagedValue(string));
QCOMPARE(stringValue.toString(), string);
QCOMPARE(engine.fromManagedValue<QUrl>(stringValue), url);
const QJSManagedValue immediateStringValue(string, &engine);
QCOMPARE(immediateStringValue.toString(), string);
QCOMPARE(engine.fromManagedValue<QUrl>(immediateStringValue), url);
}
void tst_QJSManagedValue::jsFunctionInVariant()
{
QJSEngine engine;
engine.installExtensions(QJSEngine::ConsoleExtension);
QJSManagedValue console(engine.globalObject().property(QStringLiteral("console")), &engine);
QCOMPARE(console.type(), QJSManagedValue::Object);
QVERIFY(!console.isNull());
QJSManagedValue log(console.property(QStringLiteral("log")), &engine);
QVERIFY(log.isFunction());
{
QTest::ignoreMessage(QtDebugMsg, "direct call");
log.callWithInstance(QJSValue(std::move(console)), {"direct call"});
}
}
void tst_QJSManagedValue::stringByIndex()
{
QJSEngine engine;
const QString testString = QStringLiteral("foobar");
QJSManagedValue str(testString, &engine);
for (qsizetype i = 0; i < testString.size(); ++i) {
QVERIFY(str.hasOwnProperty(i));
QVERIFY(str.hasProperty(i));
QVERIFY(str.property(i).strictlyEquals(QJSValue(testString.mid(i, 1))));
str.setProperty(i, QStringLiteral("u")); // ignored
QCOMPARE(str.toString(), testString);
}
QVERIFY(!str.hasOwnProperty(6));
QVERIFY(!str.hasProperty(6));
QVERIFY(!str.hasOwnProperty(16));
QVERIFY(!str.hasProperty(26));
QVERIFY(str.property(6).isUndefined());
QVERIFY(str.property(506).isUndefined());
}
void tst_QJSManagedValue::jsMetaTypes()
{
QJSEngine engine;
QJSManagedValue obj(engine.newObject(), &engine);
QJSManagedValue emptyMetaType = obj.jsMetaType();
QVERIFY(emptyMetaType.jsMetaMembers().isEmpty());
QJSManagedValue emptyObj = emptyMetaType.jsMetaInstantiate();
QVERIFY(emptyObj.isObject());
obj.setProperty("a", 1);
obj.setProperty("b", "foo");
obj.setProperty("llala", true);
obj.setProperty("ccc", QJSValue(std::move(emptyObj)));
const QStringList expectedMembers = { "a", "b", "llala", "ccc" };
QJSManagedValue populatedMetaType = obj.jsMetaType();
QCOMPARE(populatedMetaType.jsMetaMembers(), expectedMembers);
QJSManagedValue populatedObj = populatedMetaType.jsMetaInstantiate(
{"bar", 11, QJSValue(QJSValue::NullValue), 17, "ignored"});
QVERIFY(populatedObj.isObject());
QCOMPARE(populatedObj.property("a").toString(), QStringLiteral("bar"));
QCOMPARE(populatedObj.property("b").toInt(), 11);
QVERIFY(populatedObj.property("llala").isNull());
QCOMPARE(populatedObj.property("ccc").toInt(), 17);
QJSManagedValue halfPopulated = populatedMetaType.jsMetaInstantiate({"one", 111});
QVERIFY(halfPopulated.isObject());
QCOMPARE(halfPopulated.property("a").toString(), QStringLiteral("one"));
QCOMPARE(halfPopulated.property("b").toInt(), 111);
QVERIFY(halfPopulated.property("llala").isUndefined());
QVERIFY(halfPopulated.property("ccc").isUndefined());
}
void tst_QJSManagedValue::exceptionsOnNullAccess()
{
QJSEngine engine;
QJSManagedValue null(QJSValue(QJSValue::NullValue), &engine);
QJSManagedValue undef(QJSValue(QJSValue::UndefinedValue), &engine);
const QString nullReadError = engine.evaluate(
QStringLiteral("var n = null; n.prop")).toString();
const QString nullWriteError = engine.evaluate(
QStringLiteral("var n = null; n.prop = 5")).toString();
const QString undefReadError = engine.evaluate(
QStringLiteral("var n; n.prop")).toString();
const QString undefWriteError = engine.evaluate(
QStringLiteral("var n; n.prop = 5")).toString();
QVERIFY(null.property(QStringLiteral("prop")).isUndefined());
QVERIFY(engine.hasError());
QCOMPARE(engine.catchError().toString(), nullReadError);
null.setProperty(QStringLiteral("prop"), 5);
QVERIFY(engine.hasError());
QCOMPARE(engine.catchError().toString(), nullWriteError);
QVERIFY(undef.property(QStringLiteral("prop")).isUndefined());
QVERIFY(engine.hasError());
QCOMPARE(engine.catchError().toString(), undefReadError);
undef.setProperty(QStringLiteral("prop"), 5);
QVERIFY(engine.hasError());
QCOMPARE(engine.catchError().toString(), undefWriteError);
}
QTEST_MAIN(tst_QJSManagedValue)