diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 1 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4engine_p.h | 2 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4qmetaobjectwrapper.cpp | 71 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4qmetaobjectwrapper_p.h | 72 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 8 | ||||
| -rw-r--r-- | src/qml/qml/qqmlbinding.cpp | 3 | ||||
| -rw-r--r-- | src/qml/qml/qqmlpropertycache.cpp | 1 | ||||
| -rw-r--r-- | src/qml/qml/qqmltypewrapper.cpp | 104 | ||||
| -rw-r--r-- | src/qml/qml/qqmltypewrapper_p.h | 51 |
9 files changed, 240 insertions, 73 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 8450959de4..bd6251caa9 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -740,6 +740,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) static_cast<DataViewPrototype *>(dataViewPrototype())->init(this, dataViewCtor()); jsObjects[ValueTypeProto] = (Heap::Base *) nullptr; jsObjects[SignalHandlerProto] = (Heap::Base *) nullptr; + jsObjects[TypeWrapperProto] = (Heap::Base *) nullptr; jsObjects[IntrinsicTypedArray_Ctor] = memoryManager->allocate<IntrinsicTypedArrayCtor>(this); jsObjects[IntrinsicTypedArrayProto] = memoryManager->allocate<IntrinsicTypedArrayPrototype>(); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 8e1bd24f6b..0958ab3ab5 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -217,6 +217,7 @@ public: MapProto, IntrinsicTypedArrayProto, ValueTypeProto, + TypeWrapperProto, SignalHandlerProto, IteratorProto, ForInIteratorProto, @@ -342,6 +343,7 @@ public: Object *valueTypeWrapperPrototype() const { return reinterpret_cast<Object *>(jsObjects + ValueTypeProto); } Object *signalHandlerPrototype() const { return reinterpret_cast<Object *>(jsObjects + SignalHandlerProto); } + Object *typeWrapperPrototype() const { return reinterpret_cast<Object *>(jsObjects + TypeWrapperProto); } Object *iteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + IteratorProto); } Object *forInIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ForInIteratorProto); } Object *setIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + SetIteratorProto); } diff --git a/src/qml/jsruntime/qv4qmetaobjectwrapper.cpp b/src/qml/jsruntime/qv4qmetaobjectwrapper.cpp index 13d93c7122..6521c98dbf 100644 --- a/src/qml/jsruntime/qv4qmetaobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qmetaobjectwrapper.cpp @@ -16,38 +16,15 @@ namespace QV4 { void Heap::QMetaObjectWrapper::init(const QMetaObject *metaObject) { FunctionObject::init(); - this->metaObject = metaObject; - constructors = nullptr; - constructorCount = 0; + m_metaObject = metaObject; } void Heap::QMetaObjectWrapper::destroy() { - delete[] constructors; + delete[] m_constructors; + FunctionObject::destroy(); } -void Heap::QMetaObjectWrapper::ensureConstructorsCache() { - - const int count = metaObject->constructorCount(); - if (constructorCount != count) { - delete[] constructors; - constructorCount = count; - if (count == 0) { - constructors = nullptr; - return; - } - constructors = new QQmlPropertyData[count]; - - for (int i = 0; i < count; ++i) { - QMetaMethod method = metaObject->constructor(i); - QQmlPropertyData &d = constructors[i]; - d.load(method); - d.setCoreIndex(i); - } - } -} - - ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) { Scope scope(engine); @@ -57,7 +34,7 @@ ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObj } void QMetaObjectWrapper::init(ExecutionEngine *) { - const QMetaObject & mo = *d()->metaObject; + const QMetaObject &mo = *d()->metaObject(); for (int i = 0; i < mo.enumeratorCount(); i++) { QMetaEnum Enum = mo.enumerator(i); @@ -71,41 +48,49 @@ void QMetaObjectWrapper::init(ExecutionEngine *) { ReturnedValue QMetaObjectWrapper::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { - const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(f); - return This->constructInternal(argv, argc); + Q_ASSERT(f->as<QMetaObjectWrapper>()); + return construct(static_cast<const QMetaObjectWrapper*>(f)->d(), argv, argc); } -ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc) const +ReturnedValue QMetaObjectWrapper::constructInternal( + const QMetaObject *mo, const QQmlPropertyData *constructors, Heap::FunctionObject *d, + const Value *argv, int argc) { + ExecutionEngine *v4 = d->internalClass->engine; - d()->ensureConstructorsCache(); - - ExecutionEngine *v4 = engine(); - const QMetaObject* mo = d()->metaObject; - if (d()->constructorCount == 0) { + if (!constructors) { return v4->throwTypeError(QLatin1String(mo->className()) + QLatin1String(" has no invokable constructor")); } Scope scope(v4); - Scoped<QObjectWrapper> object(scope); + ScopedObject object(scope); JSCallData cData(nullptr, argv, argc); CallData *callData = cData.callData(scope); const QQmlObjectOrGadget objectOrGadget(mo); - if (d()->constructorCount == 1) { + const auto callType = [](QMetaType metaType) { + return metaType.flags() & QMetaType::PointerToQObject + ? QMetaObject::CreateInstance + : QMetaObject::ConstructInPlace; + }; + + const int constructorCount = mo->constructorCount(); + if (constructorCount == 1) { object = QObjectMethod::callPrecise( - objectOrGadget, d()->constructors[0], v4, callData, QMetaObject::CreateInstance); + objectOrGadget, constructors[0], v4, callData, + callType(constructors[0].propType())); } else if (const QQmlPropertyData *ctor = QObjectMethod::resolveOverloaded( - objectOrGadget, d()->constructors, d()->constructorCount, v4, callData)) { + objectOrGadget, constructors, constructorCount, v4, callData)) { object = QObjectMethod::callPrecise( - objectOrGadget, *ctor, v4, callData, QMetaObject::CreateInstance); + objectOrGadget, *ctor, v4, callData, callType(ctor->propType())); } + if (object) { - Scoped<QMetaObjectWrapper> metaObject(scope, this); - object->defineDefaultProperty(v4->id_constructor(), metaObject); - object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this)); + Scoped<FunctionObject> functionObject(scope, d); + object->defineDefaultProperty(v4->id_constructor(), functionObject); + object->setPrototypeOf(functionObject); } return object.asReturnedValue(); diff --git a/src/qml/jsruntime/qv4qmetaobjectwrapper_p.h b/src/qml/jsruntime/qv4qmetaobjectwrapper_p.h index 063bc089e7..c44b18f291 100644 --- a/src/qml/jsruntime/qv4qmetaobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qmetaobjectwrapper_p.h @@ -27,14 +27,57 @@ class QQmlPropertyData; namespace QV4 { namespace Heap { -struct QMetaObjectWrapper : FunctionObject { - const QMetaObject* metaObject; - QQmlPropertyData *constructors; - int constructorCount; - - void init(const QMetaObject* metaObject); +struct QMetaObjectWrapper : FunctionObject +{ + void init(const QMetaObject *metaObject); void destroy(); - void ensureConstructorsCache(); + + const QMetaObject *metaObject() const { return m_metaObject; } + QMetaType metaType() const + { + const QMetaType type = m_metaObject->metaType(); + if (type.flags() & QMetaType::IsGadget) + return type; + + // QObject* is our best guess because we can't get from a metatype to + // the metatype of its pointer. + return QMetaType::fromType<QObject *>(); + } + + const QQmlPropertyData *ensureConstructorsCache( + const QMetaObject *metaObject, QMetaType metaType) + { + Q_ASSERT(metaObject); + if (!m_constructors) + m_constructors = createConstructors(metaObject, metaType); + return m_constructors; + } + + + static const QQmlPropertyData *createConstructors( + const QMetaObject *metaObject, QMetaType metaType) + { + Q_ASSERT(metaObject); + const int count = metaObject->constructorCount(); + if (count == 0) + return nullptr; + + QQmlPropertyData *constructors = new QQmlPropertyData[count]; + + for (int i = 0; i < count; ++i) { + QMetaMethod method = metaObject->constructor(i); + QQmlPropertyData &d = constructors[i]; + d.load(method); + d.setPropType(metaType); + d.setCoreIndex(i); + } + + return constructors; + } + +private: + const QMetaObject *m_metaObject; + const QQmlPropertyData *m_constructors; }; } // namespace Heap @@ -45,7 +88,15 @@ struct Q_QML_EXPORT QMetaObjectWrapper : public FunctionObject V4_NEEDS_DESTROY static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject); - const QMetaObject *metaObject() const { return d()->metaObject; } + const QMetaObject *metaObject() const { return d()->metaObject(); } + + template<typename HeapObject> + ReturnedValue static construct(HeapObject *d, const Value *argv, int argc) + { + const QMetaObject *mo = d->metaObject(); + return constructInternal( + mo, d->ensureConstructorsCache(mo, d->metaType()), d, argv, argc); + } protected: static ReturnedValue virtualCallAsConstructor( @@ -54,7 +105,10 @@ protected: private: void init(ExecutionEngine *engine); - ReturnedValue constructInternal(const Value *argv, int argc) const; + + static ReturnedValue constructInternal( + const QMetaObject *mo, const QQmlPropertyData *constructors, Heap::FunctionObject *d, + const Value *argv, int argc); }; } // namespace QV4 diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 323efa0080..7c05923d66 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -599,7 +599,9 @@ void QObjectWrapper::setProperty( Scope scope(engine); if (ScopedFunctionObject f(scope, value); f) { - if (!f->isBinding()) { + if (f->as<QQmlTypeWrapper>()) { + // Ignore. It's probably a singleton or an attached type. + } else if (!f->isBinding()) { const bool isAliasToAllowed = [&]() { if (property->isAlias()) { const QQmlPropertyIndex originalIndex(property->coreIndex(), -1); @@ -713,7 +715,9 @@ void QObjectWrapper::setProperty( const QMetaType propType = property->propType(); // functions are already handled, except for the QJSValue case - Q_ASSERT(!value.as<FunctionObject>() || propType == QMetaType::fromType<QJSValue>()); + Q_ASSERT(!value.as<FunctionObject>() + || value.as<QV4::QQmlTypeWrapper>() + || propType == QMetaType::fromType<QJSValue>()); if (value.isNull() && property->isQObject()) { PROPERTY_STORE(QObject*, nullptr); diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 47f8e5c429..4dfee0a3c6 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -533,7 +533,8 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, delayedError()->setErrorDescription(QLatin1String("Unable to assign [undefined] to ") + typeName); return false; - } else if (const QV4::FunctionObject *f = result.as<QV4::FunctionObject>()) { + } else if (const QV4::FunctionObject *f = result.as<QV4::FunctionObject>(); + f && !f->as<QV4::QQmlTypeWrapper>()) { if (f->isBinding()) delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); else diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index a225f94a3f..34b4643c99 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -95,7 +95,6 @@ void QQmlPropertyData::load(const QMetaMethod &m) case QMetaMethod::Constructor: m_flags.setIsSignal(false); m_flags.setIsConstructor(true); - setPropType(QMetaType::fromType<QObject *>()); break; default: m_flags.setIsSignal(false); diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 4d62433ffd..4f65197033 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -3,31 +3,33 @@ #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(); + FunctionObject::init(); flags = quint8(m) | quint8(Type); object.init(o); QQmlType::refHandle(type); @@ -38,7 +40,7 @@ void Heap::QQmlTypeWrapper::init( TypeNameMode m, QObject *o, QQmlTypeNameCache *type, const QQmlImportRef *import) { Q_ASSERT(type); - Object::init(); + FunctionObject::init(); flags = quint8(m) | quint8(Namespace); object.init(o); n.typeNamespace = type; @@ -52,6 +54,7 @@ void Heap::QQmlTypeWrapper::destroy() case Type: Q_ASSERT(t.typePrivate); QQmlType::derefHandle(t.typePrivate); + delete[] t.constructors; break; case Namespace: Q_ASSERT(n.typeNamespace); @@ -60,7 +63,7 @@ void Heap::QQmlTypeWrapper::destroy() } object.destroy(); - Object::destroy(); + FunctionObject::destroy(); } QQmlType Heap::QQmlTypeWrapper::type() const @@ -85,6 +88,39 @@ QQmlTypeNameCache::Result Heap::QQmlTypeWrapper::queryNamespace( } +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 { return d()->type().isSingleton(); @@ -159,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 @@ -180,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>( diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index a6837abc60..fa859dd118 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -19,7 +19,8 @@ #include <QtCore/qpointer.h> #include <private/qv4value_p.h> -#include <private/qv4object_p.h> +#include <private/qv4functionobject_p.h> +#include <private/qv4qmetaobjectwrapper_p.h> QT_BEGIN_NAMESPACE @@ -32,7 +33,7 @@ namespace QV4 { namespace Heap { -struct QQmlTypeWrapper : Object { +struct QQmlTypeWrapper : FunctionObject { enum TypeNameMode : quint8 { ExcludeEnums = 0x0, @@ -50,10 +51,26 @@ struct QQmlTypeWrapper : Object { void init(TypeNameMode m, QObject *o, QQmlTypeNameCache *type, const QQmlImportRef *import); void destroy(); + + const QMetaObject *metaObject() const { return type().metaObject(); } + QMetaType metaType() const { return type().typeId(); } + QQmlType type() const; TypeNameMode typeNameMode() const { return TypeNameMode(flags & TypeNameModeMask); } Kind kind() const { return Kind(flags & KindMask); } + const QQmlPropertyData *ensureConstructorsCache( + const QMetaObject *metaObject, QMetaType metaType) + { + Q_ASSERT(kind() == Type); + if (!t.constructors && metaObject) { + t.constructors = QMetaObjectWrapper::createConstructors(metaObject, metaType); + warnIfUncreatable(); + } + return t.constructors; + } + void warnIfUncreatable() const; + QQmlTypeNameCache::Result queryNamespace( const QV4::String *name, QQmlEnginePrivate *enginePrivate) const; @@ -62,7 +79,7 @@ struct QQmlTypeWrapper : Object { union { struct { const QQmlTypePrivate *typePrivate; - const void *reserved; + const QQmlPropertyData *constructors; } t; struct { QQmlTypeNameCache *typeNamespace; @@ -73,6 +90,8 @@ struct QQmlTypeWrapper : Object { quint8 flags; }; +using QQmlTypeConstructor = QQmlTypeWrapper; + struct QQmlScopedEnumWrapper : Object { void init() { Object::init(); } void destroy(); @@ -83,9 +102,10 @@ struct QQmlScopedEnumWrapper : Object { } -struct Q_QML_EXPORT QQmlTypeWrapper : Object +struct Q_QML_EXPORT QQmlTypeWrapper : FunctionObject { - V4_OBJECT2(QQmlTypeWrapper, Object) + V4_OBJECT2(QQmlTypeWrapper, FunctionObject) + V4_PROTOTYPE(typeWrapperPrototype) V4_NEEDS_DESTROY bool isSingleton() const; @@ -95,6 +115,8 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object QVariant toVariant() const; + static void initProto(ExecutionEngine *v4); + static ReturnedValue create(ExecutionEngine *, QObject *, const QQmlType &, Heap::QQmlTypeWrapper::TypeNameMode = Heap::QQmlTypeWrapper::IncludeEnums); static ReturnedValue create(ExecutionEngine *, QObject *, const QQmlRefPointer<QQmlTypeNameCache> &, const QQmlImportRef *, @@ -116,6 +138,25 @@ protected: static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p); static bool virtualIsEqualTo(Managed *that, Managed *o); static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var); + +private: + static ReturnedValue method_hasInstance( + const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toString( + const FunctionObject *b, const Value *thisObject, const Value *, int); +}; + +struct QQmlTypeConstructor : QQmlTypeWrapper +{ + V4_OBJECT2(QQmlTypeConstructor, QQmlTypeWrapper) + + static ReturnedValue virtualCallAsConstructor( + const FunctionObject *f, const Value *argv, int argc, const Value *) + { + Q_ASSERT(f->as<QQmlTypeWrapper>()); + return QMetaObjectWrapper::construct( + static_cast<const QQmlTypeWrapper *>(f)->d(), argv, argc); + } }; struct Q_QML_EXPORT QQmlScopedEnumWrapper : Object |
