diff options
-rw-r--r-- | src/qml/jsapi/qjsengine.cpp | 43 | ||||
-rw-r--r-- | src/qml/jsapi/qjsengine.h | 8 | ||||
-rw-r--r-- | src/qml/jsapi/qjsvalue.cpp | 36 | ||||
-rw-r--r-- | src/qml/jsapi/qjsvalue.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 190 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper_p.h | 28 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycache.cpp | 79 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycache_p.h | 20 | ||||
-rw-r--r-- | tests/auto/qml/qjsengine/tst_qjsengine.cpp | 99 |
9 files changed, 467 insertions, 38 deletions
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index 09e8bbda55..4404a5d79f 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -166,6 +166,17 @@ Q_DECLARE_METATYPE(QList<int>) properties of the proxy object. No binding code is needed because it is done dynamically using the Qt meta object system. + Use newQMetaObject() to wrap a QMetaObject; this gives you a + "script representation" of a QObject-based class. newQMetaObject() + returns a proxy script object; enum values of the class are available + as properties of the proxy object. + + Constructors exposed to the meta-object system ( using Q_INVOKABLE ) can be + called from the script to create a new QObject instance with + JavaScriptOwnership. + + + \snippet code/src_script_qjsengine.cpp 5 \section1 Extensions @@ -511,6 +522,38 @@ QJSValue QJSEngine::newQObject(QObject *object) } /*! + \since 5.8 + + Creates a JavaScript object that wraps the given QMetaObject + The metaObject must outlive the script engine. It is recommended to only + use this method with static metaobjects. + + + When called as a constructor, a new instance of the class will be created. + Only constructors exposed by Q_INVOKABLE will be visible from the script engine. + + \sa newQObject() +*/ + +QJSValue QJSEngine::newQMetaObject(const QMetaObject* metaObject) { + Q_D(QJSEngine); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(d); + QV4::Scope scope(v4); + QV4::ScopedValue v(scope, QV4::QMetaObjectWrapper::create(v4, metaObject)); + return QJSValue(v4, v->asReturnedValue()); +} + +/*! \fn QJSValue QJSEngine::newQMetaObject<T>() + + \since 5.8 + Creates a JavaScript object that wraps the static QMetaObject associated + with class \c{T}. + + \sa newQObject() +*/ + + +/*! Returns this engine's Global Object. By default, the Global Object contains the built-in objects that are diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h index 6ecd0c7ec0..41c4b81270 100644 --- a/src/qml/jsapi/qjsengine.h +++ b/src/qml/jsapi/qjsengine.h @@ -74,6 +74,14 @@ public: QJSValue newQObject(QObject *object); + QJSValue newQMetaObject(const QMetaObject* metaObject); + + template <typename T> + QJSValue newQMetaObject() + { + return newQMetaObject(&T::staticMetaObject); + } + template <typename T> inline QJSValue toScriptValue(const T &value) { diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 4860908bd3..44746b8c2b 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -1234,6 +1234,28 @@ QObject *QJSValue::toQObject() const } /*! + \since 5.8 + + * If this QJSValue is a QMetaObject, returns the QMetaObject pointer + * that the QJSValue represents; otherwise, returns 0. + * + * \sa isQMetaObject() + */ +const QMetaObject *QJSValue::toQMetaObject() const +{ + QV4::ExecutionEngine *engine = QJSValuePrivate::engine(this); + if (!engine) + return 0; + QV4::Scope scope(engine); + QV4::Scoped<QV4::QMetaObjectWrapper> wrapper(scope, QJSValuePrivate::getValue(this)); + if (!wrapper) + return 0; + + return wrapper->metaObject(); +} + + +/*! Returns a QDateTime representation of this value, in local time. If this QJSValue is not a date, or the value of the date is NaN (Not-a-Number), an invalid QDateTime is returned. @@ -1286,4 +1308,18 @@ bool QJSValue::isQObject() const return val && val->as<QV4::QObjectWrapper>() != 0; } +/*! + \since 5.8 + + Returns true if this QJSValue is a QMetaObject; otherwise returns + false. + + \sa toQMetaObject(), QJSEngine::newQMetaObject() +*/ +bool QJSValue::isQMetaObject() const +{ + QV4::Value *val = QJSValuePrivate::getValue(this); + return val && val->as<QV4::QMetaObjectWrapper>() != 0; +} + QT_END_NAMESPACE diff --git a/src/qml/jsapi/qjsvalue.h b/src/qml/jsapi/qjsvalue.h index e207e1b099..ab20a2607d 100644 --- a/src/qml/jsapi/qjsvalue.h +++ b/src/qml/jsapi/qjsvalue.h @@ -98,6 +98,7 @@ public: bool isUndefined() const; bool isVariant() const; bool isQObject() const; + bool isQMetaObject() const; bool isObject() const; bool isDate() const; bool isRegExp() const; @@ -111,6 +112,7 @@ public: bool toBool() const; QVariant toVariant() const; QObject *toQObject() const; + const QMetaObject *toQMetaObject() const; QDateTime toDateTime() const; bool equals(const QJSValue &other) const; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index c888f1e44c..44c7fc9823 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -75,6 +75,7 @@ #include <QtCore/qvarlengtharray.h> #include <QtCore/qtimer.h> #include <QtCore/qatomic.h> +#include <QtCore/qmetaobject.h> QT_BEGIN_NAMESPACE @@ -1114,7 +1115,8 @@ private: } static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, int returnType, int argCount, - int *argTypes, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) + int *argTypes, QV4::ExecutionEngine *engine, QV4::CallData *callArgs, + QMetaObject::Call callType = QMetaObject::InvokeMetaMethod) { if (argCount > 0) { // Convert all arguments. @@ -1126,7 +1128,7 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index for (int ii = 0; ii < args.count(); ++ii) argData[ii] = args[ii].dataPtr(); - object.metacall(QMetaObject::InvokeMetaMethod, index, argData.data()); + object.metacall(callType, index, argData.data()); return args[0].toValue(engine); @@ -1137,14 +1139,14 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index void *args[] = { arg.dataPtr() }; - object.metacall(QMetaObject::InvokeMetaMethod, index, args); + object.metacall(callType, index, args); return arg.toValue(engine); } else { void *args[] = { 0 }; - object.metacall(QMetaObject::InvokeMetaMethod, index, args); + object.metacall(callType, index, args); return Encode::undefined(); } @@ -1354,7 +1356,8 @@ static const QQmlPropertyData * RelatedMethod(const QQmlObjectOrGadget &object, } static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPropertyData &data, - QV4::ExecutionEngine *engine, QV4::CallData *callArgs) + QV4::ExecutionEngine *engine, QV4::CallData *callArgs, + QMetaObject::Call callType = QMetaObject::InvokeMetaMethod) { QByteArray unknownTypeError; @@ -1371,7 +1374,10 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ int *args = 0; QVarLengthArray<int, 9> dummy; - args = object.methodParameterTypes(data.coreIndex, dummy, &unknownTypeError); + if (data.isConstructor()) + args = static_cast<const QQmlStaticMetaObject&>(object).constructorParameterTypes(data.coreIndex, dummy, &unknownTypeError); + else + args = object.methodParameterTypes(data.coreIndex, dummy, &unknownTypeError); if (!args) { QString typeName = QString::fromLatin1(unknownTypeError); @@ -1384,11 +1390,11 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ return engine->throwError(error); } - return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs); + return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs, callType); } else { - return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs); + return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs, callType); } } @@ -1407,7 +1413,8 @@ Resolve the overloaded method to call. The algorithm works conceptually like th score is constructed by adding the matchScore() result for each of the parameters. */ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QQmlPropertyData &data, - QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache) + QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache, + QMetaObject::Call callType = QMetaObject::InvokeMetaMethod) { int argumentCount = callArgs->argc; @@ -1457,7 +1464,7 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const } while ((attempt = RelatedMethod(object, attempt, dummy, propertyCache)) != 0); if (best.isValid()) { - return CallPrecise(object, best, engine, callArgs); + return CallPrecise(object, best, engine, callArgs, callType); } else { QString error = QLatin1String("Unable to determine callable overload. Candidates are:"); const QQmlPropertyData *candidate = &data; @@ -1884,6 +1891,169 @@ void QObjectMethod::markObjects(Heap::Base *that, ExecutionEngine *e) DEFINE_OBJECT_VTABLE(QObjectMethod); + +Heap::QMetaObjectWrapper::QMetaObjectWrapper(const QMetaObject *metaObject) + : metaObject(metaObject) { + +} + +void Heap::QMetaObjectWrapper::ensureConstructorsCache() { + + const int count = metaObject->constructorCount(); + if (constructors.size() != count) { + constructors.clear(); + constructors.reserve(count); + + for (int i = 0; i < count; ++i) { + QMetaMethod method = metaObject->constructor(i); + QQmlPropertyData d; + d.load(method); + d.coreIndex = i; + constructors << d; + } + } +} + + +ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) { + + QV4::Scope scope(engine); + Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocObject<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue()); + mo->init(engine); + return mo->asReturnedValue(); +} + +void QMetaObjectWrapper::init(ExecutionEngine *) { + const QMetaObject & mo = *d()->metaObject; + + for (int i = 0; i < mo.enumeratorCount(); i++) { + QMetaEnum Enum = mo.enumerator(i); + for (int k = 0; k < Enum.keyCount(); k++) { + const char* key = Enum.key(k); + const int value = Enum.value(k); + defineReadonlyProperty(QLatin1String(key), Primitive::fromInt32(value)); + } + } +} + +ReturnedValue QMetaObjectWrapper::construct(const Managed *m, CallData *callData) +{ + const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(m); + return This->constructInternal(callData); +} + +ReturnedValue QMetaObjectWrapper::constructInternal(CallData * callData) const { + + d()->ensureConstructorsCache(); + + ExecutionEngine *v4 = engine(); + const QMetaObject* mo = d()->metaObject; + if (d()->constructors.isEmpty()) { + return v4->throwTypeError(QStringLiteral("%1 has no invokable constructor") + .arg(QLatin1String(mo->className()))); + } + + Scope scope(v4); + Scoped<QObjectWrapper> object(scope); + + if (d()->constructors.size() == 1) { + object = callConstructor(d()->constructors.first(), v4, callData); + } + else { + object = callOverloadedConstructor(v4, callData); + } + Scoped<QMetaObjectWrapper> metaObject(scope, this); + object->defineDefaultProperty(v4->id_constructor(), metaObject); + object->setPrototype(const_cast<QMetaObjectWrapper*>(this)); + return object.asReturnedValue(); + +} + +ReturnedValue QMetaObjectWrapper::callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const { + + const QMetaObject* mo = d()->metaObject; + const QQmlStaticMetaObject object(mo); + return CallPrecise(object, data, engine, callArgs, QMetaObject::InvokeMetaMethod); +} + + +ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const { + const int numberOfConstructors = d()->constructors.size(); + const int argumentCount = callArgs->argc; + const QQmlStaticMetaObject object(d()->metaObject); + + QQmlPropertyData best; + int bestParameterScore = INT_MAX; + int bestMatchScore = INT_MAX; + + QV4::Scope scope(engine); + QV4::ScopedValue v(scope); + + for (int i = 0; i < numberOfConstructors; i++) { + const QQmlPropertyData & attempt = d()->constructors.at(i); + QVarLengthArray<int, 9> dummy; + int methodArgumentCount = 0; + int *methodArgTypes = 0; + if (attempt.hasArguments()) { + int *args = object.constructorParameterTypes(attempt.coreIndex, dummy, 0); + if (!args) // Must be an unknown argument + continue; + + methodArgumentCount = args[0]; + methodArgTypes = args + 1; + } + + if (methodArgumentCount > argumentCount) + continue; // We don't have sufficient arguments to call this method + + int methodParameterScore = argumentCount - methodArgumentCount; + if (methodParameterScore > bestParameterScore) + continue; // We already have a better option + + int methodMatchScore = 0; + for (int ii = 0; ii < methodArgumentCount; ++ii) + methodMatchScore += MatchScore((v = callArgs->args[ii]), methodArgTypes[ii]); + + if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { + best = attempt; + bestParameterScore = methodParameterScore; + bestMatchScore = methodMatchScore; + } + + if (bestParameterScore == 0 && bestMatchScore == 0) + break; // We can't get better than that + }; + + if (best.isValid()) { + return CallPrecise(object, best, engine, callArgs, QMetaObject::CreateInstance); + } else { + QString error = QLatin1String("Unable to determine callable overload. Candidates are:"); + for (int i = 0; i < numberOfConstructors; i++) { + const QQmlPropertyData & candidate = d()->constructors.at(i); + error += QLatin1String("\n ") + + QString::fromUtf8(d()->metaObject->constructor(candidate.coreIndex) + .methodSignature()); + } + + return engine->throwError(error); + } +} + +bool QMetaObjectWrapper::isEqualTo(Managed *a, Managed *b) +{ + Q_ASSERT(a->as<QMetaObjectWrapper>()); + QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>(); + QMetaObjectWrapper *bMetaObject = b->as<QMetaObjectWrapper>(); + if (!bMetaObject) + return true; + return aMetaObject->metaObject() == bMetaObject->metaObject(); +} + +DEFINE_OBJECT_VTABLE(QMetaObjectWrapper); + + + + Heap::QmlSignalHandler::QmlSignalHandler(QObject *object, int signalIndex) : object(object) , signalIndex(signalIndex) diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index d53bb88d20..4d2b263e97 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -94,6 +94,14 @@ struct QObjectMethod : FunctionObject { const QMetaObject *metaObject(); }; +struct QMetaObjectWrapper : FunctionObject { + QMetaObjectWrapper(const QMetaObject* metaObject); + const QMetaObject* metaObject; + QVector<QQmlPropertyData> constructors; + + void ensureConstructorsCache(); +}; + struct QmlSignalHandler : Object { QmlSignalHandler(QObject *object, int signalIndex); QPointer<QObject> object; @@ -194,6 +202,26 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject static QPair<QObject *, int> extractQtMethod(const QV4::FunctionObject *function); }; + +struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject +{ + V4_OBJECT2(QMetaObjectWrapper, QV4::FunctionObject) + V4_NEEDS_DESTROY + + static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject); + static ReturnedValue construct(const Managed *, CallData *callData); + static bool isEqualTo(Managed *a, Managed *b); + + const QMetaObject *metaObject() const { return d()->metaObject; } + +private: + void init(ExecutionEngine *engine); + ReturnedValue constructInternal(CallData *callData) const; + ReturnedValue callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const; + ReturnedValue callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const; + +}; + struct QmlSignalHandler : public QV4::Object { V4_OBJECT2(QmlSignalHandler, QV4::Object) diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 08d51ecfa1..562e7d1746 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -184,10 +184,16 @@ void QQmlPropertyData::load(const QMetaMethod &m) { coreIndex = m.methodIndex(); arguments = 0; + + propType = m.returnType(); + flags |= IsFunction; if (m.methodType() == QMetaMethod::Signal) flags |= IsSignal; - propType = m.returnType(); + else if (m.methodType() == QMetaMethod::Constructor) { + flags |= IsConstructor; + propType = QMetaType::QObjectStar; + } if (m.parameterCount()) { flags |= HasArguments; @@ -206,11 +212,15 @@ void QQmlPropertyData::load(const QMetaMethod &m) void QQmlPropertyData::lazyLoad(const QMetaMethod &m) { coreIndex = m.methodIndex(); + propType = QMetaType::Void; arguments = 0; flags |= IsFunction; if (m.methodType() == QMetaMethod::Signal) flags |= IsSignal; - propType = QMetaType::Void; + else if (m.methodType() == QMetaMethod::Constructor) { + flags |= IsConstructor; + propType = QMetaType::QObjectStar; + } const char *returnType = m.typeName(); if (!returnType) @@ -1508,39 +1518,49 @@ int *QQmlMetaObject::methodParameterTypes(int index, QVarLengthArray<int, 9> &du } else { QMetaMethod m = _m.asT2()->method(index); - int argc = m.parameterCount(); - dummy.resize(argc + 1); - dummy[0] = argc; - QList<QByteArray> argTypeNames; // Only loaded if needed + return methodParameterTypes(m, dummy, unknownTypeError); - for (int ii = 0; ii < argc; ++ii) { - int type = m.parameterType(ii); - QMetaType::TypeFlags flags = QMetaType::typeFlags(type); - if (flags & QMetaType::IsEnumeration) - type = QVariant::Int; - else if (type == QMetaType::UnknownType || - (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) && - type != qMetaTypeId<QJSValue>())) { - //the UserType clause is to catch registered QFlags) - if (argTypeNames.isEmpty()) - argTypeNames = m.parameterTypes(); - type = EnumType(_m.asT2(), argTypeNames.at(ii), type); - } - if (type == QMetaType::UnknownType) { - if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); - return 0; - } - dummy[ii + 1] = type; - } + } +} - return dummy.data(); +int* QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, QVarLengthArray<int, 9> &dummy, QByteArray *unknownTypeError) const { + int argc = m.parameterCount(); + dummy.resize(argc + 1); + dummy[0] = argc; + QList<QByteArray> argTypeNames; // Only loaded if needed + + for (int ii = 0; ii < argc; ++ii) { + int type = m.parameterType(ii); + QMetaType::TypeFlags flags = QMetaType::typeFlags(type); + if (flags & QMetaType::IsEnumeration) + type = QVariant::Int; + else if (type == QMetaType::UnknownType || + (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) && + type != qMetaTypeId<QJSValue>())) { + //the UserType clause is to catch registered QFlags) + if (argTypeNames.isEmpty()) + argTypeNames = m.parameterTypes(); + type = EnumType(_m.asT2(), argTypeNames.at(ii), type); + } + if (type == QMetaType::UnknownType) { + if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); + return 0; + } + dummy[ii + 1] = type; } + + return dummy.data(); } void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv) const { - if (ptr.isT1()) + if (ptr.isNull()) { + const QMetaObject *metaObject = _m.asT2(); + metaObject->d.static_metacall(0, type, index, argv); + } + else if (ptr.isT1()) { QMetaObject::metacall(ptr.asT1(), type, index, argv); + } else { const QMetaObject *metaObject = _m.asT1()->metaObject(); QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(type, &metaObject, &index); @@ -1548,4 +1568,9 @@ void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv } } +int* QQmlStaticMetaObject::constructorParameterTypes(int index, QVarLengthArray<int, 9> &dummy, QByteArray *unknownTypeError) const { + QMetaMethod m = _m.asT2()->constructor(index); + return methodParameterTypes(m, dummy, unknownTypeError); +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index b2171dd86b..3e84fb3070 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -115,9 +115,10 @@ public: IsSignalHandler = 0x00800000, // Function is a signal handler IsOverload = 0x01000000, // Function is an overload of another function IsCloned = 0x02000000, // The function was marked as cloned + IsConstructor = 0x04000000, // The function was marked is a constructor // Internal QQmlPropertyCache flags - NotFullyResolved = 0x04000000, // True if the type data is to be lazily resolved + NotFullyResolved = 0x08000000, // True if the type data is to be lazily resolved // Flags that are set based on the propType field PropTypeFlagMask = IsQObjectDerived | IsEnumType | IsQList | IsQmlBinding | IsQJSValue | @@ -156,6 +157,7 @@ public: bool isSignalHandler() const { return flags & IsSignalHandler; } bool isOverload() const { return flags & IsOverload; } bool isCloned() const { return flags & IsCloned; } + bool isConstructor() const { return flags & IsConstructor; } bool hasOverride() const { return !(flags & IsValueTypeVirtual) && !(flags & HasAccessors) && @@ -437,6 +439,8 @@ public: protected: QBiPointer<QQmlPropertyCache, const QMetaObject> _m; + int *methodParameterTypes(const QMetaMethod &method, QVarLengthArray<int, 9> &dummy, QByteArray *unknownTypeError) const; + }; class QQmlObjectOrGadget: public QQmlMetaObject @@ -455,6 +459,20 @@ public: private: QBiPointer<QObject, void> ptr; + +protected: + QQmlObjectOrGadget(const QMetaObject* metaObject) + : QQmlMetaObject(metaObject) + {} + +}; + +class QQmlStaticMetaObject : public QQmlObjectOrGadget { +public: + QQmlStaticMetaObject(const QMetaObject* metaObject) + : QQmlObjectOrGadget(metaObject) + {} + int *constructorParameterTypes(int index, QVarLengthArray<int, 9> &dummy, QByteArray *unknownTypeError) const; }; QQmlPropertyData::QQmlPropertyData() diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 99c18f91a1..43f43764d5 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -73,6 +73,7 @@ private slots: void newQObject(); void newQObject_ownership(); void newQObject_deletedEngine(); + void newQMetaObject(); void exceptionInSlot(); void globalObjectProperties(); void globalObjectEquals(); @@ -712,6 +713,104 @@ void tst_QJSEngine::newQObject_deletedEngine() QTRY_VERIFY(spy.count()); } +class TestQMetaObject : public QObject { + Q_OBJECT + Q_PROPERTY(int called READ called) +public: + enum Enum1 { + Zero = 0, + One, + Two + }; + enum Enum2 { + A = 0, + B, + C + }; + Q_ENUMS(Enum1 Enum2) + + Q_INVOKABLE TestQMetaObject() + : m_called(1) { + } + Q_INVOKABLE TestQMetaObject(int) + : m_called(2) { + } + Q_INVOKABLE TestQMetaObject(QString) + : m_called(3) { + } + Q_INVOKABLE TestQMetaObject(QString, int) + : m_called(4) { + } + int called() const { + return m_called; + } +private: + int m_called; +}; + +void tst_QJSEngine::newQMetaObject() { + { + QJSEngine engine; + QJSValue metaObject = engine.newQMetaObject(&TestQMetaObject::staticMetaObject); + QCOMPARE(metaObject.isNull(), false); + QCOMPARE(metaObject.isObject(), true); + QCOMPARE(metaObject.isQObject(), false); + QCOMPARE(metaObject.isCallable(), true); + QCOMPARE(metaObject.isQMetaObject(), true); + + QCOMPARE(metaObject.toQMetaObject(), &TestQMetaObject::staticMetaObject); + + QVERIFY(metaObject.strictlyEquals(engine.newQMetaObject<TestQMetaObject>())); + + + { + auto result = metaObject.callAsConstructor(); + if (result.isError()) + qDebug() << result.toString(); + QCOMPARE(result.isError(), false); + QCOMPARE(result.isNull(), false); + QCOMPARE(result.isObject(), true); + QCOMPARE(result.isQObject(), true); + QVERIFY(result.property("constructor").strictlyEquals(metaObject)); + QVERIFY(result.prototype().strictlyEquals(metaObject)); + + + QCOMPARE(result.property("called").toInt(), 1); + + } + + QJSValue integer(42); + QJSValue string("foo"); + + { + auto result = metaObject.callAsConstructor({integer}); + QCOMPARE(result.property("called").toInt(), 2); + } + + { + auto result = metaObject.callAsConstructor({string}); + QCOMPARE(result.property("called").toInt(), 3); + } + + { + auto result = metaObject.callAsConstructor({string, integer}); + QCOMPARE(result.property("called").toInt(), 4); + } + } + + { + QJSEngine engine; + QJSValue metaObject = engine.newQMetaObject(&TestQMetaObject::staticMetaObject); + QCOMPARE(metaObject.property("Zero").toInt(), 0); + QCOMPARE(metaObject.property("One").toInt(), 1); + QCOMPARE(metaObject.property("Two").toInt(), 2); + QCOMPARE(metaObject.property("A").toInt(), 0); + QCOMPARE(metaObject.property("B").toInt(), 1); + QCOMPARE(metaObject.property("C").toInt(), 2); + } + +} + void tst_QJSEngine::exceptionInSlot() { QJSEngine engine; |