From 8c8ec31b7ac79b10b5db0825ee338fc95e24a76f Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Sun, 29 May 2016 21:33:59 +0200 Subject: Add QJSEngine::newQMetaObject QJSEngine::newQMetaObject let us expose QMetaObject to the QJSEngine, allowing to construct QObjects instance from javascript. Additionally, enums values are exposed as property of the QMetaObject wrapper. (The engine takes ownership of the created objects) Change-Id: I5428d4b7061cceacfa89f51e703dce3379b2c329 Reviewed-by: Simon Hausmann --- src/qml/jsapi/qjsengine.cpp | 43 ++++++++ src/qml/jsapi/qjsengine.h | 8 ++ src/qml/jsapi/qjsvalue.cpp | 36 ++++++ src/qml/jsapi/qjsvalue.h | 2 + src/qml/jsruntime/qv4qobjectwrapper.cpp | 190 ++++++++++++++++++++++++++++++-- src/qml/jsruntime/qv4qobjectwrapper_p.h | 28 +++++ src/qml/qml/qqmlpropertycache.cpp | 79 ++++++++----- src/qml/qml/qqmlpropertycache_p.h | 20 +++- 8 files changed, 368 insertions(+), 38 deletions(-) (limited to 'src/qml') 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) 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 @@ -510,6 +521,38 @@ QJSValue QJSEngine::newQObject(QObject *object) return QJSValue(v4, v->asReturnedValue()); } +/*! + \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() + + \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. 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 + QJSValue newQMetaObject() + { + return newQMetaObject(&T::staticMetaObject); + } + template 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 @@ -1233,6 +1233,28 @@ QObject *QJSValue::toQObject() const return wrapper->object(); } +/*! + \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 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 @@ -1286,4 +1308,18 @@ bool QJSValue::isQObject() const return val && val->as() != 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() != 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 #include #include +#include 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 dummy; - args = object.methodParameterTypes(data.coreIndex, dummy, &unknownTypeError); + if (data.isConstructor()) + args = static_cast(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 mo(scope, engine->memoryManager->allocObject(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(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 object(scope); + + if (d()->constructors.size() == 1) { + object = callConstructor(d()->constructors.first(), v4, callData); + } + else { + object = callOverloadedConstructor(v4, callData); + } + Scoped metaObject(scope, this); + object->defineDefaultProperty(v4->id_constructor(), metaObject); + object->setPrototype(const_cast(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 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 *aMetaObject = a->as(); + QMetaObjectWrapper *bMetaObject = b->as(); + 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 constructors; + + void ensureConstructorsCache(); +}; + struct QmlSignalHandler : Object { QmlSignalHandler(QObject *object, int signalIndex); QPointer object; @@ -194,6 +202,26 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject static QPair 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 &du } else { QMetaMethod m = _m.asT2()->method(index); - int argc = m.parameterCount(); - dummy.resize(argc + 1); - dummy[0] = argc; - QList 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())) { - //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 &dummy, QByteArray *unknownTypeError) const { + int argc = m.parameterCount(); + dummy.resize(argc + 1); + dummy[0] = argc; + QList 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())) { + //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 &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 _m; + int *methodParameterTypes(const QMetaMethod &method, QVarLengthArray &dummy, QByteArray *unknownTypeError) const; + }; class QQmlObjectOrGadget: public QQmlMetaObject @@ -455,6 +459,20 @@ public: private: QBiPointer ptr; + +protected: + QQmlObjectOrGadget(const QMetaObject* metaObject) + : QQmlMetaObject(metaObject) + {} + +}; + +class QQmlStaticMetaObject : public QQmlObjectOrGadget { +public: + QQmlStaticMetaObject(const QMetaObject* metaObject) + : QQmlObjectOrGadget(metaObject) + {} + int *constructorParameterTypes(int index, QVarLengthArray &dummy, QByteArray *unknownTypeError) const; }; QQmlPropertyData::QQmlPropertyData() -- cgit v1.2.3