diff options
Diffstat (limited to 'src/qml/qml/qqmltypewrapper.cpp')
-rw-r--r-- | src/qml/qml/qqmltypewrapper.cpp | 225 |
1 files changed, 179 insertions, 46 deletions
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 0d8786a9df..2ab81102c7 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -3,63 +3,122 @@ #include "qqmltypewrapper_p.h" -#include <private/qqmlengine_p.h> +#include <private/qjsvalue_p.h> + #include <private/qqmlcontext_p.h> +#include <private/qqmlengine_p.h> #include <private/qqmlmetaobject_p.h> #include <private/qqmltypedata_p.h> #include <private/qqmlvaluetypewrapper_p.h> -#include <private/qjsvalue_p.h> -#include <private/qv4functionobject_p.h> -#include <private/qv4objectproto_p.h> -#include <private/qv4qobjectwrapper_p.h> #include <private/qv4identifiertable_p.h> #include <private/qv4lookup_p.h> +#include <private/qv4objectproto_p.h> +#include <private/qv4qobjectwrapper_p.h> +#include <private/qv4symbol_p.h> QT_BEGIN_NAMESPACE using namespace QV4; DEFINE_OBJECT_VTABLE(QQmlTypeWrapper); +DEFINE_OBJECT_VTABLE(QQmlTypeConstructor); DEFINE_OBJECT_VTABLE(QQmlScopedEnumWrapper); void Heap::QQmlTypeWrapper::init(TypeNameMode m, QObject *o, const QQmlTypePrivate *type) { Q_ASSERT(type); - Object::init(); - mode = m; + FunctionObject::init(); + flags = quint8(m) | quint8(Type); object.init(o); - typePrivate = type; - QQmlType::refHandle(typePrivate); + QQmlType::refHandle(type); + t.typePrivate = type; } void Heap::QQmlTypeWrapper::init( TypeNameMode m, QObject *o, QQmlTypeNameCache *type, const QQmlImportRef *import) { Q_ASSERT(type); - Object::init(); - mode = m; + FunctionObject::init(); + flags = quint8(m) | quint8(Namespace); object.init(o); - typeNamespace = type; - typeNamespace->addref(); - importNamespace = import; + n.typeNamespace = type; + n.typeNamespace->addref(); + n.importNamespace = import; } void Heap::QQmlTypeWrapper::destroy() { - Q_ASSERT(typePrivate || typeNamespace); - QQmlType::derefHandle(typePrivate); - typePrivate = nullptr; - if (typeNamespace) - typeNamespace->release(); + switch (kind()) { + case Type: + Q_ASSERT(t.typePrivate); + QQmlType::derefHandle(t.typePrivate); + delete[] t.constructors; + break; + case Namespace: + Q_ASSERT(n.typeNamespace); + n.typeNamespace->release(); + break; + } + object.destroy(); - Object::destroy(); + FunctionObject::destroy(); } QQmlType Heap::QQmlTypeWrapper::type() const { - return QQmlType(typePrivate); + switch (kind()) { + case Type: + return QQmlType(t.typePrivate); + case Namespace: + return QQmlType(); + } + + Q_UNREACHABLE_RETURN(QQmlType()); +} + +QQmlTypeNameCache::Result Heap::QQmlTypeWrapper::queryNamespace( + const QV4::String *name, QQmlEnginePrivate *enginePrivate) const +{ + Q_ASSERT(kind() == Namespace); + Q_ASSERT(n.typeNamespace); + Q_ASSERT(n.importNamespace); + return n.typeNamespace->query(name, n.importNamespace, QQmlTypeLoader::get(enginePrivate)); + +} + +template<typename Callback> +void warnWithLocation(const Heap::QQmlTypeWrapper *wrapper, Callback &&callback) +{ + auto log = qWarning().noquote().nospace(); + if (const CppStackFrame *frame = wrapper->internalClass->engine->currentStackFrame) + log << frame->source() << ':' << frame->lineNumber() << ':'; + callback(log.space()); +} + +void Heap::QQmlTypeWrapper::warnIfUncreatable() const +{ + const QQmlType t = type(); + Q_ASSERT(t.isValid()); + + if (t.isValueType()) + return; + + if (t.isSingleton()) { + warnWithLocation(this, [&](QDebug &log) { + log << "You are calling a Q_INVOKABLE constructor of" << t.typeName() + << "which is a singleton in QML."; + }); + return; + } + + if (!t.isCreatable()) { + warnWithLocation(this, [&](QDebug &log) { + log << "You are calling a Q_INVOKABLE constructor of" << t.typeName() + << "which is uncreatable in QML."; + }); + } } bool QQmlTypeWrapper::isSingleton() const @@ -136,17 +195,59 @@ QVariant QQmlTypeWrapper::toVariant() const return QVariant::fromValue<QObject*>(e->singletonInstance<QObject*>(type)); } +ReturnedValue QQmlTypeWrapper::method_hasInstance( + const FunctionObject *, const Value *thisObject, const Value *argv, int argc) +{ + // we want to immediately call instanceOf rather than going through Function + + if (!argc) + return Encode(false); + if (const Object *o = thisObject->as<Object>()) + return o->instanceOf(argv[0]); + return Encode(false); +} + +ReturnedValue QQmlTypeWrapper::method_toString( + const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + const QQmlTypeWrapper *typeWrapper = thisObject->as<QQmlTypeWrapper>(); + if (!typeWrapper) + RETURN_UNDEFINED(); + + const QString name = typeWrapper->d()->type().qmlTypeName(); + return Encode(b->engine()->newString(name.isEmpty() + ? QLatin1String("Unknown Type") + : name)); +} + +void QQmlTypeWrapper::initProto(ExecutionEngine *v4) +{ + if (v4->typeWrapperPrototype()->d_unchecked()) + return; + + Scope scope(v4); + ScopedObject o(scope, v4->newObject()); + + o->defineDefaultProperty(v4->symbol_hasInstance(), method_hasInstance, 1, Attr_ReadOnly); + o->defineDefaultProperty(v4->id_toString(), method_toString, 0); + o->setPrototypeOf(v4->functionPrototype()); + + v4->jsObjects[QV4::ExecutionEngine::TypeWrapperProto] = o->d(); +} // Returns a type wrapper for type t on o. This allows access of enums, and attached properties. ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, const QQmlType &t, Heap::QQmlTypeWrapper::TypeNameMode mode) { Q_ASSERT(t.isValid()); - Scope scope(engine); + initProto(engine); - Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>( - mode, o, t.priv())); - return w.asReturnedValue(); + QV4::MemoryManager *mm = engine->memoryManager; + + if (const QMetaObject *mo = t.metaObject(); !mo || mo->constructorCount() == 0) + return mm->allocate<QQmlTypeWrapper>(mode, o, t.priv())->asReturnedValue(); + + return mm->allocate<QQmlTypeConstructor>(mode, o, t.priv())->asReturnedValue(); } // Returns a type wrapper for importNamespace (of t) on o. This allows nested resolution of a type in a @@ -157,6 +258,8 @@ ReturnedValue QQmlTypeWrapper::create( { Q_ASSERT(t); Q_ASSERT(importNamespace); + initProto(engine); + Scope scope(engine); Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>( @@ -203,7 +306,8 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons if (type.isQObjectSingleton() || type.isCompositeSingleton()) { if (QObject *qobjectSingleton = enginePrivate->singletonInstance<QObject*>(type)) { // check for enum value - const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; + const bool includeEnums + = w->d()->typeNameMode() == Heap::QQmlTypeWrapper::IncludeEnums; if (includeEnums && name->startsWithUpper()) { bool ok = false; int value = enumForSingleton(v4, name, type, &ok); @@ -278,14 +382,11 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons // Fall through to base implementation - } else if (w->d()->typeNamespace) { - Q_ASSERT(w->d()->importNamespace); - QQmlTypeNameCache::Result r = w->d()->typeNamespace->query( - name, w->d()->importNamespace, QQmlTypeLoader::get(enginePrivate)); - + } else if (w->d()->kind() == Heap::QQmlTypeWrapper::Namespace) { + const QQmlTypeNameCache::Result r = w->d()->queryNamespace(name, enginePrivate); if (r.isValid()) { if (r.type.isValid()) { - return create(scope.engine, object, r.type, w->d()->mode); + return create(scope.engine, object, r.type, w->d()->typeNameMode()); } else if (r.scriptIndex != -1) { QV4::ScopedObject scripts(scope, context->importedScripts().valueRef()); return scripts->get(r.scriptIndex); @@ -389,15 +490,16 @@ bool QQmlTypeWrapper::virtualIsEqualTo(Managed *a, Managed *b) return false; } -static ReturnedValue instanceOfQObject(const QV4::QQmlTypeWrapper *typeWrapper, const QObjectWrapper *objectWrapper) +static ReturnedValue instanceOfQObject( + const QV4::QQmlTypeWrapper *typeWrapper, QObject *wrapperObject) { QV4::ExecutionEngine *engine = typeWrapper->internalClass()->engine; // in case the wrapper outlived the QObject* - const QObject *wrapperObject = objectWrapper->object(); if (!wrapperObject) return engine->throwTypeError(); - const QMetaType myTypeId = typeWrapper->d()->type().typeId(); + const QQmlType type = typeWrapper->d()->type(); + const QMetaType myTypeId = type.typeId(); QQmlMetaObject myQmlType; if (!myTypeId.isValid()) { // we're a composite type; a composite type cannot be equal to a @@ -422,7 +524,12 @@ static ReturnedValue instanceOfQObject(const QV4::QQmlTypeWrapper *typeWrapper, const QMetaObject *theirType = wrapperObject->metaObject(); - return QV4::Encode(QQmlMetaObject::canConvert(theirType, myQmlType)); + if (QQmlMetaObject::canConvert(theirType, myQmlType)) + return Encode(true); + else if (type.isValueType()) + return Encode::undefined(); + else + return Encode(false); } ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const Value &var) @@ -431,21 +538,46 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const const QV4::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::QQmlTypeWrapper *>(typeObject); if (const QObjectWrapper *objectWrapper = var.as<QObjectWrapper>()) - return instanceOfQObject(typeWrapper, objectWrapper); + return instanceOfQObject(typeWrapper, objectWrapper->object()); + + if (const QQmlTypeWrapper *varTypeWrapper = var.as<QQmlTypeWrapper>()) { + // Singleton or attachment + if (QObject *varObject = varTypeWrapper->object()) + return instanceOfQObject(typeWrapper, varObject); + } const QQmlType type = typeWrapper->d()->type(); - if (type.isValueType()) { + + // If the target type is an object type we want null. + if (!type.isValueType()) + return Encode(false); + + const auto canCastValueType = [&]() -> bool { if (const QQmlValueTypeWrapper *valueWrapper = var.as<QQmlValueTypeWrapper>()) { - return QV4::Encode(QQmlMetaObject::canConvert(valueWrapper->metaObject(), - type.metaObjectForValueType())); + return QQmlMetaObject::canConvert( + valueWrapper->metaObject(), type.metaObjectForValueType()); } - // We want "foo as valuetype" to return undefined if it doesn't match. - return Encode::undefined(); - } + switch (type.typeId().id()) { + case QMetaType::Void: + return var.isUndefined(); + case QMetaType::QVariant: + return true; // Everything is a var + case QMetaType::Int: + return var.isInteger(); + case QMetaType::Double: + return var.isDouble(); // Integers are also doubles + case QMetaType::QString: + return var.isString(); + case QMetaType::Bool: + return var.isBoolean(); + } - // If the target type is an object type we want null. - return Encode(false); + return false; + }; + + // We want "foo as valuetype" to return undefined if it doesn't match. + return canCastValueType() ? Encode(true) : Encode::undefined(); } ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) @@ -469,7 +601,8 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine()); if (type.isQObjectSingleton() || type.isCompositeSingleton()) { if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) { - const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; + const bool includeEnums + = w->d()->typeNameMode() == Heap::QQmlTypeWrapper::IncludeEnums; if (!includeEnums || !name->startsWithUpper()) { QQmlData *ddata = QQmlData::get(qobjectSingleton, false); if (ddata && ddata->propertyCache) { |