diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2021-01-07 16:03:03 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2021-01-12 14:12:39 +0100 |
commit | 1390891709236a2889d8afaeb00e1571767f480b (patch) | |
tree | 24b310fd9d4b3dd747f751f8ea2d340392e33645 | |
parent | 4c67ae2dd9a49348ed5b2d733a60ed0b06c5d203 (diff) |
QJSManagedValue: Allow retrieving and instantiating JS metatypes
... a.k.a InternalClass. This is a faster way of creating multiple
similar objects, compared to a sequence of setProperty() calls.
Change-Id: Iecc8c581e50b9062895dae9500ffe2482d88028e
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r-- | src/qml/jsapi/qjsmanagedvalue.cpp | 104 | ||||
-rw-r--r-- | src/qml/jsapi/qjsmanagedvalue.h | 6 | ||||
-rw-r--r-- | tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp | 37 | ||||
-rw-r--r-- | tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h | 1 |
4 files changed, 147 insertions, 1 deletions
diff --git a/src/qml/jsapi/qjsmanagedvalue.cpp b/src/qml/jsapi/qjsmanagedvalue.cpp index cd93366524..c5651f5dbd 100644 --- a/src/qml/jsapi/qjsmanagedvalue.cpp +++ b/src/qml/jsapi/qjsmanagedvalue.cpp @@ -533,6 +533,17 @@ bool QJSManagedValue::isError() const } /*! + * \internal + * + * Returns \c true if this value represents a JavaScript meta type, or \c false + * otherwise. + */ +bool QJSManagedValue::isJsMetaType() const +{ + return d && d->as<QV4::InternalClass>(); +} + +/*! * Converts the manged value to a string. If the managed value holds a string, * that one is returned. Otherwise a string coercion by JavaScript rules is * performed. @@ -798,7 +809,8 @@ void QJSManagedValue::setProperty(const QString &name, const QJSValue &value) } QV4::Scope scope(obj->engine()); QV4::ScopedString str(scope, obj->engine()->newString(name)); - obj->put(str->toPropertyKey(), QJSValuePrivate::convertToReturnedValue(v4, value)); + obj->put(str->toPropertyKey(), + QJSValuePrivate::convertToReturnedValue(scope.engine, value)); } } @@ -1054,6 +1066,96 @@ QJSValue QJSManagedValue::callAsConstructor(const QJSValueList &arguments) const return QJSValuePrivate::fromReturnedValue(f->callAsConstructor(jsCallData)); } +/*! + * \internal + * + * Retrieves the JavaScript meta type of this value. The JavaScript meta type + * represents the layout of members in an object. Instantiating a meta type is + * faster than re-constructing the same object using a sequence of setProperty() + * calls on a new object. + * + * \sa members(), instantiate() + */ +QJSManagedValue QJSManagedValue::jsMetaType() const +{ + if (!d) + return QJSManagedValue(); + + QJSManagedValue result(v4Engine(d)); + if (QV4::Managed *m = d->as<QV4::Managed>()) + *result.d = m->internalClass(); + + return result; +} + +/*! + * \internal + * + * If this value is a JavaScript meta type, retrieves the names of its members + * The ordering of the names corresponds to the ordering of the values to be + * passed to instantiate(). + * + * If the value is not a meta type, an empty list is returned. + * + * \sa isMetaType(), metaType(), instantiate() + */ +QStringList QJSManagedValue::jsMetaMembers() const +{ + if (!d) + return {}; + + if (QV4::InternalClass *c = d->as<QV4::InternalClass>()) { + const auto heapClass = c->d(); + const int size = heapClass->size; + QStringList result; + result.reserve(size); + for (int i = 0; i < size; ++i) + result.append(heapClass->keyAt(i)); + return result; + } + + return {}; +} + +/*! + * \internal + * + * If this value is a JavaScript meta type, instantiates it using the + * \a values, and returns the result. Otherwise returns undefined. + * + * The values are expected in the same order as the keys in the return value of + * members(), and that is the order in which properties were added to the object + * this meta type originally belongs to. + * + * \sa members(), metaType(), isMetaType(). + */ +QJSManagedValue QJSManagedValue::jsMetaInstantiate(const QJSValueList &values) const +{ + if (!d) + return {}; + + if (QV4::InternalClass *c = d->as<QV4::InternalClass>()) { + QV4::ExecutionEngine *engine = c->engine(); + QJSManagedValue result(engine); + *result.d = c->engine()->newObject(c->d()); + QV4::Object *o = result.d->as<QV4::Object>(); + + for (uint i = 0, end = qMin(qsizetype(c->d()->size), values.size()); i < end; ++i) { + const QJSValue &arg = values[i]; + if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, arg))) { + qWarning("QJSManagedValue::instantiate() failed: " + "Argument was created in different engine."); + return QJSManagedValue(); + } + o->setProperty(i, QJSValuePrivate::convertToReturnedValue(engine, arg)); + } + + return result; + } + + return {}; +} + QJSManagedValue::QJSManagedValue(QV4::ExecutionEngine *engine) : d(engine->memoryManager->m_persistentValues->allocate()) { diff --git a/src/qml/jsapi/qjsmanagedvalue.h b/src/qml/jsapi/qjsmanagedvalue.h index 702f2507f0..7a63e5ffb3 100644 --- a/src/qml/jsapi/qjsmanagedvalue.h +++ b/src/qml/jsapi/qjsmanagedvalue.h @@ -108,6 +108,7 @@ public: bool isQMetaObject() const; bool isDate() const; bool isError() const; + bool isJsMetaType() const; // Native type transformations QString toString() const; @@ -146,6 +147,11 @@ public: QJSValue callWithInstance(const QJSValue &instance, const QJSValueList &arguments = {}) const; QJSValue callAsConstructor(const QJSValueList &arguments = {}) const; + // JavaScript metatypes + QJSManagedValue jsMetaType() const; + QStringList jsMetaMembers() const; + QJSManagedValue jsMetaInstantiate(const QJSValueList &values = {}) const; + private: friend class QJSValue; friend class QJSEngine; diff --git a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp index d245a034bb..75d3ccc12d 100644 --- a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp +++ b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp @@ -1785,4 +1785,41 @@ void tst_QJSManagedValue::stringByIndex() 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()); +} + QTEST_MAIN(tst_QJSManagedValue) diff --git a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h index 041aae56aa..ea0d49589d 100644 --- a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h +++ b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h @@ -108,6 +108,7 @@ private Q_SLOTS: void stringAndUrl(); void jsFunctionInVariant(); void stringByIndex(); + void jsMetaTypes(); private: void newEngine() { engine.reset(new QJSEngine()); } |