aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/jsapi/qjsengine.cpp43
-rw-r--r--src/qml/jsapi/qjsengine.h8
-rw-r--r--src/qml/jsapi/qjsvalue.cpp36
-rw-r--r--src/qml/jsapi/qjsvalue.h2
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp190
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h28
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp79
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h20
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp99
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;