aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml')
-rw-r--r--src/qml/qml/ftw/qbipointer_p.h12
-rw-r--r--src/qml/qml/ftw/qhashedstring_p.h4
-rw-r--r--src/qml/qml/ftw/qintrusivelist_p.h285
-rw-r--r--src/qml/qml/ftw/qqmlnullablevalue_p.h71
-rw-r--r--src/qml/qml/ftw/qqmlrefcount_p.h68
-rw-r--r--src/qml/qml/ftw/qqmlthread.cpp32
-rw-r--r--src/qml/qml/ftw/qqmlthread_p.h1
-rw-r--r--src/qml/qml/ftw/qrecyclepool_p.h34
-rw-r--r--src/qml/qml/qqml.cpp1164
-rw-r--r--src/qml/qml/qqml.h71
-rw-r--r--src/qml/qml/qqmlabstractbinding_p.h2
-rw-r--r--src/qml/qml/qqmlanybinding_p.h7
-rw-r--r--src/qml/qml/qqmlapplicationengine.cpp43
-rw-r--r--src/qml/qml/qqmlapplicationengine_p.h2
-rw-r--r--src/qml/qml/qqmlbinding.cpp25
-rw-r--r--src/qml/qml/qqmlbinding_p.h2
-rw-r--r--src/qml/qml/qqmlboundsignal.cpp13
-rw-r--r--src/qml/qml/qqmlboundsignal_p.h7
-rw-r--r--src/qml/qml/qqmlbuiltinfunctions.cpp176
-rw-r--r--src/qml/qml/qqmlbuiltinfunctions_p.h32
-rw-r--r--src/qml/qml/qqmlcomponent.cpp257
-rw-r--r--src/qml/qml/qqmlcomponent.h11
-rw-r--r--src/qml/qml/qqmlcomponent_p.h21
-rw-r--r--src/qml/qml/qqmlcomponentandaliasresolver_p.h484
-rw-r--r--src/qml/qml/qqmlcomponentattached_p.h13
-rw-r--r--src/qml/qml/qqmlcontext.cpp129
-rw-r--r--src/qml/qml/qqmlcontext.h1
-rw-r--r--src/qml/qml/qqmlcontextdata_p.h34
-rw-r--r--src/qml/qml/qqmlcustomparser.cpp21
-rw-r--r--src/qml/qml/qqmlcustomparser_p.h3
-rw-r--r--src/qml/qml/qqmldata_p.h90
-rw-r--r--src/qml/qml/qqmldatablob.cpp26
-rw-r--r--src/qml/qml/qqmldatablob_p.h5
-rw-r--r--src/qml/qml/qqmldelayedcallqueue.cpp9
-rw-r--r--src/qml/qml/qqmldelayedcallqueue_p.h6
-rw-r--r--src/qml/qml/qqmlengine.cpp435
-rw-r--r--src/qml/qml/qqmlengine.h2
-rw-r--r--src/qml/qml/qqmlengine_p.h73
-rw-r--r--src/qml/qml/qqmlexpression.cpp8
-rw-r--r--src/qml/qml/qqmlexpression.h1
-rw-r--r--src/qml/qml/qqmlextensionplugin.cpp20
-rw-r--r--src/qml/qml/qqmlfile.cpp185
-rw-r--r--src/qml/qml/qqmlfile.h6
-rw-r--r--src/qml/qml/qqmlfileselector_p.h6
-rw-r--r--src/qml/qml/qqmlfinalizer_p.h2
-rw-r--r--src/qml/qml/qqmlglobal.cpp780
-rw-r--r--src/qml/qml/qqmlglobal_p.h59
-rw-r--r--src/qml/qml/qqmlguard_p.h14
-rw-r--r--src/qml/qml/qqmlimport.cpp315
-rw-r--r--src/qml/qml/qqmlimport_p.h51
-rw-r--r--src/qml/qml/qqmlincubator.cpp58
-rw-r--r--src/qml/qml/qqmlincubator_p.h4
-rw-r--r--src/qml/qml/qqmlirloader.cpp15
-rw-r--r--src/qml/qml/qqmlirloader_p.h2
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp46
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h6
-rw-r--r--src/qml/qml/qqmllist.cpp2
-rw-r--r--src/qml/qml/qqmllist.h39
-rw-r--r--src/qml/qml/qqmllist_p.h2
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp217
-rw-r--r--src/qml/qml/qqmllistwrapper_p.h31
-rw-r--r--src/qml/qml/qqmllocale.cpp602
-rw-r--r--src/qml/qml/qqmllocale_p.h236
-rw-r--r--src/qml/qml/qqmlloggingcategory.cpp133
-rw-r--r--src/qml/qml/qqmlloggingcategory_p.h72
-rw-r--r--src/qml/qml/qqmlloggingcategorybase_p.h47
-rw-r--r--src/qml/qml/qqmlmetamoduleregistration.cpp26
-rw-r--r--src/qml/qml/qqmlmetaobject.cpp62
-rw-r--r--src/qml/qml/qqmlmetaobject_p.h107
-rw-r--r--src/qml/qml/qqmlmetatype.cpp621
-rw-r--r--src/qml/qml/qqmlmetatype_p.h179
-rw-r--r--src/qml/qml/qqmlmetatypedata.cpp17
-rw-r--r--src/qml/qml/qqmlmetatypedata_p.h10
-rw-r--r--src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp15
-rw-r--r--src/qml/qml/qqmlnotifier_p.h2
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp225
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h41
-rw-r--r--src/qml/qml/qqmlopenmetaobject.cpp2
-rw-r--r--src/qml/qml/qqmlopenmetaobject_p.h7
-rw-r--r--src/qml/qml/qqmlplatform.cpp7
-rw-r--r--src/qml/qml/qqmlplatform_p.h5
-rw-r--r--src/qml/qml/qqmlpluginimporter.cpp15
-rw-r--r--src/qml/qml/qqmlprivate.h214
-rw-r--r--src/qml/qml/qqmlproperty.cpp198
-rw-r--r--src/qml/qml/qqmlproperty.h1
-rw-r--r--src/qml/qml/qqmlproperty_p.h4
-rw-r--r--src/qml/qml/qqmlpropertybinding.cpp15
-rw-r--r--src/qml/qml/qqmlpropertybinding_p.h42
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp130
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h15
-rw-r--r--src/qml/qml/qqmlpropertycachecreator.cpp110
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h529
-rw-r--r--src/qml/qml/qqmlpropertycachevector_p.h36
-rw-r--r--src/qml/qml/qqmlpropertydata_p.h112
-rw-r--r--src/qml/qml/qqmlpropertyresolver.cpp7
-rw-r--r--src/qml/qml/qqmlpropertytopropertybinding_p.h2
-rw-r--r--src/qml/qml/qqmlpropertyvalidator.cpp82
-rw-r--r--src/qml/qml/qqmlpropertyvalidator_p.h8
-rw-r--r--src/qml/qml/qqmlpropertyvalueinterceptor_p.h2
-rw-r--r--src/qml/qml/qqmlproxymetaobject.cpp12
-rw-r--r--src/qml/qml/qqmlproxymetaobject_p.h7
-rw-r--r--src/qml/qml/qqmlregistration.h13
-rw-r--r--src/qml/qml/qqmlscriptblob.cpp62
-rw-r--r--src/qml/qml/qqmlscriptblob_p.h3
-rw-r--r--src/qml/qml/qqmlscriptdata.cpp11
-rw-r--r--src/qml/qml/qqmlscriptdata_p.h15
-rw-r--r--src/qml/qml/qqmlscriptstring.cpp20
-rw-r--r--src/qml/qml/qqmlscriptstring.h1
-rw-r--r--src/qml/qml/qqmlstringconverters.cpp68
-rw-r--r--src/qml/qml/qqmlstringconverters_p.h91
-rw-r--r--src/qml/qml/qqmltype.cpp545
-rw-r--r--src/qml/qml/qqmltype_p.h53
-rw-r--r--src/qml/qml/qqmltype_p_p.h234
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp505
-rw-r--r--src/qml/qml/qqmltypecompiler_p.h75
-rw-r--r--src/qml/qml/qqmltypedata.cpp463
-rw-r--r--src/qml/qml/qqmltypedata_p.h35
-rw-r--r--src/qml/qml/qqmltypeloader.cpp154
-rw-r--r--src/qml/qml/qqmltypeloader_p.h25
-rw-r--r--src/qml/qml/qqmltypeloaderqmldircontent.cpp4
-rw-r--r--src/qml/qml/qqmltypeloaderqmldircontent_p.h9
-rw-r--r--src/qml/qml/qqmltypeloaderthread.cpp10
-rw-r--r--src/qml/qml/qqmltypemodule_p.h2
-rw-r--r--src/qml/qml/qqmltypenamecache_p.h32
-rw-r--r--src/qml/qml/qqmltypenotavailable.cpp15
-rw-r--r--src/qml/qml/qqmltypenotavailable_p.h34
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp349
-rw-r--r--src/qml/qml/qqmltypewrapper_p.h91
-rw-r--r--src/qml/qml/qqmlvaluetype.cpp19
-rw-r--r--src/qml/qml/qqmlvaluetype_p.h67
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp121
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h55
-rw-r--r--src/qml/qml/qqmlvme_p.h2
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp331
-rw-r--r--src/qml/qml/qqmlvmemetaobject_p.h90
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp128
136 files changed, 7890 insertions, 5339 deletions
diff --git a/src/qml/qml/ftw/qbipointer_p.h b/src/qml/qml/ftw/qbipointer_p.h
index 4039d6b60d..1597b9e4fc 100644
--- a/src/qml/qml/ftw/qbipointer_p.h
+++ b/src/qml/qml/ftw/qbipointer_p.h
@@ -17,6 +17,8 @@
#include <QtCore/private/qglobal_p.h>
+#include <QtCore/qhashfunctions.h>
+
QT_BEGIN_NAMESPACE
namespace QtPrivate {
@@ -43,17 +45,17 @@ template <> struct QFlagPointerAlignment<void>
template<typename T, typename T2>
class QBiPointer {
public:
- constexpr QBiPointer() noexcept = default;
+ Q_NODISCARD_CTOR constexpr QBiPointer() noexcept = default;
~QBiPointer() noexcept = default;
- QBiPointer(const QBiPointer &o) noexcept = default;
- QBiPointer(QBiPointer &&o) noexcept = default;
+ Q_NODISCARD_CTOR QBiPointer(const QBiPointer &o) noexcept = default;
+ Q_NODISCARD_CTOR QBiPointer(QBiPointer &&o) noexcept = default;
QBiPointer<T, T2> &operator=(const QBiPointer<T, T2> &o) noexcept = default;
QBiPointer<T, T2> &operator=(QBiPointer<T, T2> &&o) noexcept = default;
void swap(QBiPointer &other) noexcept { std::swap(ptr_value, other.ptr_value); }
- inline QBiPointer(T *);
- inline QBiPointer(T2 *);
+ Q_NODISCARD_CTOR inline QBiPointer(T *);
+ Q_NODISCARD_CTOR inline QBiPointer(T2 *);
inline bool isNull() const;
inline bool isT1() const;
diff --git a/src/qml/qml/ftw/qhashedstring_p.h b/src/qml/qml/ftw/qhashedstring_p.h
index 4de5524a10..78ce738f3b 100644
--- a/src/qml/qml/ftw/qhashedstring_p.h
+++ b/src/qml/qml/ftw/qhashedstring_p.h
@@ -26,7 +26,7 @@
QT_BEGIN_NAMESPACE
class QHashedStringRef;
-class Q_QML_PRIVATE_EXPORT QHashedString : public QString
+class Q_QML_EXPORT QHashedString : public QString
{
public:
inline QHashedString();
@@ -56,7 +56,7 @@ private:
};
class QHashedCStringRef;
-class Q_QML_PRIVATE_EXPORT QHashedStringRef
+class Q_QML_EXPORT QHashedStringRef
{
public:
inline QHashedStringRef();
diff --git a/src/qml/qml/ftw/qintrusivelist_p.h b/src/qml/qml/ftw/qintrusivelist_p.h
index 0d4e428fa0..1170370fae 100644
--- a/src/qml/qml/ftw/qintrusivelist_p.h
+++ b/src/qml/qml/ftw/qintrusivelist_p.h
@@ -19,221 +19,140 @@
QT_BEGIN_NAMESPACE
-class QIntrusiveListNode;
-template<class N, QIntrusiveListNode N::*member>
-class QIntrusiveList
+class QIntrusiveListNode
{
public:
- inline QIntrusiveList();
- inline ~QIntrusiveList();
-
- inline bool isEmpty() const;
- inline void insert(N *n);
- inline void remove(N *n);
- inline bool contains(N *) const;
-
- class iterator {
- public:
- inline iterator();
- inline iterator(N *value);
-
- inline N *operator*() const;
- inline N *operator->() const;
- inline bool operator==(const iterator &other) const;
- inline bool operator!=(const iterator &other) const;
- inline iterator &operator++();
-
- inline iterator &erase();
-
- private:
- N *_value;
- };
- typedef iterator Iterator;
-
- inline N *first() const;
- static inline N *next(N *current);
+ ~QIntrusiveListNode() { remove(); }
+
+ void remove()
+ {
+ if (_prev) *_prev = _next;
+ if (_next) _next->_prev = _prev;
+ _prev = nullptr;
+ _next = nullptr;
+ }
- inline iterator begin();
- inline iterator end();
+ bool isInList() const { return _prev != nullptr; }
private:
- static inline N *nodeToN(QIntrusiveListNode *node);
-
- QIntrusiveListNode *__first = nullptr;
-};
-
-class QIntrusiveListNode
-{
-public:
- inline QIntrusiveListNode();
- inline ~QIntrusiveListNode();
-
- inline void remove();
- inline bool isInList() const;
+ template<class N, QIntrusiveListNode N::*member>
+ friend class QIntrusiveList;
QIntrusiveListNode *_next = nullptr;
QIntrusiveListNode**_prev = nullptr;
};
template<class N, QIntrusiveListNode N::*member>
-QIntrusiveList<N, member>::iterator::iterator()
-: _value(nullptr)
-{
-}
-
-template<class N, QIntrusiveListNode N::*member>
-QIntrusiveList<N, member>::iterator::iterator(N *value)
-: _value(value)
-{
-}
-
-template<class N, QIntrusiveListNode N::*member>
-N *QIntrusiveList<N, member>::iterator::operator*() const
-{
- return _value;
-}
-
-template<class N, QIntrusiveListNode N::*member>
-N *QIntrusiveList<N, member>::iterator::operator->() const
-{
- return _value;
-}
-
-template<class N, QIntrusiveListNode N::*member>
-bool QIntrusiveList<N, member>::iterator::operator==(const iterator &other) const
+class QIntrusiveList
{
- return other._value == _value;
-}
+private:
+ template<typename O>
+ class iterator_impl {
+ public:
+ iterator_impl() = default;
+ iterator_impl(O value) : _value(value) {}
+
+ O operator*() const { return _value; }
+ O operator->() const { return _value; }
+ bool operator==(const iterator_impl &other) const { return other._value == _value; }
+ bool operator!=(const iterator_impl &other) const { return other._value != _value; }
+ iterator_impl &operator++()
+ {
+ _value = QIntrusiveList<N, member>::next(_value);
+ return *this;
+ }
+
+ protected:
+ O _value = nullptr;
+ };
-template<class N, QIntrusiveListNode N::*member>
-bool QIntrusiveList<N, member>::iterator::operator!=(const iterator &other) const
-{
- return other._value != _value;
-}
+public:
+ class iterator : public iterator_impl<N *>
+ {
+ public:
+ iterator() = default;
+ iterator(N *value) : iterator_impl<N *>(value) {}
+
+ iterator &erase()
+ {
+ N *old = this->_value;
+ this->_value = QIntrusiveList<N, member>::next(this->_value);
+ (old->*member).remove();
+ return *this;
+ }
+ };
-template<class N, QIntrusiveListNode N::*member>
-typename QIntrusiveList<N, member>::iterator &QIntrusiveList<N, member>::iterator::operator++()
-{
- _value = QIntrusiveList<N, member>::next(_value);
- return *this;
-}
+ using const_iterator = iterator_impl<const N *>;
-template<class N, QIntrusiveListNode N::*member>
-typename QIntrusiveList<N, member>::iterator &QIntrusiveList<N, member>::iterator::erase()
-{
- N *old = _value;
- _value = QIntrusiveList<N, member>::next(_value);
- (old->*member).remove();
- return *this;
-}
+ using Iterator = iterator;
+ using ConstIterator = const_iterator;
-template<class N, QIntrusiveListNode N::*member>
-QIntrusiveList<N, member>::QIntrusiveList()
+ ~QIntrusiveList() { while (__first) __first->remove(); }
-{
-}
+ bool isEmpty() const { return __first == nullptr; }
-template<class N, QIntrusiveListNode N::*member>
-QIntrusiveList<N, member>::~QIntrusiveList()
-{
- while (__first) __first->remove();
-}
+ void insert(N *n)
+ {
+ QIntrusiveListNode *nnode = &(n->*member);
+ nnode->remove();
-template<class N, QIntrusiveListNode N::*member>
-bool QIntrusiveList<N, member>::isEmpty() const
-{
- return __first == nullptr;
-}
+ nnode->_next = __first;
+ if (nnode->_next) nnode->_next->_prev = &nnode->_next;
+ __first = nnode;
+ nnode->_prev = &__first;
+ }
-template<class N, QIntrusiveListNode N::*member>
-void QIntrusiveList<N, member>::insert(N *n)
-{
- QIntrusiveListNode *nnode = &(n->*member);
- nnode->remove();
+ void remove(N *n)
+ {
+ QIntrusiveListNode *nnode = &(n->*member);
+ nnode->remove();
+ }
- nnode->_next = __first;
- if (nnode->_next) nnode->_next->_prev = &nnode->_next;
- __first = nnode;
- nnode->_prev = &__first;
-}
+ bool contains(const N *n) const
+ {
+ QIntrusiveListNode *nnode = __first;
+ while (nnode) {
+ if (nodeToN(nnode) == n)
+ return true;
+ nnode = nnode->_next;
+ }
+ return false;
+ }
-template<class N, QIntrusiveListNode N::*member>
-void QIntrusiveList<N, member>::remove(N *n)
-{
- QIntrusiveListNode *nnode = &(n->*member);
- nnode->remove();
-}
+ const N *first() const { return __first ? nodeToN(__first) : nullptr; }
+ N *first() { return __first ? nodeToN(__first) : nullptr; }
-template<class N, QIntrusiveListNode N::*member>
-bool QIntrusiveList<N, member>::contains(N *n) const
-{
- QIntrusiveListNode *nnode = __first;
- while (nnode) {
- if (nodeToN(nnode) == n)
- return true;
- nnode = nnode->_next;
+ template<typename O>
+ static O next(O current)
+ {
+ QIntrusiveListNode *nextnode = (current->*member)._next;
+ return nextnode ? nodeToN(nextnode) : nullptr;
}
- return false;
-}
-template<class N, QIntrusiveListNode N::*member>
-N *QIntrusiveList<N, member>::first() const
-{
- return __first?nodeToN(__first):nullptr;
-}
+ iterator begin() { return __first ? iterator(nodeToN(__first)) : iterator(); }
+ iterator end() { return iterator(); }
-template<class N, QIntrusiveListNode N::*member>
-N *QIntrusiveList<N, member>::next(N *current)
-{
- QIntrusiveListNode *nextnode = (current->*member)._next;
- N *nextstruct = nextnode?nodeToN(nextnode):nullptr;
- return nextstruct;
-}
+ const_iterator begin() const
+ {
+ return __first ? const_iterator(nodeToN(__first)) : const_iterator();
+ }
-template<class N, QIntrusiveListNode N::*member>
-typename QIntrusiveList<N, member>::iterator QIntrusiveList<N, member>::begin()
-{
- return __first?iterator(nodeToN(__first)):iterator();
-}
+ const_iterator end() const { return const_iterator(); }
-template<class N, QIntrusiveListNode N::*member>
-typename QIntrusiveList<N, member>::iterator QIntrusiveList<N, member>::end()
-{
- return iterator();
-}
+private:
-template<class N, QIntrusiveListNode N::*member>
-N *QIntrusiveList<N, member>::nodeToN(QIntrusiveListNode *node)
-{
- QT_WARNING_PUSH
+ static N *nodeToN(QIntrusiveListNode *node)
+ {
+ QT_WARNING_PUSH
#if defined(Q_CC_CLANG) && Q_CC_CLANG >= 1300
- QT_WARNING_DISABLE_CLANG("-Wnull-pointer-subtraction")
+ QT_WARNING_DISABLE_CLANG("-Wnull-pointer-subtraction")
#endif
- return (N *)((char *)node - ((char *)&(((N *)nullptr)->*member) - (char *)nullptr));
- QT_WARNING_POP
-}
-
-QIntrusiveListNode::QIntrusiveListNode()
-{
-}
-
-QIntrusiveListNode::~QIntrusiveListNode()
-{
- remove();
-}
-
-void QIntrusiveListNode::remove()
-{
- if (_prev) *_prev = _next;
- if (_next) _next->_prev = _prev;
- _prev = nullptr;
- _next = nullptr;
-}
+ return (N *)((char *)node - ((char *)&(((N *)nullptr)->*member) - (char *)nullptr));
+ QT_WARNING_POP
+ }
-bool QIntrusiveListNode::isInList() const
-{
- return _prev != nullptr;
-}
+ QIntrusiveListNode *__first = nullptr;
+};
QT_END_NAMESPACE
diff --git a/src/qml/qml/ftw/qqmlnullablevalue_p.h b/src/qml/qml/ftw/qqmlnullablevalue_p.h
index 2c1b94b556..62899e4644 100644
--- a/src/qml/qml/ftw/qqmlnullablevalue_p.h
+++ b/src/qml/qml/ftw/qqmlnullablevalue_p.h
@@ -22,22 +22,69 @@ QT_BEGIN_NAMESPACE
template<typename T>
struct QQmlNullableValue
{
- QQmlNullableValue()
- : value(T()) {}
+ QQmlNullableValue() = default;
+
QQmlNullableValue(const QQmlNullableValue<T> &o)
- : isNull(o.isNull), value(o.value) {}
+ : m_value(o.m_value)
+ , m_isNull(o.m_isNull)
+ {}
+
+ QQmlNullableValue(QQmlNullableValue<T> &&o) noexcept
+ : m_value(std::move(o.m_value))
+ , m_isNull(std::exchange(o.m_isNull, true))
+ {}
+
QQmlNullableValue(const T &t)
- : isNull(false), value(t) {}
- QQmlNullableValue<T> &operator=(const T &t)
- { isNull = false; value = t; return *this; }
+ : m_value(t)
+ , m_isNull(false)
+ {}
+
+ QQmlNullableValue(T &&t) noexcept
+ : m_value(std::move(t))
+ , m_isNull(false)
+ {}
+
QQmlNullableValue<T> &operator=(const QQmlNullableValue<T> &o)
- { isNull = o.isNull; value = o.value; return *this; }
- operator T() const { return value; }
+ {
+ if (&o != this) {
+ m_value = o.m_value;
+ m_isNull = o.m_isNull;
+ }
+ return *this;
+ }
+
+ QQmlNullableValue<T> &operator=(QQmlNullableValue<T> &&o) noexcept
+ {
+ if (&o != this) {
+ m_value = std::move(o.m_value);
+ m_isNull = std::exchange(o.m_isNull, true);
+ }
+ return *this;
+ }
+
+ QQmlNullableValue<T> &operator=(const T &t)
+ {
+ m_value = t;
+ m_isNull = false;
+ return *this;
+ }
+
+ QQmlNullableValue<T> &operator=(T &&t) noexcept
+ {
+ m_value = std::move(t);
+ m_isNull = false;
+ return *this;
+ }
+
+ const T &value() const { return m_value; }
+ operator T() const { return m_value; }
+
+ void invalidate() { m_isNull = true; }
+ bool isValid() const { return !m_isNull; }
- void invalidate() { isNull = true; }
- bool isValid() const { return !isNull; }
- bool isNull = true;
- T value;
+private:
+ T m_value = T();
+ bool m_isNull = true;
};
QT_END_NAMESPACE
diff --git a/src/qml/qml/ftw/qqmlrefcount_p.h b/src/qml/qml/ftw/qqmlrefcount_p.h
index 8b4b5b1535..e7616915eb 100644
--- a/src/qml/qml/ftw/qqmlrefcount_p.h
+++ b/src/qml/qml/ftw/qqmlrefcount_p.h
@@ -21,23 +21,34 @@
QT_BEGIN_NAMESPACE
+template <typename T>
+class QQmlRefCounted;
-class Q_QML_PRIVATE_EXPORT QQmlRefCount
+class QQmlRefCount
{
Q_DISABLE_COPY_MOVE(QQmlRefCount)
public:
inline QQmlRefCount();
inline void addref() const;
- inline void release() const;
inline int count() const;
-protected:
- inline virtual ~QQmlRefCount();
+private:
+ inline ~QQmlRefCount();
+ template <typename T> friend class QQmlRefCounted;
private:
mutable QAtomicInt refCount;
};
+template <typename T>
+class QQmlRefCounted : public QQmlRefCount
+{
+public:
+ inline void release() const;
+protected:
+ inline ~QQmlRefCounted();
+};
+
template<class T>
class QQmlRefPointer
{
@@ -46,16 +57,16 @@ public:
AddRef,
Adopt
};
- inline QQmlRefPointer();
- inline QQmlRefPointer(T *, Mode m = AddRef);
- inline QQmlRefPointer(const QQmlRefPointer<T> &);
- inline QQmlRefPointer(QQmlRefPointer<T> &&);
+ Q_NODISCARD_CTOR inline QQmlRefPointer() noexcept;
+ Q_NODISCARD_CTOR inline QQmlRefPointer(T *, Mode m = AddRef);
+ Q_NODISCARD_CTOR inline QQmlRefPointer(const QQmlRefPointer &);
+ Q_NODISCARD_CTOR inline QQmlRefPointer(QQmlRefPointer &&) noexcept;
inline ~QQmlRefPointer();
void swap(QQmlRefPointer &other) noexcept { qt_ptr_swap(o, other.o); }
inline QQmlRefPointer<T> &operator=(const QQmlRefPointer<T> &o);
- inline QQmlRefPointer<T> &operator=(QQmlRefPointer<T> &&o);
+ inline QQmlRefPointer<T> &operator=(QQmlRefPointer<T> &&o) noexcept;
inline bool isNull() const { return !o; }
@@ -68,8 +79,20 @@ public:
inline T* take() { T *res = o; o = nullptr; return res; }
- friend bool operator==(const QQmlRefPointer &a, const QQmlRefPointer &b) { return a.o == b.o; }
- friend bool operator!=(const QQmlRefPointer &a, const QQmlRefPointer &b) { return !(a == b); }
+ friend bool operator==(const QQmlRefPointer &a, const QQmlRefPointer &b) noexcept
+ {
+ return a.o == b.o;
+ }
+
+ friend bool operator!=(const QQmlRefPointer &a, const QQmlRefPointer &b) noexcept
+ {
+ return !(a == b);
+ }
+
+ friend size_t qHash(const QQmlRefPointer &v, size_t seed = 0) noexcept
+ {
+ return qHash(v.o, seed);
+ }
void reset(T *t = nullptr)
{
@@ -90,7 +113,7 @@ namespace QQml {
/*!
\internal
Creates a QQmlRefPointer which takes ownership of a newly constructed T.
- T must derive from QQmlRefCount (as we rely on an initial refcount of _1_).
+ T must derive from QQmlRefCounted<T> (as we rely on an initial refcount of _1_).
T will be constructed by forwarding \a args to its constructor.
*/
template <typename T, typename ...Args>
@@ -120,11 +143,22 @@ void QQmlRefCount::addref() const
refCount.ref();
}
-void QQmlRefCount::release() const
+template <typename T>
+void QQmlRefCounted<T>::release() const
{
+ static_assert(std::is_base_of_v<QQmlRefCounted, T>,
+ "QQmlRefCounted<T> must be a base of T (CRTP)");
Q_ASSERT(refCount.loadRelaxed() > 0);
if (!refCount.deref())
- delete this;
+ delete static_cast<const T *>(this);
+}
+
+template <typename T>
+QQmlRefCounted<T>::~QQmlRefCounted()
+{
+ static_assert(std::is_final_v<T> || std::has_virtual_destructor_v<T>,
+ "T must either be marked final or have a virtual dtor, "
+ "lest release() runs into UB.");
}
int QQmlRefCount::count() const
@@ -133,7 +167,7 @@ int QQmlRefCount::count() const
}
template<class T>
-QQmlRefPointer<T>::QQmlRefPointer()
+QQmlRefPointer<T>::QQmlRefPointer() noexcept
: o(nullptr)
{
}
@@ -154,7 +188,7 @@ QQmlRefPointer<T>::QQmlRefPointer(const QQmlRefPointer<T> &other)
}
template <class T>
-QQmlRefPointer<T>::QQmlRefPointer(QQmlRefPointer<T> &&other)
+QQmlRefPointer<T>::QQmlRefPointer(QQmlRefPointer<T> &&other) noexcept
: o(other.take())
{
}
@@ -179,7 +213,7 @@ QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(const QQmlRefPointer<T> &other)
}
template <class T>
-QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(QQmlRefPointer<T> &&other)
+QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(QQmlRefPointer<T> &&other) noexcept
{
QQmlRefPointer<T> m(std::move(other));
swap(m);
diff --git a/src/qml/qml/ftw/qqmlthread.cpp b/src/qml/qml/ftw/qqmlthread.cpp
index 8589402bff..57f91b6b4d 100644
--- a/src/qml/qml/ftw/qqmlthread.cpp
+++ b/src/qml/qml/ftw/qqmlthread.cpp
@@ -194,17 +194,6 @@ void QQmlThread::shutdown()
Q_ASSERT(!d->m_shutdown);
d->m_shutdown = true;
- for (;;) {
- if (d->mainSync || !d->mainList.isEmpty()) {
- d->unlock();
- d->mainEvent();
- d->lock();
- } else if (!d->threadList.isEmpty()) {
- d->wait();
- } else {
- break;
- }
- }
if (QCoreApplication::closingDown())
d->quit();
@@ -213,6 +202,10 @@ void QQmlThread::shutdown()
d->unlock();
d->QThread::wait();
+
+ // Discard all remaining messages.
+ // We don't need the lock anymore because the thread is dead.
+ discardMessages();
}
bool QQmlThread::isShutdown() const
@@ -399,5 +392,22 @@ void QQmlThread::waitForNextMessage()
d->m_mainThreadWaiting = false;
}
+/*!
+ \internal
+ \note This method must be called in the main thread
+ \warning This method requires that the lock is held!
+
+ Clear all pending events, for either thread.
+*/
+void QQmlThread::discardMessages()
+{
+ Q_ASSERT(!isThisThread());
+ if (Message *mainSync = std::exchange(d->mainSync, nullptr))
+ delete mainSync;
+ while (!d->mainList.isEmpty())
+ delete d->mainList.takeFirst();
+ while (!d->threadList.isEmpty())
+ delete d->threadList.takeFirst();
+}
QT_END_NAMESPACE
diff --git a/src/qml/qml/ftw/qqmlthread_p.h b/src/qml/qml/ftw/qqmlthread_p.h
index 4af94036b5..35f586f7e7 100644
--- a/src/qml/qml/ftw/qqmlthread_p.h
+++ b/src/qml/qml/ftw/qqmlthread_p.h
@@ -64,6 +64,7 @@ public:
void postMethodToMain(Method &&method, Args &&...args);
void waitForNextMessage();
+ void discardMessages();
private:
friend class QQmlThreadPrivate;
diff --git a/src/qml/qml/ftw/qrecyclepool_p.h b/src/qml/qml/ftw/qrecyclepool_p.h
index 6c2b22d93b..6661c88631 100644
--- a/src/qml/qml/ftw/qrecyclepool_p.h
+++ b/src/qml/qml/ftw/qrecyclepool_p.h
@@ -17,6 +17,8 @@
#include <QtCore/private/qglobal_p.h>
+#include <QtCore/q20memory.h>
+
QT_BEGIN_NAMESPACE
#define QRECYCLEPOOLCOOKIE 0x33218ADF
@@ -67,11 +69,8 @@ public:
inline QRecyclePool();
inline ~QRecyclePool();
- inline T *New();
- template<typename T1>
- inline T *New(const T1 &);
- template<typename T1>
- inline T *New(T1 &);
+ template<typename...Args>
+ [[nodiscard]] inline T *New(Args&&...args);
static inline void Delete(T *);
@@ -93,29 +92,10 @@ QRecyclePool<T, Step>::~QRecyclePool()
}
template<typename T, int Step>
-T *QRecyclePool<T, Step>::New()
-{
- T *rv = d->allocate();
- new (rv) T;
- return rv;
-}
-
-template<typename T, int Step>
-template<typename T1>
-T *QRecyclePool<T, Step>::New(const T1 &a)
+template<typename...Args>
+T *QRecyclePool<T, Step>::New(Args&&...args)
{
- T *rv = d->allocate();
- new (rv) T(a);
- return rv;
-}
-
-template<typename T, int Step>
-template<typename T1>
-T *QRecyclePool<T, Step>::New(T1 &a)
-{
- T *rv = d->allocate();
- new (rv) T(a);
- return rv;
+ return q20::construct_at(d->allocate(), std::forward<Args>(args)...);
}
template<typename T, int Step>
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index c752adc5a0..63e67ac804 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -6,22 +6,22 @@
#include <QtQml/qqmlprivate.h>
#include <private/qjsvalue_p.h>
+#include <private/qqmlbuiltinfunctions_p.h>
+#include <private/qqmlcomponent_p.h>
#include <private/qqmlengine_p.h>
+#include <private/qqmlfinalizer_p.h>
+#include <private/qqmlloggingcategorybase_p.h>
#include <private/qqmlmetatype_p.h>
#include <private/qqmlmetatypedata_p.h>
#include <private/qqmltype_p_p.h>
#include <private/qqmltypemodule_p.h>
-#include <private/qqmltypenotavailable_p.h>
-#include <private/qqmlcomponent_p.h>
#include <private/qqmltypewrapper_p.h>
#include <private/qqmlvaluetypewrapper_p.h>
+#include <private/qv4dateobject_p.h>
+#include <private/qv4errorobject_p.h>
+#include <private/qv4identifiertable_p.h>
#include <private/qv4lookup_p.h>
#include <private/qv4qobjectwrapper_p.h>
-#include <private/qv4identifiertable_p.h>
-#include <private/qv4errorobject_p.h>
-#include <private/qqmlbuiltinfunctions_p.h>
-#include <private/qqmlfinalizer_p.h>
-#include <private/qqmlloggingcategory_p.h>
#include <QtCore/qmutex.h>
@@ -50,17 +50,26 @@ void qmlExecuteDeferred(QObject *object)
{
QQmlData *data = QQmlData::get(object);
- if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object)) {
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine());
+ if (!data
+ || !data->context
+ || !data->context->engine()
+ || data->deferredData.isEmpty()
+ || data->wasDeleted(object)) {
+ return;
+ }
+
+ if (!data->propertyCache)
+ data->propertyCache = QQmlMetaType::propertyCache(object->metaObject());
- QQmlComponentPrivate::DeferredState state;
- QQmlComponentPrivate::beginDeferred(ep, object, &state);
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine());
- // Release the reference for the deferral action (we still have one from construction)
- data->releaseDeferredData();
+ QQmlComponentPrivate::DeferredState state;
+ QQmlComponentPrivate::beginDeferred(ep, object, &state);
- QQmlComponentPrivate::completeDeferred(ep, &state);
- }
+ // Release the reference for the deferral action (we still have one from construction)
+ data->releaseDeferredData();
+
+ QQmlComponentPrivate::completeDeferred(ep, &state);
}
QQmlContext *qmlContext(const QObject *obj)
@@ -146,16 +155,17 @@ void QQmlPrivate::qmlRegistrationWarning(
{
switch (warning) {
case UnconstructibleType:
- qWarning()
+ qWarning().nospace()
<< metaType.name()
- << "is neither a QObject, nor default- and copy-constructible, nor uncreatable."
+ << " is neither a default constructible QObject, nor a default- "
+ << "and copy-constructible Q_GADGET, nor marked as uncreatable.\n"
<< "You should not use it as a QML type.";
break;
case UnconstructibleSingleton:
qWarning()
<< "Singleton" << metaType.name()
- << "needs either a default constructor or, when adding a default"
- << "constructor is infeasible, a public static"
+ << "needs to be a concrete class with either a default constructor"
+ << "or, when adding a default constructor is infeasible, a public static"
<< "create(QQmlEngine *, QJSEngine *) method.";
break;
case NonQObjectWithAtached:
@@ -166,6 +176,22 @@ void QQmlPrivate::qmlRegistrationWarning(
}
}
+QMetaType QQmlPrivate::compositeMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName)
+{
+ return QQmlTypePrivate::compositeQmlType(
+ unit->baseCompilationUnit(), unit->engine->typeLoader(), elementName)
+ .typeId();
+}
+
+QMetaType QQmlPrivate::compositeListMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName)
+{
+ return QQmlTypePrivate::compositeQmlType(
+ unit->baseCompilationUnit(), unit->engine->typeLoader(), elementName)
+ .qListTypeId();
+}
+
int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
const char *uri, int versionMajor,
int versionMinor, const char *qmlName,
@@ -261,7 +287,7 @@ static QTypeRevision resolveModuleVersion(int moduleMajor)
/*!
* \relates QQmlEngine
- * Registers an implicit import for module \a uri of major version \a moduleMajor.
+ * Registers a qmldir-import for module \a uri of major version \a moduleMajor.
*
* This has the same effect as an \c import statement in a qmldir file: Whenever
* \a uri of version \a moduleMajor is imported, \a import of version
@@ -470,13 +496,280 @@ static void uniqueRevisions(QVector<QTypeRevision> *revisions, QTypeRevision def
revisions->erase(it, revisions->end());
}
+static QQmlType::SingletonInstanceInfo::ConstPtr singletonInstanceInfo(
+ const QQmlPrivate::RegisterSingletonType &type)
+{
+ QQmlType::SingletonInstanceInfo::Ptr siinfo = QQmlType::SingletonInstanceInfo::create();
+ siinfo->scriptCallback = type.scriptApi;
+ siinfo->qobjectCallback = type.qObjectApi;
+ siinfo->typeName = type.typeName;
+ return QQmlType::SingletonInstanceInfo::ConstPtr(
+ siinfo.take(), QQmlType::SingletonInstanceInfo::ConstPtr::Adopt);
+}
+
+static QQmlType::SingletonInstanceInfo::ConstPtr singletonInstanceInfo(
+ const QQmlPrivate::RegisterCompositeSingletonType &type)
+{
+ QQmlType::SingletonInstanceInfo::Ptr siinfo = QQmlType::SingletonInstanceInfo::create();
+ siinfo->url = QQmlTypeLoader::normalize(type.url);
+ siinfo->typeName = type.typeName;
+ return QQmlType::SingletonInstanceInfo::ConstPtr(
+ siinfo.take(), QQmlType::SingletonInstanceInfo::ConstPtr::Adopt);
+}
+
+static int finalizeType(const QQmlType &dtype)
+{
+ if (!dtype.isValid())
+ return -1;
+
+ QQmlMetaType::registerUndeletableType(dtype);
+ return dtype.index();
+}
+
+using ElementNames = QVarLengthArray<const char *, 8>;
+static ElementNames classElementNames(const QMetaObject *metaObject)
+{
+ Q_ASSERT(metaObject);
+ const char *key = "QML.Element";
+
+ const int offset = metaObject->classInfoOffset();
+ const int start = metaObject->classInfoCount() + offset - 1;
+
+ ElementNames elementNames;
+
+ for (int i = start; i >= offset; --i) {
+ const QMetaClassInfo classInfo = metaObject->classInfo(i);
+ if (qstrcmp(key, classInfo.name()) == 0) {
+ const char *elementName = classInfo.value();
+
+ if (qstrcmp(elementName, "auto") == 0) {
+ const char *strippedClassName = metaObject->className();
+ for (const char *c = strippedClassName; *c != '\0'; c++) {
+ if (*c == ':')
+ strippedClassName = c + 1;
+ }
+ elementName = strippedClassName;
+ } else if (qstrcmp(elementName, "anonymous") == 0) {
+ if (elementNames.isEmpty())
+ elementNames.push_back(nullptr);
+ else if (elementNames[0] != nullptr)
+ qWarning() << metaObject->className() << "is both anonymous and named";
+ continue;
+ }
+
+ if (!elementNames.isEmpty() && elementNames[0] == nullptr) {
+ qWarning() << metaObject->className() << "is both anonymous and named";
+ elementNames[0] = elementName;
+ } else {
+ elementNames.push_back(elementName);
+ }
+ }
+ }
+
+ return elementNames;
+}
+
+struct AliasRegistrar
+{
+ AliasRegistrar(const ElementNames *elementNames) : elementNames(elementNames) {}
+
+ void registerAliases(int typeId)
+ {
+ if (elementNames) {
+ for (int i = 1, end = elementNames->length(); i < end; ++i)
+ otherNames.append(QString::fromUtf8(elementNames->at(i)));
+ elementNames = nullptr;
+ }
+
+ for (const QString &otherName : std::as_const(otherNames))
+ QQmlMetaType::registerTypeAlias(typeId, otherName);
+ }
+
+private:
+ const ElementNames *elementNames;
+ QVarLengthArray<QString, 8> otherNames;
+};
+
+
+static void doRegisterTypeAndRevisions(
+ const QQmlPrivate::RegisterTypeAndRevisions &type,
+ const ElementNames &elementNames)
+{
+ using namespace QQmlPrivate;
+
+ const bool isValueType = !(type.typeId.flags() & QMetaType::PointerToQObject);
+ const bool creatable = (elementNames[0] != nullptr || isValueType)
+ && boolClassInfo(type.classInfoMetaObject, "QML.Creatable", true);
+
+ QString noCreateReason;
+ ValueTypeCreationMethod creationMethod = ValueTypeCreationMethod::None;
+
+ if (!creatable) {
+ noCreateReason = QString::fromUtf8(
+ classInfo(type.classInfoMetaObject, "QML.UncreatableReason"));
+ if (noCreateReason.isEmpty())
+ noCreateReason = QLatin1String("Type cannot be created in QML.");
+ } else if (isValueType) {
+ const char *method = classInfo(type.classInfoMetaObject, "QML.CreationMethod");
+ if (qstrcmp(method, "structured") == 0)
+ creationMethod = ValueTypeCreationMethod::Structured;
+ else if (qstrcmp(method, "construct") == 0)
+ creationMethod = ValueTypeCreationMethod::Construct;
+ }
+
+ RegisterType typeRevision = {
+ QQmlPrivate::RegisterType::CurrentVersion,
+ type.typeId,
+ type.listId,
+ creatable ? type.objectSize : 0,
+ nullptr,
+ nullptr,
+ noCreateReason,
+ type.createValueType,
+ type.uri,
+ type.version,
+ nullptr,
+ type.metaObject,
+ type.attachedPropertiesFunction,
+ type.attachedPropertiesMetaObject,
+ type.parserStatusCast,
+ type.valueSourceCast,
+ type.valueInterceptorCast,
+ type.extensionObjectCreate,
+ type.extensionMetaObject,
+ nullptr,
+ QTypeRevision(),
+ type.structVersion > 0 ? type.finalizerCast : -1,
+ creationMethod
+ };
+
+ QQmlPrivate::RegisterSequentialContainer sequenceRevision = {
+ 0,
+ type.uri,
+ type.version,
+ nullptr,
+ type.listId,
+ type.structVersion > 1 ? type.listMetaSequence : QMetaSequence(),
+ QTypeRevision(),
+ };
+
+ const QTypeRevision added = revisionClassInfo(
+ type.classInfoMetaObject, "QML.AddedInVersion",
+ QTypeRevision::fromVersion(type.version.majorVersion(), 0));
+ const QTypeRevision removed = revisionClassInfo(
+ type.classInfoMetaObject, "QML.RemovedInVersion");
+ const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
+ "QML.ExtraVersion");
+
+ auto revisions = prepareRevisions(type.metaObject, added) + furtherRevisions;
+ if (type.attachedPropertiesMetaObject)
+ revisions += availableRevisions(type.attachedPropertiesMetaObject);
+ uniqueRevisions(&revisions, type.version, added);
+
+ AliasRegistrar aliasRegistrar(&elementNames);
+ for (QTypeRevision revision : revisions) {
+ if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
+ break;
+
+ assignVersions(&typeRevision, revision, type.version);
+
+ // When removed or before added, we still add revisions, but anonymous ones
+ if (typeRevision.version < added
+ || (removed.isValid() && !(typeRevision.version < removed))) {
+ typeRevision.elementName = nullptr;
+ typeRevision.create = nullptr;
+ typeRevision.userdata = nullptr;
+ } else {
+ typeRevision.elementName = elementNames[0];
+ typeRevision.create = creatable ? type.create : nullptr;
+ typeRevision.userdata = type.userdata;
+ }
+
+ typeRevision.customParser = type.customParserFactory();
+ const int id = qmlregister(TypeRegistration, &typeRevision);
+ if (type.qmlTypeIds)
+ type.qmlTypeIds->append(id);
+
+ if (typeRevision.elementName)
+ aliasRegistrar.registerAliases(id);
+
+ if (sequenceRevision.metaSequence != QMetaSequence()) {
+ sequenceRevision.version = typeRevision.version;
+ sequenceRevision.revision = typeRevision.revision;
+ const int id = QQmlPrivate::qmlregister(
+ QQmlPrivate::SequentialContainerRegistration, &sequenceRevision);
+ if (type.qmlTypeIds)
+ type.qmlTypeIds->append(id);
+ }
+ }
+}
+
+static void doRegisterSingletonAndRevisions(
+ const QQmlPrivate::RegisterSingletonTypeAndRevisions &type,
+ const ElementNames &elementNames)
+{
+ using namespace QQmlPrivate;
+
+ RegisterSingletonType revisionRegistration = {
+ 0,
+ type.uri,
+ type.version,
+ elementNames[0],
+ nullptr,
+ type.qObjectApi,
+ type.instanceMetaObject,
+ type.typeId,
+ type.extensionObjectCreate,
+ type.extensionMetaObject,
+ QTypeRevision()
+ };
+ const QQmlType::SingletonInstanceInfo::ConstPtr siinfo
+ = singletonInstanceInfo(revisionRegistration);
+
+ const QTypeRevision added = revisionClassInfo(
+ type.classInfoMetaObject, "QML.AddedInVersion",
+ QTypeRevision::fromVersion(type.version.majorVersion(), 0));
+ const QTypeRevision removed = revisionClassInfo(
+ type.classInfoMetaObject, "QML.RemovedInVersion");
+ const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
+ "QML.ExtraVersion");
+
+ auto revisions = prepareRevisions(type.instanceMetaObject, added) + furtherRevisions;
+ uniqueRevisions(&revisions, type.version, added);
+
+ AliasRegistrar aliasRegistrar(&elementNames);
+ for (QTypeRevision revision : std::as_const(revisions)) {
+ if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
+ break;
+
+ assignVersions(&revisionRegistration, revision, type.version);
+
+ // When removed or before added, we still add revisions, but anonymous ones
+ if (revisionRegistration.version < added
+ || (removed.isValid() && !(revisionRegistration.version < removed))) {
+ revisionRegistration.typeName = nullptr;
+ revisionRegistration.qObjectApi = nullptr;
+ } else {
+ revisionRegistration.typeName = elementNames[0];
+ revisionRegistration.qObjectApi = type.qObjectApi;
+ }
+
+ const int id = finalizeType(
+ QQmlMetaType::registerSingletonType(revisionRegistration, siinfo));
+ if (type.qmlTypeIds)
+ type.qmlTypeIds->append(id);
+
+ if (revisionRegistration.typeName)
+ aliasRegistrar.registerAliases(id);
+ }
+}
+
/*
This method is "over generalized" to allow us to (potentially) register more types of things in
the future without adding exported symbols.
*/
int QQmlPrivate::qmlregister(RegistrationType type, void *data)
{
- QQmlType dtype;
switch (type) {
case AutoParentRegistration:
return QQmlMetaType::registerAutoParentFunction(
@@ -486,159 +779,29 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
*reinterpret_cast<RegisterQmlUnitCacheHook *>(data));
case TypeAndRevisionsRegistration: {
const RegisterTypeAndRevisions &type = *reinterpret_cast<RegisterTypeAndRevisions *>(data);
- const char *elementName = (type.structVersion > 1 && type.forceAnonymous)
- ? nullptr
- : classElementName(type.classInfoMetaObject);
- const bool creatable = (elementName != nullptr)
- && boolClassInfo(type.classInfoMetaObject, "QML.Creatable", true);
-
- QString noCreateReason;
- ValueTypeCreationMethod creationMethod = ValueTypeCreationMethod::None;
-
- if (!creatable) {
- noCreateReason = QString::fromUtf8(
- classInfo(type.classInfoMetaObject, "QML.UncreatableReason"));
- if (noCreateReason.isEmpty())
- noCreateReason = QLatin1String("Type cannot be created in QML.");
- } else if (!(type.typeId.flags() & QMetaType::PointerToQObject)) {
- const char *method = classInfo(type.classInfoMetaObject, "QML.CreationMethod");
- if (qstrcmp(method, "structured") == 0)
- creationMethod = ValueTypeCreationMethod::Structured;
- else if (qstrcmp(method, "construct") == 0)
- creationMethod = ValueTypeCreationMethod::Construct;
- }
-
- RegisterType typeRevision = {
- QQmlPrivate::RegisterType::CurrentVersion,
- type.typeId,
- type.listId,
- creatable ? type.objectSize : 0,
- nullptr,
- nullptr,
- noCreateReason,
- type.createValueType,
- type.uri,
- type.version,
- nullptr,
- type.metaObject,
- type.attachedPropertiesFunction,
- type.attachedPropertiesMetaObject,
- type.parserStatusCast,
- type.valueSourceCast,
- type.valueInterceptorCast,
- type.extensionObjectCreate,
- type.extensionMetaObject,
- nullptr,
- QTypeRevision(),
- type.structVersion > 0 ? type.finalizerCast : -1,
- creationMethod
- };
-
- QQmlPrivate::RegisterSequentialContainer sequenceRevision = {
- 0,
- type.uri,
- type.version,
- nullptr,
- type.listId,
- type.structVersion > 1 ? type.listMetaSequence : QMetaSequence(),
- QTypeRevision(),
- };
-
- const QTypeRevision added = revisionClassInfo(
- type.classInfoMetaObject, "QML.AddedInVersion",
- QTypeRevision::fromVersion(type.version.majorVersion(), 0));
- const QTypeRevision removed = revisionClassInfo(
- type.classInfoMetaObject, "QML.RemovedInVersion");
- const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
- "QML.ExtraVersion");
-
- auto revisions = prepareRevisions(type.metaObject, added) + furtherRevisions;
- if (type.attachedPropertiesMetaObject)
- revisions += availableRevisions(type.attachedPropertiesMetaObject);
- uniqueRevisions(&revisions, type.version, added);
-
- for (QTypeRevision revision : revisions) {
- if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
- break;
-
- assignVersions(&typeRevision, revision, type.version);
-
- // When removed or before added, we still add revisions, but anonymous ones
- if (typeRevision.version < added
- || (removed.isValid() && !(typeRevision.version < removed))) {
- typeRevision.elementName = nullptr;
- typeRevision.create = nullptr;
- typeRevision.userdata = nullptr;
+ if (type.structVersion > 1 && type.forceAnonymous) {
+ doRegisterTypeAndRevisions(type, {nullptr});
+ } else {
+ const ElementNames names = classElementNames(type.classInfoMetaObject);
+ if (names.isEmpty()) {
+ qWarning().nospace() << "Missing QML.Element class info for "
+ << type.classInfoMetaObject->className();
} else {
- typeRevision.elementName = elementName;
- typeRevision.create = creatable ? type.create : nullptr;
- typeRevision.userdata = type.userdata;
+ doRegisterTypeAndRevisions(type, names);
}
- typeRevision.customParser = type.customParserFactory();
- const int id = qmlregister(TypeRegistration, &typeRevision);
- if (type.qmlTypeIds)
- type.qmlTypeIds->append(id);
-
- if (sequenceRevision.metaSequence != QMetaSequence()) {
- sequenceRevision.version = typeRevision.version;
- sequenceRevision.revision = typeRevision.revision;
- const int id = QQmlPrivate::qmlregister(
- QQmlPrivate::SequentialContainerRegistration, &sequenceRevision);
- if (type.qmlTypeIds)
- type.qmlTypeIds->append(id);
- }
}
break;
}
case SingletonAndRevisionsRegistration: {
const RegisterSingletonTypeAndRevisions &type
= *reinterpret_cast<RegisterSingletonTypeAndRevisions *>(data);
- const char *elementName = classElementName(type.classInfoMetaObject);
- RegisterSingletonType revisionRegistration = {
- 0,
- type.uri,
- type.version,
- elementName,
- nullptr,
- type.qObjectApi,
- type.instanceMetaObject,
- type.typeId,
- type.extensionObjectCreate,
- type.extensionMetaObject,
- QTypeRevision()
- };
-
- const QTypeRevision added = revisionClassInfo(
- type.classInfoMetaObject, "QML.AddedInVersion",
- QTypeRevision::fromMinorVersion(0));
- const QTypeRevision removed = revisionClassInfo(
- type.classInfoMetaObject, "QML.RemovedInVersion");
- const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
- "QML.ExtraVersion");
-
- auto revisions = prepareRevisions(type.instanceMetaObject, added) + furtherRevisions;
- uniqueRevisions(&revisions, type.version, added);
-
- for (QTypeRevision revision : std::as_const(revisions)) {
- if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
- break;
-
- assignVersions(&revisionRegistration, revision, type.version);
-
- // When removed or before added, we still add revisions, but anonymous ones
- if (revisionRegistration.version < added
- || (removed.isValid() && !(revisionRegistration.version < removed))) {
- revisionRegistration.typeName = nullptr;
- revisionRegistration.qObjectApi = nullptr;
- } else {
- revisionRegistration.typeName = elementName;
- revisionRegistration.qObjectApi = type.qObjectApi;
- }
-
- const int id = qmlregister(SingletonRegistration, &revisionRegistration);
- if (type.qmlTypeIds)
- type.qmlTypeIds->append(id);
+ const ElementNames names = classElementNames(type.classInfoMetaObject);
+ if (names.isEmpty()) {
+ qWarning().nospace() << "Missing QML.Element class info for "
+ << type.classInfoMetaObject->className();
+ } else {
+ doRegisterSingletonAndRevisions(type, names);
}
break;
}
@@ -677,32 +840,30 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
break;
}
case TypeRegistration:
- dtype = QQmlMetaType::registerType(*reinterpret_cast<RegisterType *>(data));
- break;
+ return finalizeType(
+ QQmlMetaType::registerType(*reinterpret_cast<RegisterType *>(data)));
case InterfaceRegistration:
- dtype = QQmlMetaType::registerInterface(*reinterpret_cast<RegisterInterface *>(data));
- break;
+ return finalizeType(
+ QQmlMetaType::registerInterface(*reinterpret_cast<RegisterInterface *>(data)));
case SingletonRegistration:
- dtype = QQmlMetaType::registerSingletonType(*reinterpret_cast<RegisterSingletonType *>(data));
- break;
+ return finalizeType(QQmlMetaType::registerSingletonType(
+ *reinterpret_cast<RegisterSingletonType *>(data),
+ singletonInstanceInfo(*reinterpret_cast<RegisterSingletonType *>(data))));
case CompositeRegistration:
- dtype = QQmlMetaType::registerCompositeType(*reinterpret_cast<RegisterCompositeType *>(data));
- break;
+ return finalizeType(QQmlMetaType::registerCompositeType(
+ *reinterpret_cast<RegisterCompositeType *>(data)));
case CompositeSingletonRegistration:
- dtype = QQmlMetaType::registerCompositeSingletonType(*reinterpret_cast<RegisterCompositeSingletonType *>(data));
- break;
+ return finalizeType(QQmlMetaType::registerCompositeSingletonType(
+ *reinterpret_cast<RegisterCompositeSingletonType *>(data),
+ singletonInstanceInfo(*reinterpret_cast<RegisterCompositeSingletonType *>(data))));
case SequentialContainerRegistration:
- dtype = QQmlMetaType::registerSequentialContainer(*reinterpret_cast<RegisterSequentialContainer *>(data));
- break;
+ return finalizeType(QQmlMetaType::registerSequentialContainer(
+ *reinterpret_cast<RegisterSequentialContainer *>(data)));
default:
return -1;
}
- if (!dtype.isValid())
- return -1;
-
- QQmlMetaType::registerUndeletableType(dtype);
- return dtype.index();
+ return -1;
}
void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
@@ -749,6 +910,13 @@ QList<QTypeRevision> QQmlPrivate::revisionClassInfos(const QMetaObject *metaObje
return revisions;
}
+int qmlRegisterTypeNotAvailable(
+ const char *uri, int versionMajor, int versionMinor,
+ const char *qmlName, const QString &message)
+{
+ return qmlRegisterUncreatableType<QQmlTypeNotAvailable>(
+ uri, versionMajor, versionMinor, qmlName, message);
+}
namespace QQmlPrivate {
template<>
@@ -792,10 +960,21 @@ void qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>(
qmlregister(TypeAndRevisionsRegistration, &type);
}
+QObject *AOTCompiledContext::thisObject() const
+{
+ return static_cast<QV4::MetaTypesStackFrame *>(engine->handle()->currentStackFrame)
+ ->thisObject();
+}
QQmlEngine *AOTCompiledContext::qmlEngine() const
{
- return qmlContext ? qmlContext->engine() : nullptr;
+ return engine->handle()->qmlEngine();
+}
+
+static QQmlPropertyCapture *propertyCapture(const AOTCompiledContext *aotContext)
+{
+ QQmlEngine *engine = aotContext->qmlEngine();
+ return engine ? QQmlEnginePrivate::get(aotContext->qmlEngine())->propertyCapture : nullptr;
}
QJSValue AOTCompiledContext::jsMetaType(int index) const
@@ -818,37 +997,25 @@ void AOTCompiledContext::setReturnValueUndefined() const
}
}
-static QQmlPropertyCapture *propertyCapture(const QQmlContextData *qmlContext)
-{
- if (!qmlContext)
- return nullptr;
-
- QQmlEngine *engine = qmlContext->engine();
- Q_ASSERT(engine);
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
- Q_ASSERT(ep);
- return ep->propertyCapture;
-}
-
static void captureFallbackProperty(
QObject *object, int coreIndex, int notifyIndex, bool isConstant,
- const QQmlContextData *qmlContext)
+ const AOTCompiledContext *aotContext)
{
if (isConstant)
return;
- if (QQmlPropertyCapture *capture = propertyCapture(qmlContext))
+ if (QQmlPropertyCapture *capture = propertyCapture(aotContext))
capture->captureProperty(object, coreIndex, notifyIndex);
}
static void captureObjectProperty(
QObject *object, const QQmlPropertyCache *propertyCache,
- const QQmlPropertyData *property, QQmlContextData *qmlContext)
+ const QQmlPropertyData *property, const AOTCompiledContext *aotContext)
{
if (property->isConstant())
return;
- if (QQmlPropertyCapture *capture = propertyCapture(qmlContext))
+ if (QQmlPropertyCapture *capture = propertyCapture(aotContext))
capture->captureProperty(object, propertyCache, property);
}
@@ -863,83 +1030,202 @@ static bool inherits(const QQmlPropertyCache *descendent, const QQmlPropertyCach
enum class ObjectPropertyResult { OK, NeedsInit, Deleted };
-static ObjectPropertyResult loadObjectProperty(
- QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
+struct ObjectPropertyQmlData
+{
+ QQmlData *qmlData;
+ ObjectPropertyResult result;
+};
+
+template<bool StrictType>
+ObjectPropertyQmlData findObjectPropertyQmlData(QV4::Lookup *l, QObject *object)
{
QQmlData *qmlData = QQmlData::get(object);
if (!qmlData)
- return ObjectPropertyResult::NeedsInit;
+ return {qmlData, ObjectPropertyResult::NeedsInit};
if (qmlData->isQueuedForDeletion)
- return ObjectPropertyResult::Deleted;
+ return {qmlData, ObjectPropertyResult::Deleted};
Q_ASSERT(!QQmlData::wasDeleted(object));
const QQmlPropertyCache *propertyCache = l->qobjectLookup.propertyCache;
- if (!inherits(qmlData->propertyCache.data(), propertyCache))
- return ObjectPropertyResult::NeedsInit;
- const QQmlPropertyData *property = l->qobjectLookup.propertyData;
+ if (StrictType) {
+ if (qmlData->propertyCache.data() != propertyCache)
+ return {qmlData, ObjectPropertyResult::NeedsInit};
+ } else if (!inherits(qmlData->propertyCache.data(), propertyCache)) {
+ return {qmlData, ObjectPropertyResult::NeedsInit};
+ }
+ return {qmlData, ObjectPropertyResult::OK};
+}
- const int coreIndex = property->coreIndex();
- if (qmlData->hasPendingBindingBit(coreIndex))
- qmlData->flushPendingBinding(coreIndex);
+template<bool StrictType = false>
+ObjectPropertyResult loadObjectProperty(
+ QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
+{
+ const ObjectPropertyQmlData data = findObjectPropertyQmlData<StrictType>(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
+
+ const QQmlPropertyData *propertyData = l->qobjectLookup.propertyData;
+ const int coreIndex = propertyData->coreIndex();
+ if (data.qmlData->hasPendingBindingBit(coreIndex))
+ data.qmlData->flushPendingBinding(coreIndex);
- captureObjectProperty(object, propertyCache, property, qmlContext);
- property->readProperty(object, target);
+ captureObjectProperty(object, l->qobjectLookup.propertyCache, propertyData, aotContext);
+ propertyData->readProperty(object, target);
return ObjectPropertyResult::OK;
}
-static ObjectPropertyResult loadFallbackProperty(
- QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
+template<bool StrictType = false>
+ObjectPropertyResult writeBackObjectProperty(QV4::Lookup *l, QObject *object, void *source)
+{
+ const ObjectPropertyQmlData data = findObjectPropertyQmlData<StrictType>(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
+
+ l->qobjectLookup.propertyData->writeProperty(object, source, {});
+ return ObjectPropertyResult::OK;
+}
+
+struct FallbackPropertyQmlData
+{
+ QQmlData *qmlData;
+ const QMetaObject *metaObject;
+ ObjectPropertyResult result;
+};
+
+static FallbackPropertyQmlData findFallbackPropertyQmlData(QV4::Lookup *l, QObject *object)
{
QQmlData *qmlData = QQmlData::get(object);
if (qmlData && qmlData->isQueuedForDeletion)
- return ObjectPropertyResult::Deleted;
+ return {qmlData, nullptr, ObjectPropertyResult::Deleted};
Q_ASSERT(!QQmlData::wasDeleted(object));
const QMetaObject *metaObject
= reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
if (!metaObject || metaObject != object->metaObject())
- return ObjectPropertyResult::NeedsInit;
+ return {qmlData, nullptr, ObjectPropertyResult::NeedsInit};
+
+ return {qmlData, metaObject, ObjectPropertyResult::OK};
+}
+
+static ObjectPropertyResult loadFallbackProperty(
+ QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
+{
+ const FallbackPropertyQmlData data = findFallbackPropertyQmlData(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
const int coreIndex = l->qobjectFallbackLookup.coreIndex;
- if (qmlData && qmlData->hasPendingBindingBit(coreIndex))
- qmlData->flushPendingBinding(coreIndex);
+ if (data.qmlData && data.qmlData->hasPendingBindingBit(coreIndex))
+ data.qmlData->flushPendingBinding(coreIndex);
captureFallbackProperty(object, coreIndex, l->qobjectFallbackLookup.notifyIndex,
- l->qobjectFallbackLookup.isConstant, qmlContext);
+ l->qobjectFallbackLookup.isConstant, aotContext);
void *a[] = { target, nullptr };
- metaObject->metacall(object, QMetaObject::ReadProperty, coreIndex, a);
+ data.metaObject->metacall(object, QMetaObject::ReadProperty, coreIndex, a);
return ObjectPropertyResult::OK;
}
-template<typename Op>
+static ObjectPropertyResult writeBackFallbackProperty(QV4::Lookup *l, QObject *object, void *source)
+{
+ const FallbackPropertyQmlData data = findFallbackPropertyQmlData(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
+
+ void *a[] = { source, nullptr };
+ data.metaObject->metacall(
+ object, QMetaObject::WriteProperty, l->qobjectFallbackLookup.coreIndex, a);
+
+ return ObjectPropertyResult::OK;
+}
+
+ObjectPropertyResult loadObjectAsVariant(
+ QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
+{
+ QVariant *variant = static_cast<QVariant *>(target);
+ const QMetaType propType = l->qobjectLookup.propertyData->propType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return loadObjectProperty<true>(l, object, variant, aotContext);
+
+ *variant = QVariant(propType);
+ return loadObjectProperty<true>(l, object, variant->data(), aotContext);
+}
+
+ObjectPropertyResult writeBackObjectAsVariant(QV4::Lookup *l, QObject *object, void *source)
+{
+ QVariant *variant = static_cast<QVariant *>(source);
+ const QMetaType propType = l->qobjectLookup.propertyData->propType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return writeBackObjectProperty<true>(l, object, variant);
+
+ Q_ASSERT(variant->metaType() == propType);
+ return writeBackObjectProperty<true>(l, object, variant->data());
+}
+
+ObjectPropertyResult loadFallbackAsVariant(
+ QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
+{
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ Q_ASSERT(metaObject);
+
+ QVariant *variant = static_cast<QVariant *>(target);
+ const QMetaType propType = metaObject->property(l->qobjectFallbackLookup.coreIndex).metaType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return loadFallbackProperty(l, object, variant, aotContext);
+
+ *variant = QVariant(propType);
+ return loadFallbackProperty(l, object, variant->data(), aotContext);
+}
+
+ObjectPropertyResult writeBackFallbackAsVariant(QV4::Lookup *l, QObject *object, void *source)
+{
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ Q_ASSERT(metaObject);
+
+ QVariant *variant = static_cast<QVariant *>(source);
+ const QMetaType propType = metaObject->property(l->qobjectFallbackLookup.coreIndex).metaType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return writeBackFallbackProperty(l, object, variant);
+
+ Q_ASSERT(variant->metaType() == propType);
+ return writeBackFallbackProperty(l, object, variant->data());
+}
+
+template<bool StrictType, typename Op>
static ObjectPropertyResult changeObjectProperty(QV4::Lookup *l, QObject *object, Op op)
{
- const QQmlData *qmlData = QQmlData::get(object);
- if (!qmlData)
- return ObjectPropertyResult::NeedsInit;
- if (qmlData->isQueuedForDeletion)
- return ObjectPropertyResult::Deleted;
- Q_ASSERT(!QQmlData::wasDeleted(object));
- if (!inherits(qmlData->propertyCache.data(), l->qobjectLookup.propertyCache))
- return ObjectPropertyResult::NeedsInit;
+ const ObjectPropertyQmlData data = findObjectPropertyQmlData<StrictType>(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
+
const QQmlPropertyData *property = l->qobjectLookup.propertyData;
QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex()));
op(property);
return ObjectPropertyResult::OK;
}
-static ObjectPropertyResult resetObjectProperty(QV4::Lookup *l, QObject *object)
+template<bool StrictType = false>
+static ObjectPropertyResult resetObjectProperty(
+ QV4::Lookup *l, QObject *object, QV4::ExecutionEngine *v4)
{
- return changeObjectProperty(l, object, [&](const QQmlPropertyData *property) {
- property->resetProperty(object, {});
+ return changeObjectProperty<StrictType>(l, object, [&](const QQmlPropertyData *property) {
+ if (property->isResettable()) {
+ property->resetProperty(object, {});
+ } else {
+ v4->throwError(
+ QLatin1String("Cannot assign [undefined] to ") +
+ QLatin1String(property->propType().name()));
+ }
});
}
+template<bool StrictType = false>
static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object, void *value)
{
- return changeObjectProperty(l, object, [&](const QQmlPropertyData *property) {
+ return changeObjectProperty<StrictType>(l, object, [&](const QQmlPropertyData *property) {
property->writeProperty(object, value, {});
});
}
@@ -947,20 +1233,14 @@ static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object,
template<typename Op>
static ObjectPropertyResult changeFallbackProperty(QV4::Lookup *l, QObject *object, Op op)
{
- const QQmlData *qmlData = QQmlData::get(object);
- if (qmlData && qmlData->isQueuedForDeletion)
- return ObjectPropertyResult::Deleted;
- Q_ASSERT(!QQmlData::wasDeleted(object));
-
- const QMetaObject *metaObject
- = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
- if (!metaObject || metaObject != object->metaObject())
- return ObjectPropertyResult::NeedsInit;
+ const FallbackPropertyQmlData data = findFallbackPropertyQmlData(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
const int coreIndex = l->qobjectFallbackLookup.coreIndex;
QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(coreIndex));
- op(metaObject, coreIndex);
+ op(data.metaObject, coreIndex);
return ObjectPropertyResult::OK;
}
@@ -972,11 +1252,18 @@ static ObjectPropertyResult storeFallbackProperty(QV4::Lookup *l, QObject *objec
});
}
-static ObjectPropertyResult resetFallbackProperty(QV4::Lookup *l, QObject *object)
+static ObjectPropertyResult resetFallbackProperty(
+ QV4::Lookup *l, QObject *object, const QMetaProperty *property, QV4::ExecutionEngine *v4)
{
return changeFallbackProperty(l, object, [&](const QMetaObject *metaObject, int coreIndex) {
- void *args[] = { nullptr };
- metaObject->metacall(object, QMetaObject::ResetProperty, coreIndex, args);
+ if (property->isResettable()) {
+ void *args[] = { nullptr };
+ metaObject->metacall(object, QMetaObject::ResetProperty, coreIndex, args);
+ } else {
+ v4->throwError(
+ QLatin1String("Cannot assign [undefined] to ") +
+ QLatin1String(property->typeName()));
+ }
});
}
@@ -1003,19 +1290,97 @@ static bool isTypeCompatible(QMetaType lookupType, QMetaType propertyType)
if (!foundMetaObject)
return false;
- } else if (lookupType == QMetaType::fromType<int>()
- && propertyType.flags() & QMetaType::IsEnumeration) {
- return true;
+ } else if (propertyType.flags() & QMetaType::IsEnumeration) {
+ if (propertyType == lookupType)
+ return true;
+
+ // You can pass the underlying type of an enum.
+ // We don't want to check for the actual underlying type because
+ // moc and qmltyperegistrar are not very precise about it. Especially
+ // the long and longlong types can be ambiguous.
+
+ const bool isUnsigned = propertyType.flags() & QMetaType::IsUnsignedEnumeration;
+ switch (propertyType.sizeOf()) {
+ case 1:
+ return isUnsigned
+ ? lookupType == QMetaType::fromType<quint8>()
+ : lookupType == QMetaType::fromType<qint8>();
+ case 2:
+ return isUnsigned
+ ? lookupType == QMetaType::fromType<ushort>()
+ : lookupType == QMetaType::fromType<short>();
+ case 4:
+ // The default type, if moc doesn't know the actual enum type, is int.
+ // However, the compiler can still decide to encode the enum in uint.
+ // Therefore, we also accept int for uint enums.
+ // TODO: This is technically UB.
+ return isUnsigned
+ ? (lookupType == QMetaType::fromType<int>()
+ || lookupType == QMetaType::fromType<uint>())
+ : lookupType == QMetaType::fromType<int>();
+ case 8:
+ return isUnsigned
+ ? lookupType == QMetaType::fromType<qulonglong>()
+ : lookupType == QMetaType::fromType<qlonglong>();
+ }
+
+ return false;
} else if (propertyType != lookupType) {
return false;
}
return true;
}
+static ObjectPropertyResult storeObjectAsVariant(
+ QV4::ExecutionEngine *v4, QV4::Lookup *l, QObject *object, void *value)
+{
+ QVariant *variant = static_cast<QVariant *>(value);
+ const QMetaType propType = l->qobjectLookup.propertyData->propType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return storeObjectProperty<true>(l, object, variant);
+
+ if (!variant->isValid())
+ return resetObjectProperty<true>(l, object, v4);
+
+ if (isTypeCompatible(variant->metaType(), propType))
+ return storeObjectProperty<true>(l, object, variant->data());
+
+ QVariant converted(propType);
+ v4->metaTypeFromJS(v4->fromVariant(*variant), propType, converted.data());
+ return storeObjectProperty<true>(l, object, converted.data());
+}
+
+static ObjectPropertyResult storeFallbackAsVariant(
+ QV4::ExecutionEngine *v4, QV4::Lookup *l, QObject *object, void *value)
+{
+ QVariant *variant = static_cast<QVariant *>(value);
+
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ Q_ASSERT(metaObject);
+
+ const QMetaProperty property = metaObject->property(l->qobjectFallbackLookup.coreIndex);
+ const QMetaType propType = property.metaType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return storeFallbackProperty(l, object, variant);
+
+ if (!variant->isValid())
+ return resetFallbackProperty(l, object, &property, v4);
+
+ if (isTypeCompatible(variant->metaType(), propType))
+ return storeFallbackProperty(l, object, variant->data());
+
+ QVariant converted(propType);
+ v4->metaTypeFromJS(v4->fromVariant(*variant), propType, converted.data());
+ return storeFallbackProperty(l, object, converted.data());
+}
+
enum class ObjectLookupResult {
Failure,
Object,
- Fallback
+ Fallback,
+ ObjectAsVariant,
+ FallbackAsVariant,
};
static ObjectLookupResult initObjectLookup(
@@ -1045,6 +1410,7 @@ static ObjectLookupResult initObjectLookup(
name.getPointer(), object, aotContext->qmlContext);
}
+ const bool doVariantLookup = type == QMetaType::fromType<QVariant>();
if (!property) {
const QMetaObject *metaObject = object->metaObject();
if (!metaObject)
@@ -1056,7 +1422,7 @@ static ObjectLookupResult initObjectLookup(
return ObjectLookupResult::Failure;
const QMetaProperty property = metaObject->property(coreIndex);
- if (!isTypeCompatible(type, property.metaType()))
+ if (!doVariantLookup && !isTypeCompatible(type, property.metaType()))
return ObjectLookupResult::Failure;
l->releasePropertyCache();
@@ -1066,16 +1432,21 @@ static ObjectLookupResult initObjectLookup(
l->qobjectFallbackLookup.notifyIndex =
QMetaObjectPrivate::signalIndex(property.notifySignal());
l->qobjectFallbackLookup.isConstant = property.isConstant() ? 1 : 0;
- return ObjectLookupResult::Fallback;
+ return doVariantLookup
+ ? ObjectLookupResult::FallbackAsVariant
+ : ObjectLookupResult::Fallback;
}
- if (!isTypeCompatible(type, property->propType()))
+ if (!doVariantLookup && !isTypeCompatible(type, property->propType()))
return ObjectLookupResult::Failure;
Q_ASSERT(ddata->propertyCache);
QV4::setupQObjectLookup(l, ddata, property);
- return ObjectLookupResult::Object;
+
+ return doVariantLookup
+ ? ObjectLookupResult::ObjectAsVariant
+ : ObjectLookupResult::Object;
}
static bool initValueLookup(QV4::Lookup *l, QV4::ExecutableCompilationUnit *compilationUnit,
@@ -1095,13 +1466,25 @@ static bool initValueLookup(QV4::Lookup *l, QV4::ExecutableCompilationUnit *comp
static void amendException(QV4::ExecutionEngine *engine)
{
+ const int missingLineNumber = engine->currentStackFrame->missingLineNumber();
const int lineNumber = engine->currentStackFrame->lineNumber();
- engine->exceptionStackTrace.front().line = lineNumber;
+ Q_ASSERT(missingLineNumber != lineNumber);
+
+ auto amendStackTrace = [&](QV4::StackTrace *stackTrace) {
+ for (auto it = stackTrace->begin(), end = stackTrace->end(); it != end; ++it) {
+ if (it->line == missingLineNumber) {
+ it->line = lineNumber;
+ break;
+ }
+ }
+ };
+
+ amendStackTrace(&engine->exceptionStackTrace);
QV4::Scope scope(engine);
QV4::Scoped<QV4::ErrorObject> error(scope, *engine->exceptionValue);
if (error) // else some other value was thrown
- error->d()->stackTrace->front().line = lineNumber;
+ amendStackTrace(error->d()->stackTrace);
}
@@ -1112,19 +1495,21 @@ bool AOTCompiledContext::captureLookup(uint index, QObject *object) const
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
if (l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
- || l->getter == QV4::Lookup::getterQObject) {
+ || l->getter == QV4::Lookup::getterQObject
+ || l->getter == QV4::Lookup::getterQObjectAsVariant) {
const QQmlPropertyData *property = l->qobjectLookup.propertyData;
QQmlData::flushPendingBinding(object, property->coreIndex());
- captureObjectProperty(object, l->qobjectLookup.propertyCache, property, qmlContext);
+ captureObjectProperty(object, l->qobjectLookup.propertyCache, property, this);
return true;
}
- if (l->getter == QV4::Lookup::getterFallback) {
+ if (l->getter == QV4::Lookup::getterFallback
+ || l->getter == QV4::Lookup::getterFallbackAsVariant) {
const int coreIndex = l->qobjectFallbackLookup.coreIndex;
QQmlData::flushPendingBinding(object, coreIndex);
captureFallbackProperty(
object, coreIndex, l->qobjectFallbackLookup.notifyIndex,
- l->qobjectFallbackLookup.isConstant, qmlContext);
+ l->qobjectFallbackLookup.isConstant, this);
return true;
}
@@ -1138,7 +1523,7 @@ bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const
&& l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty) {
const QQmlPropertyData *property = l->qobjectLookup.propertyData;
QQmlData::flushPendingBinding(qmlScopeObject, property->coreIndex());
- captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache, property, qmlContext);
+ captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache, property, this);
return true;
}
@@ -1146,7 +1531,7 @@ bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const
const int coreIndex = l->qobjectFallbackLookup.coreIndex;
QQmlData::flushPendingBinding(qmlScopeObject, coreIndex);
captureFallbackProperty(qmlScopeObject, coreIndex, l->qobjectFallbackLookup.notifyIndex,
- l->qobjectFallbackLookup.isConstant, qmlContext);
+ l->qobjectFallbackLookup.isConstant, this);
return true;
}
@@ -1155,7 +1540,7 @@ bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const
void AOTCompiledContext::captureTranslation() const
{
- if (QQmlPropertyCapture *capture = propertyCapture(qmlContext))
+ if (QQmlPropertyCapture *capture = propertyCapture(this))
capture->captureTranslation();
}
@@ -1175,7 +1560,9 @@ QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
|| l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty
|| l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
|| l->getter == QV4::Lookup::getterQObject
- || l->setter == QV4::Lookup::setterQObject) {
+ || l->setter == QV4::Lookup::setterQObject
+ || l->getter == QV4::Lookup::getterQObjectAsVariant
+ || l->setter == QV4::Lookup::setterQObjectAsVariant) {
return l->qobjectLookup.propertyData->propType();
} else if (l->getter == QV4::QQmlValueTypeWrapper::lookupGetter) {
return QMetaType(l->qgadgetLookup.metaType);
@@ -1188,6 +1575,8 @@ QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
return QMetaType::fromType<QObject *>();
} else if (l->getter == QV4::Lookup::getterFallback
|| l->setter == QV4::Lookup::setterFallback
+ || l->getter == QV4::Lookup::getterFallbackAsVariant
+ || l->setter == QV4::Lookup::setterFallbackAsVariant
|| l->qmlContextPropertyGetter
== QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
const QMetaObject *metaObject
@@ -1218,38 +1607,42 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType
// the property cache we store a value into the property.
QV4::Lookup l;
- l.clear();
+ memset(&l, 0, sizeof(QV4::Lookup));
l.nameIndex = nameIndex;
l.forCall = false;
ObjectPropertyResult storeResult = ObjectPropertyResult::NeedsInit;
switch (initObjectLookup(this, &l, qmlScopeObject, QMetaType())) {
+ case ObjectLookupResult::ObjectAsVariant:
case ObjectLookupResult::Object: {
const QMetaType propType = l.qobjectLookup.propertyData->propType();
- if (type == propType) {
+ if (isTypeCompatible(type, propType)) {
storeResult = storeObjectProperty(&l, qmlScopeObject, value);
} else if (isUndefined(value, type)) {
- storeResult = resetObjectProperty(&l, qmlScopeObject);
+ storeResult = resetObjectProperty(&l, qmlScopeObject, engine->handle());
} else {
QVariant var(propType);
- propType.convert(type, value, propType, var.data());
+ QV4::ExecutionEngine *v4 = engine->handle();
+ v4->metaTypeFromJS(v4->metaTypeToJS(type, value), propType, var.data());
storeResult = storeObjectProperty(&l, qmlScopeObject, var.data());
}
l.qobjectLookup.propertyCache->release();
break;
}
+ case ObjectLookupResult::FallbackAsVariant:
case ObjectLookupResult::Fallback: {
const QMetaObject *metaObject
= reinterpret_cast<const QMetaObject *>(l.qobjectFallbackLookup.metaObject - 1);
- const QMetaType propType
- = metaObject->property(l.qobjectFallbackLookup.coreIndex).metaType();
- if (type == propType) {
+ const QMetaProperty property = metaObject->property(l.qobjectFallbackLookup.coreIndex);
+ const QMetaType propType = property.metaType();
+ if (isTypeCompatible(type, propType)) {
storeResult = storeFallbackProperty(&l, qmlScopeObject, value);
} else if (isUndefined(value, type)) {
- storeResult = resetFallbackProperty(&l, qmlScopeObject);
+ storeResult = resetFallbackProperty(&l, qmlScopeObject, &property, engine->handle());
} else {
QVariant var(propType);
- propType.convert(type, value, propType, var.data());
+ QV4::ExecutionEngine *v4 = engine->handle();
+ v4->metaTypeFromJS(v4->metaTypeToJS(type, value), propType, var.data());
storeResult = storeFallbackProperty(&l, qmlScopeObject, var.data());
}
break;
@@ -1284,10 +1677,10 @@ const QLoggingCategory *AOTCompiledContext::resolveLoggingCategory(QObject *wrap
{
if (wrapper) {
// We have to check this here because you may pass a plain QObject that only
- // turns out to be a QQmlLoggingCategory at run time.
- if (QQmlLoggingCategory *qQmlLoggingCategory
- = qobject_cast<QQmlLoggingCategory *>(wrapper)) {
- QLoggingCategory *loggingCategory = qQmlLoggingCategory->category();
+ // turns out to be a QQmlLoggingCategoryBase at run time.
+ if (QQmlLoggingCategoryBase *qQmlLoggingCategory
+ = qobject_cast<QQmlLoggingCategoryBase *>(wrapper)) {
+ const QLoggingCategory *loggingCategory = qQmlLoggingCategory->category();
*ok = true;
if (!loggingCategory) {
engine->handle()->throwError(
@@ -1309,8 +1702,10 @@ void AOTCompiledContext::writeToConsole(
const QV4::CppStackFrame *frame = engine->handle()->currentStackFrame;
Q_ASSERT(frame);
- QMessageLogger logger(qUtf8Printable(frame->source()), frame->lineNumber(),
- qUtf8Printable(frame->function()), loggingCategory->categoryName());
+ const QByteArray source(frame->source().toUtf8());
+ const QByteArray function(frame->function().toUtf8());
+ QMessageLogger logger(source.constData(), frame->lineNumber(),
+ function.constData(), loggingCategory->categoryName());
switch (type) {
case QtDebugMsg:
@@ -1330,6 +1725,32 @@ void AOTCompiledContext::writeToConsole(
}
}
+QVariant AOTCompiledContext::constructValueType(
+ QMetaType resultMetaType, const QMetaObject *resultMetaObject,
+ int ctorIndex, void *ctorArg) const
+{
+ return QQmlValueTypeProvider::constructValueType(
+ resultMetaType, resultMetaObject, ctorIndex, ctorArg);
+}
+
+QDateTime AOTCompiledContext::constructDateTime(double timestamp) const
+{
+ return QV4::DateObject::timestampToDateTime(timestamp);
+}
+
+QDateTime AOTCompiledContext::constructDateTime(const QString &string) const
+{
+ return QV4::DateObject::stringToDateTime(string, engine->handle());
+}
+
+QDateTime AOTCompiledContext::constructDateTime(
+ double year, double month, double day, double hours,
+ double minutes, double seconds, double msecs) const
+{
+ return constructDateTime(QV4::DateObject::componentsToTimestamp(
+ year, month, day, hours, minutes, seconds, msecs, engine->handle()));
+}
+
bool AOTCompiledContext::callQmlContextPropertyLookup(
uint index, void **args, const QMetaType *types, int argc) const
{
@@ -1483,11 +1904,17 @@ bool AOTCompiledContext::loadScopeObjectPropertyLookup(uint index, void *target)
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (!qmlScopeObject) {
+ engine->handle()->throwReferenceError(
+ compilationUnit->runtimeStrings[l->nameIndex]->toQString());
+ return false;
+ }
+
ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty)
- result = loadObjectProperty(l, qmlScopeObject, target, qmlContext);
+ result = loadObjectProperty(l, qmlScopeObject, target, this);
else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty)
- result = loadFallbackProperty(l, qmlScopeObject, target, qmlContext);
+ result = loadFallbackProperty(l, qmlScopeObject, target, this);
else
return false;
@@ -1506,6 +1933,29 @@ bool AOTCompiledContext::loadScopeObjectPropertyLookup(uint index, void *target)
Q_UNREACHABLE_RETURN(false);
}
+bool AOTCompiledContext::writeBackScopeObjectPropertyLookup(uint index, void *source) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty)
+ result = writeBackObjectProperty(l, qmlScopeObject, source);
+ else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty)
+ result = writeBackFallbackProperty(l, qmlScopeObject, source);
+ else
+ return false;
+
+ switch (result) {
+ case ObjectPropertyResult::NeedsInit:
+ return false;
+ case ObjectPropertyResult::Deleted: // Silently omit the write back. Same as interpreter
+ case ObjectPropertyResult::OK:
+ return true;
+ }
+
+ Q_UNREACHABLE_RETURN(false);
+}
+
void AOTCompiledContext::initLoadScopeObjectPropertyLookup(uint index, QMetaType type) const
{
QV4::ExecutionEngine *v4 = engine->handle();
@@ -1517,9 +1967,11 @@ void AOTCompiledContext::initLoadScopeObjectPropertyLookup(uint index, QMetaType
}
switch (initObjectLookup(this, l, qmlScopeObject, type)) {
+ case ObjectLookupResult::ObjectAsVariant:
case ObjectLookupResult::Object:
l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeObjectProperty;
break;
+ case ObjectLookupResult::FallbackAsVariant:
case ObjectLookupResult::Fallback:
l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeFallbackProperty;
break;
@@ -1558,17 +2010,21 @@ static void initTypeWrapperLookup(
if (importNamespace != AOTCompiledContext::InvalidStringId) {
QV4::Scope scope(context->engine->handle());
QV4::ScopedString import(scope, context->compilationUnit->runtimeStrings[importNamespace]);
+
+ QQmlTypeLoader *typeLoader = scope.engine->typeLoader();
+ Q_ASSERT(typeLoader);
if (const QQmlImportRef *importRef
- = context->qmlContext->imports()->query(import).importNamespace) {
+ = context->qmlContext->imports()->query(import, typeLoader).importNamespace) {
+
QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(
scope, QV4::QQmlTypeWrapper::create(
scope.engine, nullptr, context->qmlContext->imports(), importRef));
wrapper = l->qmlContextPropertyGetter(l, context->engine->handle(), wrapper);
l->qmlContextPropertyGetter = qmlContextPropertyGetter;
if (qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton)
- l->qmlContextSingletonLookup.singletonObject = wrapper->heapObject();
+ l->qmlContextSingletonLookup.singletonObject.set(scope.engine, wrapper->heapObject());
else if (qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupType)
- l->qmlTypeLookup.qmlTypeWrapper = wrapper->heapObject();
+ l->qmlTypeLookup.qmlTypeWrapper.set(scope.engine, wrapper->heapObject());
return;
}
scope.engine->throwTypeError();
@@ -1618,12 +2074,16 @@ void AOTCompiledContext::initLoadAttachedLookup(
QV4::ScopedString name(scope, compilationUnit->runtimeStrings[l->nameIndex]);
QQmlType type;
+ QQmlTypeLoader *typeLoader = scope.engine->typeLoader();
+ Q_ASSERT(typeLoader);
if (importNamespace != InvalidStringId) {
QV4::ScopedString import(scope, compilationUnit->runtimeStrings[importNamespace]);
- if (const QQmlImportRef *importRef = qmlContext->imports()->query(import).importNamespace)
- type = qmlContext->imports()->query(name, importRef).type;
+ if (const QQmlImportRef *importRef
+ = qmlContext->imports()->query(import, typeLoader).importNamespace) {
+ type = qmlContext->imports()->query(name, importRef, typeLoader).type;
+ }
} else {
- type = qmlContext->imports()->query<QQmlImport::AllowRecursion>(name).type;
+ type = qmlContext->imports()->query<QQmlImport::AllowRecursion>(name, typeLoader).type;
}
if (!type.isValid()) {
@@ -1635,7 +2095,7 @@ void AOTCompiledContext::initLoadAttachedLookup(
scope, QV4::QQmlTypeWrapper::create(scope.engine, object, type,
QV4::Heap::QQmlTypeWrapper::ExcludeEnums));
- l->qmlTypeLookup.qmlTypeWrapper = wrapper->d();
+ l->qmlTypeLookup.qmlTypeWrapper.set(scope.engine, wrapper->d());
l->getter = QV4::QObjectWrapper::lookupAttached;
}
@@ -1646,15 +2106,9 @@ bool AOTCompiledContext::loadTypeLookup(uint index, void *target) const
return false;
const QV4::Heap::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::Heap::QQmlTypeWrapper *>(
- l->qmlTypeLookup.qmlTypeWrapper);
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine());
+ l->qmlTypeLookup.qmlTypeWrapper.get());
QMetaType metaType = typeWrapper->type().typeId();
- if (!metaType.isValid()) {
- metaType = ep->typeLoader.getType(typeWrapper->type().sourceUrl())
- ->compilationUnit()->typeIds.id;
- }
-
*static_cast<const QMetaObject **>(target)
= QQmlMetaType::metaObjectForType(metaType).metaObject();
return true;
@@ -1668,7 +2122,6 @@ void AOTCompiledContext::initLoadTypeLookup(uint index, uint importNamespace) co
bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *target) const
{
-
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
const auto doThrow = [&]() {
engine->handle()->throwTypeError(
@@ -1682,9 +2135,13 @@ bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *targ
ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
if (l->getter == QV4::Lookup::getterQObject)
- result = loadObjectProperty(l, object, target, qmlContext);
+ result = loadObjectProperty(l, object, target, this);
else if (l->getter == QV4::Lookup::getterFallback)
- result = loadFallbackProperty(l, object, target, qmlContext);
+ result = loadFallbackProperty(l, object, target, this);
+ else if (l->getter == QV4::Lookup::getterQObjectAsVariant)
+ result = loadObjectAsVariant(l, object, target, this);
+ else if (l->getter == QV4::Lookup::getterFallbackAsVariant)
+ result = loadFallbackAsVariant(l, object, target, this);
else
return false;
@@ -1700,6 +2157,35 @@ bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *targ
Q_UNREACHABLE_RETURN(false);
}
+bool AOTCompiledContext::writeBackObjectLookup(uint index, QObject *object, void *source) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (!object)
+ return true;
+
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->getter == QV4::Lookup::getterQObject)
+ result = writeBackObjectProperty(l, object, source);
+ else if (l->getter == QV4::Lookup::getterFallback)
+ result = writeBackFallbackProperty(l, object, source);
+ else if (l->getter == QV4::Lookup::getterQObjectAsVariant)
+ result = writeBackObjectAsVariant(l, object, source);
+ else if (l->getter == QV4::Lookup::getterFallbackAsVariant)
+ result = writeBackFallbackAsVariant(l, object, source);
+ else
+ return false;
+
+ switch (result) {
+ case ObjectPropertyResult::NeedsInit:
+ return false;
+ case ObjectPropertyResult::Deleted: // Silently omit the write back
+ case ObjectPropertyResult::OK:
+ return true;
+ }
+
+ Q_UNREACHABLE_RETURN(false);
+}
+
void AOTCompiledContext::initGetObjectLookup(uint index, QObject *object, QMetaType type) const
{
QV4::ExecutionEngine *v4 = engine->handle();
@@ -1711,9 +2197,15 @@ void AOTCompiledContext::initGetObjectLookup(uint index, QObject *object, QMetaT
case ObjectLookupResult::Object:
l->getter = QV4::Lookup::getterQObject;
break;
+ case ObjectLookupResult::ObjectAsVariant:
+ l->getter = QV4::Lookup::getterQObjectAsVariant;
+ break;
case ObjectLookupResult::Fallback:
l->getter = QV4::Lookup::getterFallback;
break;
+ case ObjectLookupResult::FallbackAsVariant:
+ l->getter = QV4::Lookup::getterFallbackAsVariant;
+ break;
case ObjectLookupResult::Failure:
engine->handle()->throwTypeError();
break;
@@ -1740,6 +2232,25 @@ bool AOTCompiledContext::getValueLookup(uint index, void *value, void *target) c
return true;
}
+bool AOTCompiledContext::writeBackValueLookup(uint index, void *value, void *source) const
+{
+ Q_ASSERT(value);
+
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (l->getter != QV4::QQmlValueTypeWrapper::lookupGetter)
+ return false;
+
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qgadgetLookup.metaObject - 1);
+ Q_ASSERT(metaObject);
+
+ void *args[] = { source, nullptr };
+ metaObject->d.static_metacall(
+ reinterpret_cast<QObject*>(value), QMetaObject::WriteProperty,
+ l->qgadgetLookup.coreIndex, args);
+ return true;
+}
+
void AOTCompiledContext::initGetValueLookup(
uint index, const QMetaObject *metaObject, QMetaType type) const
{
@@ -1751,13 +2262,44 @@ void AOTCompiledContext::initGetValueLookup(
engine->handle()->throwTypeError();
}
-bool AOTCompiledContext::getEnumLookup(uint index, int *target) const
+bool AOTCompiledContext::getEnumLookup(uint index, void *target) const
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
if (l->getter != QV4::QQmlTypeWrapper::lookupEnumValue)
return false;
- *target = l->qmlEnumValueLookup.encodedEnumValue;
- return true;
+ const bool isUnsigned
+ = l->qmlEnumValueLookup.metaType->flags & QMetaType::IsUnsignedEnumeration;
+ const QV4::ReturnedValue encoded = l->qmlEnumValueLookup.encodedEnumValue;
+ switch (l->qmlEnumValueLookup.metaType->size) {
+ case 1:
+ if (isUnsigned)
+ *static_cast<quint8 *>(target) = encoded;
+ else
+ *static_cast<qint8 *>(target) = encoded;
+ return true;
+ case 2:
+ if (isUnsigned)
+ *static_cast<quint16 *>(target) = encoded;
+ else
+ *static_cast<qint16 *>(target) = encoded;
+ return true;
+ case 4:
+ if (isUnsigned)
+ *static_cast<quint32 *>(target) = encoded;
+ else
+ *static_cast<qint32 *>(target) = encoded;
+ return true;
+ case 8:
+ if (isUnsigned)
+ *static_cast<quint64 *>(target) = encoded;
+ else
+ *static_cast<qint64 *>(target) = encoded;
+ return true;
+ default:
+ break;
+ }
+
+ return false;
}
void AOTCompiledContext::initGetEnumLookup(
@@ -1766,10 +2308,16 @@ void AOTCompiledContext::initGetEnumLookup(
{
Q_ASSERT(!engine->hasError());
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- Q_ASSERT(metaObject);
+ if (!metaObject) {
+ engine->handle()->throwTypeError(
+ QStringLiteral("Cannot read property '%1' of undefined")
+ .arg(QString::fromUtf8(enumValue)));
+ return;
+ }
const int enumIndex = metaObject->indexOfEnumerator(enumerator);
- const int value = metaObject->enumerator(enumIndex).keyToValue(enumValue);
- l->qmlEnumValueLookup.encodedEnumValue = value;
+ const QMetaEnum metaEnum = metaObject->enumerator(enumIndex);
+ l->qmlEnumValueLookup.encodedEnumValue = metaEnum.keyToValue(enumValue);
+ l->qmlEnumValueLookup.metaType = metaEnum.metaType().iface();
l->getter = QV4::QQmlTypeWrapper::lookupEnumValue;
}
@@ -1790,6 +2338,10 @@ bool AOTCompiledContext::setObjectLookup(uint index, QObject *object, void *valu
result = storeObjectProperty(l, object, value);
else if (l->setter == QV4::Lookup::setterFallback)
result = storeFallbackProperty(l, object, value);
+ else if (l->setter == QV4::Lookup::setterQObjectAsVariant)
+ result = storeObjectAsVariant(engine->handle(), l, object, value);
+ else if (l->setter == QV4::Lookup::setterFallbackAsVariant)
+ result = storeFallbackAsVariant(engine->handle(), l, object, value);
else
return false;
@@ -1816,9 +2368,15 @@ void AOTCompiledContext::initSetObjectLookup(uint index, QObject *object, QMetaT
case ObjectLookupResult::Object:
l->setter = QV4::Lookup::setterQObject;
break;
+ case ObjectLookupResult::ObjectAsVariant:
+ l->setter = QV4::Lookup::setterQObjectAsVariant;
+ break;
case ObjectLookupResult::Fallback:
l->setter = QV4::Lookup::setterFallback;
break;
+ case ObjectLookupResult::FallbackAsVariant:
+ l->setter = QV4::Lookup::setterFallbackAsVariant;
+ break;
case ObjectLookupResult::Failure:
engine->handle()->throwTypeError();
break;
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index 1695a78bbf..3e6441bfa7 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -133,8 +133,16 @@ void qmlRegisterAnonymousTypesAndRevisions(const char *uri, int versionMajor)
nullptr, true);
}
+class QQmlTypeNotAvailable : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(TypeNotAvailable)
+ QML_ADDED_IN_VERSION(2, 15)
+ QML_UNCREATABLE("Type not available.")
+};
+
int Q_QML_EXPORT qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor,
- const char *qmlName, const QString& message);
+ const char *qmlName, const QString &message);
template<typename T>
int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason)
@@ -662,37 +670,25 @@ QObject *qmlAttachedPropertiesObject(const QObject *obj, bool create = true)
return qmlAttachedPropertiesObject(const_cast<QObject *>(obj), func, create);
}
-inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName,
- QJSValue (*callback)(QQmlEngine *, QJSEngine *))
-{
- QQmlPrivate::RegisterSingletonType api = {
- 0,
-
- uri, QTypeRevision::fromVersion(versionMajor, versionMinor), typeName,
-
- callback,
- nullptr, nullptr, QMetaType(),
- nullptr, nullptr,
- QTypeRevision::zero()
- };
-
- return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &api);
-}
-
-template <typename T>
-inline int qmlRegisterSingletonType(
- const char *uri, int versionMajor, int versionMinor, const char *typeName,
- QObject *(*callback)(QQmlEngine *, QJSEngine *))
+#ifdef Q_QDOC
+int qmlRegisterSingletonType(
+ const char *uri, int versionMajor, int versionMinor, const char *typeName,
+ std::function<QJSValue(QQmlEngine *, QJSEngine *)> callback)
+#else
+template<typename F, typename std::enable_if<std::is_convertible<F, std::function<QJSValue(QQmlEngine *, QJSEngine *)>>::value, void>::type* = nullptr>
+int qmlRegisterSingletonType(
+ const char *uri, int versionMajor, int versionMinor, const char *typeName, F &&callback)
+#endif
{
QQmlPrivate::RegisterSingletonType api = {
0,
uri,
QTypeRevision::fromVersion(versionMajor, versionMinor),
typeName,
+ std::forward<F>(callback),
nullptr,
- callback,
- QQmlPrivate::StaticMetaObject<T>::staticMetaObject(),
- QQmlPrivate::QmlMetaType<T>::self(),
+ nullptr,
+ QMetaType(),
nullptr, nullptr,
QTypeRevision::zero()
};
@@ -702,12 +698,13 @@ inline int qmlRegisterSingletonType(
#ifdef Q_QDOC
template <typename T>
-int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, std::function<QObject*(QQmlEngine *, QJSEngine *)> callback)
+int qmlRegisterSingletonType(
+ const char *uri, int versionMajor, int versionMinor, const char *typeName,
+ std::function<QObject *(QQmlEngine *, QJSEngine *)> callback)
#else
-template <typename T, typename F, typename std::enable_if<std::is_convertible<F, std::function<QObject *(QQmlEngine *, QJSEngine *)>>::value
- && !std::is_convertible<F, QObject *(*)(QQmlEngine *, QJSEngine *)>::value, void>::type* = nullptr>
-inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName,
- F&& callback)
+template<typename T, typename F, typename std::enable_if<std::is_convertible<F, std::function<QObject *(QQmlEngine *, QJSEngine *)>>::value, void>::type* = nullptr>
+int qmlRegisterSingletonType(
+ const char *uri, int versionMajor, int versionMinor, const char *typeName, F &&callback)
#endif
{
QQmlPrivate::RegisterSingletonType api = {
@@ -716,7 +713,7 @@ inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versi
QTypeRevision::fromVersion(versionMajor, versionMinor),
typeName,
nullptr,
- callback,
+ std::forward<F>(callback),
QQmlPrivate::StaticMetaObject<T>::staticMetaObject(),
QQmlPrivate::QmlMetaType<T>::self(),
nullptr, nullptr,
@@ -818,8 +815,8 @@ struct QmlTypeAndRevisionsRegistration<T, Resolved, Extended, false, false, fals
}
#else
static_assert(QQmlPrivate::QmlMetaType<Resolved>::hasAcceptableCtors(),
- "This type is neither a QObject, nor default- and copy-constructible, nor"
- "uncreatable.\n"
+ "This type is neither a default constructible QObject, nor a default- "
+ "and copy-constructible Q_GADGET, nor marked as uncreatable.\n"
"You should not use it as a QML type.");
static_assert(std::is_base_of_v<QObject, Resolved>
|| !QQmlTypeInfo<Resolved>::hasAttachedProperties);
@@ -871,12 +868,14 @@ struct QmlTypeAndRevisionsRegistration<T, Resolved, Extended, true, false, false
{
#if QT_DEPRECATED_SINCE(6, 4)
// ### Qt7: Remove the warning, and leave only the static assert below.
- if constexpr (!QQmlPrivate::QmlMetaType<Resolved>::hasAcceptableSingletonCtors()) {
+ if constexpr (QQmlPrivate::singletonConstructionMode<Resolved, T>()
+ == QQmlPrivate::SingletonConstructionMode::None) {
QQmlPrivate::qmlRegistrationWarning(QQmlPrivate::UnconstructibleSingleton,
QMetaType::fromType<Resolved>());
}
#else
- static_assert(QQmlPrivate::QmlMetaType<Resolved>::hasAcceptableSingletonCtors(),
+ static_assert(QQmlPrivate::singletonConstructionMode<Resolved, T>()
+ != QQmlPrivate::SingletonConstructionMode::None,
"A singleton needs either a default constructor or, when adding a default "
"constructor is infeasible, a public static "
"create(QQmlEngine *, QJSEngine *) method");
@@ -920,7 +919,7 @@ void qmlRegisterTypesAndRevisions(const char *uri, int versionMajor, QList<int>
QQmlPrivate::QmlSingleton<T>::Value,
QQmlPrivate::QmlInterface<T>::Value,
QQmlPrivate::QmlSequence<T>::Value,
- QQmlPrivate::QmlUncreatable<T>::Value>
+ QQmlPrivate::QmlUncreatable<T>::Value || QQmlPrivate::QmlAnonymous<T>::Value>
::registerTypeAndRevisions(uri, versionMajor, qmlTypeIds,
QQmlPrivate::QmlExtendedNamespace<T>::metaObject()), ...);
}
diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h
index d4c00ea886..8230c6aa6b 100644
--- a/src/qml/qml/qqmlabstractbinding_p.h
+++ b/src/qml/qml/qqmlabstractbinding_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQmlObjectCreator;
class QQmlAnyBinding;
-class Q_QML_PRIVATE_EXPORT QQmlAbstractBinding
+class Q_QML_EXPORT QQmlAbstractBinding
{
friend class QQmlAnyBinding;
protected:
diff --git a/src/qml/qml/qqmlanybinding_p.h b/src/qml/qml/qqmlanybinding_p.h
index fbe4c12350..f432d2abae 100644
--- a/src/qml/qml/qqmlanybinding_p.h
+++ b/src/qml/qml/qqmlanybinding_p.h
@@ -73,10 +73,10 @@ public:
{
QQmlAnyBinding binding;
Q_ASSERT(object);
- QQmlData *data = QQmlData::get(object, true);
auto coreIndex = index.coreIndex();
// we don't support bindable properties on value types so far
- if (!index.hasValueTypeIndex() && data->propertyCache->property(coreIndex)->isBindable()) {
+ if (!index.hasValueTypeIndex()
+ && QQmlData::ensurePropertyCache(object)->property(coreIndex)->isBindable()) {
auto metaProp = object->metaObject()->property(coreIndex);
QUntypedBindable bindable = metaProp.bindable(object);
binding = bindable.binding();
@@ -459,8 +459,7 @@ private:
delete qqmlptr;
} else if (d.isT2()) {
QPropertyBindingPrivate *priv = d.asT2();
- priv->ref--;
- if (!priv->ref)
+ if (!priv->deref())
QPropertyBindingPrivate::destroyAndFreeMemory(priv);
}
d = static_cast<QQmlAbstractBinding *>(nullptr);
diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp
index 869291caad..82cc335c8e 100644
--- a/src/qml/qml/qqmlapplicationengine.cpp
+++ b/src/qml/qml/qqmlapplicationengine.cpp
@@ -7,10 +7,10 @@
#include <QQmlComponent>
#include "qqmlapplicationengine.h"
#include "qqmlapplicationengine_p.h"
+#include <QtQml/private/qqmlcomponent_p.h>
+#include <QtQml/private/qqmldirdata_p.h>
#include <QtQml/private/qqmlfileselector_p.h>
-#include <memory>
-
QT_BEGIN_NAMESPACE
QQmlApplicationEnginePrivate::QQmlApplicationEnginePrivate(QQmlEngine *e)
@@ -109,15 +109,36 @@ void QQmlApplicationEnginePrivate::startLoad(const QUrl &url, const QByteArray &
ensureLoadingFinishes(c);
}
-void QQmlApplicationEnginePrivate::startLoad(QAnyStringView uri, QAnyStringView type)
+void QQmlApplicationEnginePrivate::startLoad(QAnyStringView uri, QAnyStringView typeName)
{
Q_Q(QQmlApplicationEngine);
- _q_loadTranslations(); //Translations must be loaded before the QML file is
QQmlComponent *c = new QQmlComponent(q, q);
ensureInitialized();
- c->loadFromModule(uri, type);
+
+ auto *componentPriv = QQmlComponentPrivate::get(c);
+ const auto [status, type] = componentPriv->prepareLoadFromModule(uri, typeName);
+
+ if (type.sourceUrl().isValid()) {
+ const auto qmlDirData = typeLoader.getQmldir(type.sourceUrl());
+ const QUrl url = qmlDirData->finalUrl();
+ if (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc")) {
+ QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
+ translationsDirectory = fi.path() + QLatin1String("/i18n");
+ } else {
+ translationsDirectory.clear();
+ }
+ }
+
+ /* Translations must be loaded before the QML file. They require translationDirectory to
+ * already be resolved. But, in order to resolve the translationDirectory, the type of the
+ * module to load needs to be known. Therefore, loadFromModule is split into resolution and
+ * loading because the translation directory needs to be set in between.
+ */
+ _q_loadTranslations();
+ componentPriv->completeLoadFromModule(uri, typeName, type, status);
+
ensureLoadingFinishes(c);
}
@@ -236,10 +257,9 @@ void QQmlApplicationEnginePrivate::ensureLoadingFinishes(QQmlComponent *c)
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
- // quit on error
- QObject::connect(&app, QQmlApplicationEngine::objectCreationFailed,
- QCoreApplication::instance(), QCoreApplication::quit,
- Qt::QueuedConnection);
+ // exit on error
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
+ &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection);
engine.load(QUrl());
return app.exec();
\endcode
@@ -354,6 +374,11 @@ void QQmlApplicationEngine::load(const QString &filePath)
engine.loadFromModule("QtQuick", "Rectangle");
\endcode
+ \note The module identified by \a uri is searched in the
+ \l {QML Import Path}{import path}, in the same way as if
+ you were doing \c{import uri} inside a QML file. If the
+ module cannot be located there, this function will fail.
+
\since 6.5
\sa QQmlComponent::loadFromModule
*/
diff --git a/src/qml/qml/qqmlapplicationengine_p.h b/src/qml/qml/qqmlapplicationengine_p.h
index 4cf21bba6d..df99783e0a 100644
--- a/src/qml/qml/qqmlapplicationengine_p.h
+++ b/src/qml/qml/qqmlapplicationengine_p.h
@@ -24,7 +24,7 @@
QT_BEGIN_NAMESPACE
class QFileSelector;
-class Q_QML_PRIVATE_EXPORT QQmlApplicationEnginePrivate : public QQmlEnginePrivate
+class Q_QML_EXPORT QQmlApplicationEnginePrivate : public QQmlEnginePrivate
{
Q_DECLARE_PUBLIC(QQmlApplicationEngine)
public:
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index cd4af5c839..4dfee0a3c6 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -29,6 +29,9 @@
QT_BEGIN_NAMESPACE
+Q_TRACE_POINT(qtqml, QQmlBinding_entry, const QQmlEngine *engine, const QString &function, const QString &fileName, int line, int column)
+Q_TRACE_POINT(qtqml, QQmlBinding_exit)
+
QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt)
{
QQmlBinding *b = newBinding(property);
@@ -254,7 +257,7 @@ protected:
break;
default:
if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) {
- if (vtw->d()->valueType()->metaType() == pd->propType()) {
+ if (vtw->d()->metaType() == pd->propType()) {
return vtw->write(m_target.data(), pd->coreIndex());
}
}
@@ -407,6 +410,8 @@ QQmlBinding *QQmlBinding::createTranslationBinding(
location.line, location.column });
}
+#else
+ Q_UNUSED(propertyName)
#endif
return b;
}
@@ -528,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
@@ -670,22 +676,23 @@ void QQmlBinding::doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::Write
auto canWrite = [&]() { return !watcher.wasDeleted() && isAddedToObject() && !hasError(); };
const QV4::Function *v4Function = function();
if (v4Function && v4Function->kind == QV4::Function::AotCompiled && !hasBoundFunction()) {
- const auto returnType = v4Function->typedFunction->returnType;
+ const auto returnType = v4Function->aotCompiledFunction.types[0];
if (returnType == QMetaType::fromType<QVariant>()) {
- // It expects uninitialized memory
- Q_ALLOCA_VAR(QVariant, result, sizeof(QVariant));
- const bool isUndefined = !evaluate(result, returnType);
+ QVariant result;
+ const bool isUndefined = !evaluate(&result, returnType);
if (canWrite())
- error = !write(result->data(), result->metaType(), isUndefined, flags);
- result->~QVariant();
+ error = !write(result.data(), result.metaType(), isUndefined, flags);
} else {
const auto size = returnType.sizeOf();
if (Q_LIKELY(size > 0)) {
Q_ALLOCA_VAR(void, result, size);
+ if (returnType.flags() & QMetaType::NeedsConstruction)
+ returnType.construct(result);
const bool isUndefined = !evaluate(result, returnType);
if (canWrite())
error = !write(result, returnType, isUndefined, flags);
- returnType.destruct(result);
+ if (returnType.flags() & QMetaType::NeedsDestruction)
+ returnType.destruct(result);
} else if (canWrite()) {
error = !write(QV4::Encode::undefined(), true, flags);
}
diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h
index 00aa0d6f58..4031a2655e 100644
--- a/src/qml/qml/qqmlbinding_p.h
+++ b/src/qml/qml/qqmlbinding_p.h
@@ -29,7 +29,7 @@
QT_BEGIN_NAMESPACE
class QQmlContext;
-class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlJavaScriptExpression,
+class Q_QML_EXPORT QQmlBinding : public QQmlJavaScriptExpression,
public QQmlAbstractBinding
{
friend class QQmlAbstractBinding;
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp
index da81244f18..0bac2f45a2 100644
--- a/src/qml/qml/qqmlboundsignal.cpp
+++ b/src/qml/qml/qqmlboundsignal.cpp
@@ -24,6 +24,10 @@
QT_BEGIN_NAMESPACE
+Q_TRACE_POINT(qtqml, QQmlHandlingSignal_entry, const QQmlEngine *engine, const QString &function,
+ const QString &fileName, int line, int column)
+Q_TRACE_POINT(qtqml, QQmlHandlingSignal_exit)
+
QQmlBoundSignalExpression::QQmlBoundSignalExpression(const QObject *target, int index, const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope,
const QString &expression, const QString &fileName, quint16 line, quint16 column,
const QString &handlerName, const QString &parameterString)
@@ -58,7 +62,8 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(const QObject *target, int
function += QLatin1String(") { ") + expression + QLatin1String(" })");
QV4::Scope valueScope(v4);
- QV4::ScopedFunctionObject f(valueScope, evalFunction(context(), scopeObject(), function, fileName, line));
+ QV4::Scoped<QV4::JavaScriptFunctionObject> f(
+ valueScope, evalFunction(context(), scopeObject(), function, fileName, line));
QV4::ScopedContext context(valueScope, f->scope());
setupFunction(context, f->function());
}
@@ -103,7 +108,7 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(const QObject *target, int
// we need to run the outer function to get the nested one.
if (function->isClosureWrapper()) {
bool isUndefined = false;
- QV4::ScopedFunctionObject result(
+ QV4::Scoped<QV4::JavaScriptFunctionObject> result(
valueScope, QQmlJavaScriptExpression::evaluate(&isUndefined));
Q_ASSERT(!isUndefined);
@@ -175,7 +180,7 @@ void QQmlBoundSignalExpression::evaluate(void **a)
QMetaObjectPrivate::signal(targetMeta, m_index).methodIndex());
int argCount = metaMethod.parameterCount();
- QQmlMetaObject::ArgTypeStorage storage;
+ QQmlMetaObject::ArgTypeStorage<9> storage;
storage.reserve(argCount + 1);
storage.append(QMetaType()); // We're not interested in the return value
for (int i = 0; i < argCount; ++i) {
@@ -183,7 +188,7 @@ void QQmlBoundSignalExpression::evaluate(void **a)
if (!type.isValid())
argCount = 0;
else if (type.flags().testFlag(QMetaType::IsEnumeration))
- storage.append(QMetaType::fromType<int>());
+ storage.append(type.underlyingType());
else
storage.append(type);
}
diff --git a/src/qml/qml/qqmlboundsignal_p.h b/src/qml/qml/qqmlboundsignal_p.h
index 42612b1df7..9eab562f56 100644
--- a/src/qml/qml/qqmlboundsignal_p.h
+++ b/src/qml/qml/qqmlboundsignal_p.h
@@ -24,8 +24,11 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlBoundSignalExpression : public QQmlJavaScriptExpression, public QQmlRefCount
+class Q_QML_EXPORT QQmlBoundSignalExpression final
+ : public QQmlJavaScriptExpression,
+ public QQmlRefCounted<QQmlBoundSignalExpression>
{
+ friend class QQmlRefCounted<QQmlBoundSignalExpression>;
public:
QQmlBoundSignalExpression(
const QObject *target, int index, const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope,
@@ -59,7 +62,7 @@ private:
const QObject *m_target;
};
-class Q_QML_PRIVATE_EXPORT QQmlBoundSignal : public QQmlNotifierEndpoint
+class Q_QML_EXPORT QQmlBoundSignal : public QQmlNotifierEndpoint
{
public:
QQmlBoundSignal(QObject *target, int signal, QObject *owner, QQmlEngine *engine);
diff --git a/src/qml/qml/qqmlbuiltinfunctions.cpp b/src/qml/qml/qqmlbuiltinfunctions.cpp
index 36111f32a6..bc5d87881b 100644
--- a/src/qml/qml/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/qqmlbuiltinfunctions.cpp
@@ -3,48 +3,35 @@
#include "qqmlbuiltinfunctions_p.h"
-#include <QtQml/qqmlcomponent.h>
-#include <QtQml/qqmlfile.h>
-#include <private/qqmlengine_p.h>
#include <private/qqmlcomponent_p.h>
-#include <private/qqmlloggingcategory_p.h>
-#include <private/qqmlstringconverters_p.h>
-#if QT_CONFIG(qml_locale)
-#include <private/qqmllocale_p.h>
-#endif
-#include <private/qqmldelayedcallqueue_p.h>
-#include <QFileInfo>
-
#include <private/qqmldebugconnector_p.h>
#include <private/qqmldebugserviceinterfaces_p.h>
-#include <private/qqmlglobal_p.h>
-
+#include <private/qqmldelayedcallqueue_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qqmlloggingcategorybase_p.h>
#include <private/qqmlplatform_p.h>
+#include <private/qqmlstringconverters_p.h>
+#include <private/qv4dateobject_p.h>
#include <private/qv4engine_p.h>
#include <private/qv4functionobject_p.h>
#include <private/qv4include_p.h>
-#include <private/qv4context_p.h>
-#include <private/qv4stringobject_p.h>
-#include <private/qv4dateobject_p.h>
#include <private/qv4mm_p.h>
-#include <private/qv4jsonobject_p.h>
-#include <private/qv4objectproto_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qv4stackframe_p.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qdatetime.h>
+#include <QtQml/qqmlfile.h>
+
+#include <QtCore/qcoreapplication.h>
#include <QtCore/qcryptographichash.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qpoint.h>
#include <QtCore/qrect.h>
#include <QtCore/qsize.h>
-#include <QtCore/qpoint.h>
+#include <QtCore/qstring.h>
#include <QtCore/qurl.h>
-#include <QtCore/qfile.h>
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qloggingcategory.h>
-
-#include <QDebug>
QT_BEGIN_NAMESPACE
@@ -87,6 +74,7 @@ the \l Qt::LeftButton and \l Qt::RightButton enumeration values as \c Qt.LeftBut
\section1 Types
+\target globalqtobjecttypes
The Qt object also contains helper functions for creating objects of specific
data types. This is primarily useful when setting the properties of an item
@@ -109,8 +97,6 @@ creating objects of specific data types are also available for clients to use:
\li \c matrix4x4 - use \l{Qt::matrix4x4()}{Qt.matrix4x4()}
\endlist
-There are also string based constructors for these types. See \l{qtqml-typesystem-valuetypes.html}{QML Value Types} for more information.
-
\section1 Date/Time Formatters
The Qt object contains several functions for formatting QDateTime, QDate and QTime values.
@@ -173,6 +159,7 @@ The following functions are also on the Qt object.
\li \c "android" - Android
\li \c "ios" - iOS
\li \c "tvos" - tvOS
+ \li \c "visionos" - visionOS
\li \c "linux" - Linux
\li \c "osx" - \macos
\li \c "qnx" - QNX (since Qt 5.9.3)
@@ -181,6 +168,9 @@ The following functions are also on the Qt object.
\li \c "wasm" - WebAssembly
\endlist
+ \note The property's value on \macos is "osx", regardless of Apple naming convention.
+ The returned value will be updated to "macos" for Qt 7.
+
\row
\li \c platform.pluginName
\li This is the name of the platform set on the QGuiApplication instance
@@ -190,7 +180,7 @@ The following functions are also on the Qt object.
*/
/*!
- \qmlproperty object Qt::application
+ \qmlproperty Application Qt::application
\since 5.1
The \c application object provides access to global application state
@@ -213,7 +203,7 @@ The following functions are also on the Qt object.
*/
/*!
- \qmlproperty object Qt::inputMethod
+ \qmlproperty InputMethod Qt::inputMethod
\since 5.0
It is the same as the \l InputMethod singleton.
@@ -263,6 +253,9 @@ The \c status property will be updated as the operation progresses.
If provided, \a callback is invoked when the operation completes. The callback is passed
the same object as is returned from the Qt.include() call.
+
+\warning Using this function is strict mode does not actually put identifier into the
+current context.
*/
// Qt.include() is implemented in qv4include.cpp
@@ -475,8 +468,9 @@ QVariant QtObject::font(const QJSValue &fontSpecifier) const
}
{
- QVariant v((QMetaType(QMetaType::QFont)));
- if (QQmlValueTypeProvider::constructFromJSValue(fontSpecifier, v.metaType(), v.data()))
+ const QVariant v = QQmlValueTypeProvider::createValueType(
+ fontSpecifier, QMetaType(QMetaType::QFont));
+ if (v.isValid())
return v;
}
@@ -511,9 +505,8 @@ static QVariant constructFromJSValue(QJSEngine *e, QMetaType type, T... paramete
return QVariant();
QJSValue params = e->newArray(sizeof...(parameters));
addParameters(e, params, 0, parameters...);
- QVariant variant(type);
- QQmlValueTypeProvider::constructFromJSValue(params, type, variant.data());
- return variant;
+ const QVariant variant = QQmlValueTypeProvider::createValueType(params, type);
+ return variant.isValid() ? variant : QVariant(type);
}
/*!
@@ -563,10 +556,9 @@ QVariant QtObject::quaternion(double scalar, double x, double y, double z) const
*/
QVariant QtObject::matrix4x4() const
{
- QVariant variant((QMetaType(QMetaType::QMatrix4x4)));
- QQmlValueTypeProvider::constructFromJSValue(
- QJSValue(), variant.metaType(), variant.data());
- return variant;
+ const QMetaType metaType(QMetaType::QMatrix4x4);
+ const QVariant variant = QQmlValueTypeProvider::createValueType(QJSValue(), metaType);
+ return variant.isValid() ? variant : QVariant(metaType);
}
/*!
@@ -587,8 +579,9 @@ QVariant QtObject::matrix4x4() const
QVariant QtObject::matrix4x4(const QJSValue &value) const
{
if (value.isObject()) {
- QVariant v((QMetaType(QMetaType::QMatrix4x4)));
- if (QQmlValueTypeProvider::constructFromJSValue(value, v.metaType(), v.data()))
+ QVariant v = QQmlValueTypeProvider::createValueType(
+ value, QMetaType(QMetaType::QMatrix4x4));
+ if (v.isValid())
return v;
}
@@ -808,12 +801,12 @@ static std::optional<QDate> dateFromString(const QString &string, QV4::Execution
return std::nullopt;
}
-QString QtObject::formatDate(const QDate &date, const QString &format) const
+QString QtObject::formatDate(QDate date, const QString &format) const
{
return date.toString(format);
}
-QString QtObject::formatDate(const QDate &date, Qt::DateFormat format) const
+QString QtObject::formatDate(QDate date, Qt::DateFormat format) const
{
return formatDateTimeObjectUsingDateFormat(date, format);
}
@@ -845,7 +838,7 @@ QString QtObject::formatDate(const QString &string, Qt::DateFormat format) const
}
#if QT_CONFIG(qml_locale)
-QString QtObject::formatDate(const QDate &date, const QLocale &locale,
+QString QtObject::formatDate(QDate date, const QLocale &locale,
QLocale::FormatType formatType) const
{
return locale.toString(date, formatType);
@@ -913,7 +906,7 @@ static std::optional<QTime> timeFromString(const QString &string, QV4::Execution
return std::nullopt;
}
-QString QtObject::formatTime(const QTime &time, const QString &format) const
+QString QtObject::formatTime(QTime time, const QString &format) const
{
return time.toString(format);
}
@@ -932,7 +925,7 @@ QString QtObject::formatTime(const QString &time, const QString &format) const
return QString();
}
-QString QtObject::formatTime(const QTime &time, Qt::DateFormat format) const
+QString QtObject::formatTime(QTime time, Qt::DateFormat format) const
{
return formatDateTimeObjectUsingDateFormat(time, format);
}
@@ -951,7 +944,7 @@ QString QtObject::formatTime(const QString &time, Qt::DateFormat format) const
}
#if QT_CONFIG(qml_locale)
-QString QtObject::formatTime(const QTime &time, const QLocale &locale,
+QString QtObject::formatTime(QTime time, const QLocale &locale,
QLocale::FormatType formatType) const
{
return locale.toString(time, formatType);
@@ -1300,10 +1293,15 @@ Each object in this array has the members \c lineNumber, \c columnNumber, \c fil
For example, if the above snippet had misspelled color as 'colro' then the array would contain an object like the following:
{ "lineNumber" : 1, "columnNumber" : 32, "fileName" : "dynamicSnippet1", "message" : "Cannot assign to non-existent property \"colro\""}.
-Note that this function returns immediately, and therefore may not work if
+\note This function returns immediately, and therefore may not work if
the \a qml string loads new components (that is, external QML files that have not yet been loaded).
If this is the case, consider using \l{QtQml::Qt::createComponent()}{Qt.createComponent()} instead.
+\warning This function is extremely slow since it has to compile the passed QML string every time
+it is invoked. Furthermore, it's very easy to produce invalid QML when programmatically constructing
+QML code. It's much better to keep your QML components as separate files and add properties and
+methods to customize their behavior than to produce new components by string manipulation.
+
See \l {Dynamic QML Object Creation from JavaScript} for more information on using this function.
*/
QObject *QtObject::createQmlObject(const QString &qml, QObject *parent, const QUrl &url) const
@@ -1465,14 +1463,15 @@ use \l{QtQml::Qt::createQmlObject()}{Qt.createQmlObject()}.
*/
/*!
+\since 6.5
\qmlmethod Component Qt::createComponent(string moduleUri, string typeName, enumeration mode, QtObject parent)
\overload
Returns a \l Component object created for the type specified by \a moduleUri and \a typeName.
\qml
-import QtQuick
+import QtQml
QtObject {
id: root
- property Component myComponent: Qt.createComponent(Rectangle, root)
+ property Component myComponent: Qt.createComponent("QtQuick", "Rectangle", Component.Asynchronous, root)
}
\endqml
This overload mostly behaves as the \c url based version, but can be used
@@ -1609,14 +1608,21 @@ QLocale QtObject::locale(const QString &name) const
}
#endif
-void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *bindingFunction)
+void Heap::QQmlBindingFunction::init(const QV4::JavaScriptFunctionObject *bindingFunction)
{
Scope scope(bindingFunction->engine());
ScopedContext context(scope, bindingFunction->scope());
- FunctionObject::init(context, bindingFunction->function());
+ JavaScriptFunctionObject::init(context, bindingFunction->function());
this->bindingFunction.set(internalClass->engine, bindingFunction->d());
}
+ReturnedValue QQmlBindingFunction::virtualCall(
+ const FunctionObject *f, const Value *, const Value *, int)
+{
+ // Mark this as a callable object, so that we can perform the binding magic on it.
+ return f->engine()->throwTypeError(QStringLiteral("Bindings must not be called directly."));
+}
+
QQmlSourceLocation QQmlBindingFunction::currentLocation() const
{
QV4::CppStackFrame *frame = engine()->currentStackFrame;
@@ -1671,7 +1677,8 @@ DEFINE_OBJECT_VTABLE(QQmlBindingFunction);
*/
QJSValue QtObject::binding(const QJSValue &function) const
{
- const QV4::FunctionObject *f = QJSValuePrivate::asManagedType<FunctionObject>(&function);
+ const QV4::JavaScriptFunctionObject *f
+ = QJSValuePrivate::asManagedType<JavaScriptFunctionObject>(&function);
QV4::ExecutionEngine *e = v4Engine();
if (!f) {
return QJSValuePrivate::fromReturnedValue(
@@ -1684,7 +1691,7 @@ QJSValue QtObject::binding(const QJSValue &function) const
Encode(e->memoryManager->allocate<QQmlBindingFunction>(f)));
}
-void QtObject::callLater(QQmlV4Function *args)
+void QtObject::callLater(QQmlV4FunctionPtr args)
{
m_engine->delayedCallQueue()->addUniquelyAndExecuteLater(m_engine, args);
}
@@ -1749,21 +1756,22 @@ enum ConsoleLogTypes {
static QString jsStack(QV4::ExecutionEngine *engine) {
QString stack;
- QVector<QV4::StackFrame> stackTrace = engine->stackTrace(10);
-
- for (int i = 0; i < stackTrace.size(); i++) {
- const QV4::StackFrame &frame = stackTrace.at(i);
-
+ int i = 0;
+ for (CppStackFrame *f = engine->currentStackFrame; f && i < 10; f = f->parentFrame(), ++i) {
QString stackFrame;
- if (frame.column >= 0)
- stackFrame = QStringLiteral("%1 (%2:%3:%4)").arg(frame.function,
- frame.source,
- QString::number(frame.line),
- QString::number(frame.column));
- else
- stackFrame = QStringLiteral("%1 (%2:%3)").arg(frame.function,
- frame.source,
- QString::number(frame.line));
+
+ if (f->isJSTypesFrame() && static_cast<JSTypesStackFrame *>(f)->isTailCalling()) {
+ stackFrame = QStringLiteral("[elided tail calls]");
+ } else {
+ const int line = f->lineNumber();
+ if (line != f->missingLineNumber()) {
+ stackFrame = QStringLiteral("%1 (%2:%3)").arg(
+ f->function(), f->source(), QString::number(qAbs(line)));
+ } else {
+ stackFrame = QStringLiteral("%1 (%2)").arg(
+ f->function(), f->source());
+ }
+ }
if (i)
stack += QLatin1Char('\n');
@@ -1808,7 +1816,8 @@ static ReturnedValue writeToConsole(const FunctionObject *b, const Value *argv,
int start = 0;
if (argc > 0) {
if (const QObjectWrapper* wrapper = argv[0].as<QObjectWrapper>()) {
- if (QQmlLoggingCategory* category = qobject_cast<QQmlLoggingCategory*>(wrapper->object())) {
+ if (QQmlLoggingCategoryBase *category
+ = qobject_cast<QQmlLoggingCategoryBase *>(wrapper->object())) {
if (category->category())
loggingCategory = category->category();
else
@@ -1989,7 +1998,7 @@ ReturnedValue ConsoleObject::method_trace(const FunctionObject *b, const Value *
QV4::CppStackFrame *frame = v4->currentStackFrame;
QMessageLogger(frame->source().toUtf8().constData(), frame->lineNumber(),
frame->function().toUtf8().constData())
- .debug("%s", qPrintable(stack));
+ .debug(v4->qmlEngine() ? lcQml() : lcJs(), "%s", qPrintable(stack));
return QV4::Encode::undefined();
}
@@ -2133,7 +2142,7 @@ ReturnedValue GlobalExtensions::method_qsTranslate(const FunctionObject *b, cons
}
/*!
- \qmlmethod string Qt::qsTranslateNoOp(string context, string sourceText, string disambiguation)
+ \qmlmethod string Qt::QT_TRANSLATE_NOOP(string context, string sourceText, string disambiguation)
Marks \a sourceText for dynamic translation in the given \a context; i.e, the stored \a sourceText
will not be altered.
@@ -2170,8 +2179,12 @@ QString GlobalExtensions::currentTranslationContext(ExecutionEngine *engine)
// The first non-empty source URL in the call stack determines the translation context.
while (frame && context.isEmpty()) {
- if (CompiledData::CompilationUnitBase *baseUnit = frame->v4Function->compilationUnit) {
- const auto *unit = static_cast<const CompiledData::CompilationUnit *>(baseUnit);
+ if (ExecutableCompilationUnit *unit = frame->v4Function->executableCompilationUnit()) {
+ auto translationContextIndex = unit->unitData()->translationContextIndex();
+ if (translationContextIndex)
+ context = unit->stringAt(*translationContextIndex);
+ if (!context.isEmpty())
+ break;
QString fileName = unit->fileName();
QUrl url(unit->fileName());
if (url.isValid() && url.isRelative()) {
@@ -2244,7 +2257,7 @@ ReturnedValue GlobalExtensions::method_qsTr(const FunctionObject *b, const Value
}
/*!
- \qmlmethod string Qt::qsTrNoOp(string sourceText, string disambiguation)
+ \qmlmethod string Qt::QT_TR_NOOP(string sourceText, string disambiguation)
Marks \a sourceText for dynamic translation; i.e, the stored \a sourceText
will not be altered.
@@ -2325,7 +2338,7 @@ ReturnedValue GlobalExtensions::method_qsTrId(const FunctionObject *b, const Val
}
/*!
- \qmlmethod string Qt::qsTrIdNoOp(string id)
+ \qmlmethod string Qt::QT_TRID_NOOP(string id)
Marks \a id for dynamic translation.
@@ -2349,16 +2362,23 @@ ReturnedValue GlobalExtensions::method_qsTrIdNoOp(const FunctionObject *, const
}
#endif // translation
+/*!
+ \qmlmethod void Qt::gc()
+ Runs the garbage collector.
+
+ This is equivalent to calling QJSEngine::collectGarbage().
+
+ \sa {Garbage Collection}
+*/
ReturnedValue GlobalExtensions::method_gc(const FunctionObject *b, const Value *, const Value *, int)
{
- b->engine()->memoryManager->runGC();
+ auto mm = b->engine()->memoryManager;
+ mm->runFullGC();
return QV4::Encode::undefined();
}
-
-
ReturnedValue GlobalExtensions::method_string_arg(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
QV4::Scope scope(b);
diff --git a/src/qml/qml/qqmlbuiltinfunctions_p.h b/src/qml/qml/qqmlbuiltinfunctions_p.h
index 46c4f415e3..c2732e1aff 100644
--- a/src/qml/qml/qqmlbuiltinfunctions_p.h
+++ b/src/qml/qml/qqmlbuiltinfunctions_p.h
@@ -46,7 +46,6 @@ class Q_QML_EXPORT QtObject : public QObject
QML_NAMED_ELEMENT(Qt)
QML_SINGLETON
QML_EXTENDED_NAMESPACE(Qt)
- QML_ADDED_IN_VERSION(2, 0)
Q_CLASSINFO("QML.StrictArguments", "true")
@@ -85,17 +84,17 @@ public:
Q_INVOKABLE QVariant alpha(const QJSValue &baseColor, double value) const;
Q_INVOKABLE QVariant tint(const QJSValue &baseColor, const QJSValue &tintColor) const;
- Q_INVOKABLE QString formatDate(const QDate &date, const QString &format) const;
+ Q_INVOKABLE QString formatDate(QDate date, const QString &format) const;
Q_INVOKABLE QString formatDate(const QDateTime &dateTime, const QString &format) const;
Q_INVOKABLE QString formatDate(const QString &string, const QString &format) const;
- Q_INVOKABLE QString formatDate(const QDate &date, Qt::DateFormat format) const;
+ Q_INVOKABLE QString formatDate(QDate date, Qt::DateFormat format) const;
Q_INVOKABLE QString formatDate(const QDateTime &dateTime, Qt::DateFormat format) const;
Q_INVOKABLE QString formatDate(const QString &string, Qt::DateFormat format) const;
- Q_INVOKABLE QString formatTime(const QTime &time, const QString &format) const;
+ Q_INVOKABLE QString formatTime(QTime time, const QString &format) const;
Q_INVOKABLE QString formatTime(const QDateTime &dateTime, const QString &format) const;
Q_INVOKABLE QString formatTime(const QString &time, const QString &format) const;
- Q_INVOKABLE QString formatTime(const QTime &time, Qt::DateFormat format) const;
+ Q_INVOKABLE QString formatTime(QTime time, Qt::DateFormat format) const;
Q_INVOKABLE QString formatTime(const QDateTime &dateTime, Qt::DateFormat format) const;
Q_INVOKABLE QString formatTime(const QString &time, Qt::DateFormat format) const;
@@ -105,13 +104,13 @@ public:
Q_INVOKABLE QString formatDateTime(const QString &string, Qt::DateFormat format) const;
#if QT_CONFIG(qml_locale)
- Q_INVOKABLE QString formatDate(const QDate &date, const QLocale &locale = QLocale(),
+ Q_INVOKABLE QString formatDate(QDate date, const QLocale &locale = QLocale(),
QLocale::FormatType formatType = QLocale::ShortFormat) const;
Q_INVOKABLE QString formatDate(const QDateTime &dateTime, const QLocale &locale = QLocale(),
QLocale::FormatType formatType = QLocale::ShortFormat) const;
Q_INVOKABLE QString formatDate(const QString &string, const QLocale &locale = QLocale(),
QLocale::FormatType formatType = QLocale::ShortFormat) const;
- Q_INVOKABLE QString formatTime(const QTime &time, const QLocale &locale = QLocale(),
+ Q_INVOKABLE QString formatTime(QTime time, const QLocale &locale = QLocale(),
QLocale::FormatType formatType = QLocale::ShortFormat) const;
Q_INVOKABLE QString formatTime(const QDateTime &dateTime, const QLocale &locale = QLocale(),
QLocale::FormatType formatType = QLocale::ShortFormat) const;
@@ -154,7 +153,7 @@ public:
QObject *parent = nullptr) const;
Q_INVOKABLE QJSValue binding(const QJSValue &function) const;
- Q_INVOKABLE void callLater(QQmlV4Function *args);
+ Q_INVOKABLE void callLater(QQmlV4FunctionPtr args);
#if QT_CONFIG(translation)
QString uiLanguage() const;
@@ -199,10 +198,10 @@ struct ConsoleObject : Object {
};
#define QQmlBindingFunctionMembers(class, Member) \
- Member(class, Pointer, FunctionObject *, bindingFunction)
-DECLARE_HEAP_OBJECT(QQmlBindingFunction, FunctionObject) {
+ Member(class, Pointer, JavaScriptFunctionObject *, bindingFunction)
+DECLARE_HEAP_OBJECT(QQmlBindingFunction, JavaScriptFunctionObject) {
DECLARE_MARKOBJECTS(QQmlBindingFunction)
- void init(const QV4::FunctionObject *bindingFunction);
+ void init(const QV4::JavaScriptFunctionObject *bindingFunction);
};
}
@@ -226,7 +225,7 @@ struct ConsoleObject : Object
};
-struct Q_QML_PRIVATE_EXPORT GlobalExtensions {
+struct Q_QML_EXPORT GlobalExtensions {
static void init(Object *globalObject, QJSEngine::Extensions extensions);
#if QT_CONFIG(translation)
@@ -245,11 +244,14 @@ struct Q_QML_PRIVATE_EXPORT GlobalExtensions {
};
-struct QQmlBindingFunction : public QV4::FunctionObject
+struct QQmlBindingFunction : public QV4::JavaScriptFunctionObject
{
- V4_OBJECT2(QQmlBindingFunction, FunctionObject)
+ V4_OBJECT2(QQmlBindingFunction, JavaScriptFunctionObject)
- Heap::FunctionObject *bindingFunction() const { return d()->bindingFunction; }
+ static ReturnedValue virtualCall(
+ const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+
+ Heap::JavaScriptFunctionObject *bindingFunction() const { return d()->bindingFunction; }
QQmlSourceLocation currentLocation() const; // from caller stack trace
};
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 4ff47588ed..e063418de4 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -29,6 +29,9 @@
#include <QtCore/qloggingcategory.h>
#include <qqmlinfo.h>
+
+using namespace Qt::Literals::StringLiterals;
+
namespace {
Q_CONSTINIT thread_local int creationDepth = 0;
}
@@ -247,7 +250,7 @@ V4_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension);
}
\endqml
- \sa {Qt QML}
+ \sa {Qt Qml}
*/
/*!
@@ -297,7 +300,8 @@ void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p)
void QQmlComponentPrivate::fromTypeData(const QQmlRefPointer<QQmlTypeData> &data)
{
url = data->finalUrl();
- compilationUnit.reset(data->compilationUnit());
+ if (auto cu = data->compilationUnit())
+ compilationUnit = engine->handle()->executableCompilationUnit(std::move(cu));
if (!compilationUnit) {
Q_ASSERT(data->isError());
@@ -320,7 +324,7 @@ void QQmlComponentPrivate::clear()
compilationUnit.reset();
loadedType = {};
- isInlineComponent = false;
+ inlineComponentName.reset();
}
QObject *QQmlComponentPrivate::doBeginCreate(QQmlComponent *q, QQmlContext *context)
@@ -335,6 +339,35 @@ QObject *QQmlComponentPrivate::doBeginCreate(QQmlComponent *q, QQmlContext *cont
return q->beginCreate(context);
}
+static void removePendingQPropertyBinding(
+ QV4::Value *object, const QString &propertyName, QQmlObjectCreator *creator)
+{
+ if (!creator)
+ return;
+
+ QV4::QObjectWrapper *wrapper = object->as<QV4::QObjectWrapper>();
+ if (!wrapper)
+ return;
+
+ QObject *o = wrapper->object();
+ if (!o)
+ return;
+
+ if (QQmlData *ddata = QQmlData::get(o)) {
+ const QQmlPropertyData *propData = ddata->propertyCache->property(
+ propertyName, o, ddata->outerContext);
+ if (propData && propData->isBindable())
+ creator->removePendingBinding(o, propData->coreIndex());
+ return;
+ }
+
+ const QMetaObject *meta = o->metaObject();
+ Q_ASSERT(meta);
+ const int index = meta->indexOfProperty(propertyName.toUtf8());
+ if (index != -1 && meta->property(index).isBindable())
+ creator->removePendingBinding(o, index);
+}
+
bool QQmlComponentPrivate::setInitialProperty(
QObject *base, const QString &name, const QVariant &value)
{
@@ -351,13 +384,16 @@ bool QQmlComponentPrivate::setInitialProperty(
if (scope.engine->hasException)
break;
}
- segment = scope.engine->newString(properties.last());
+ const QString lastProperty = properties.last();
+ segment = scope.engine->newString(lastProperty);
object->put(segment, scope.engine->metaTypeToJS(value.metaType(), value.constData()));
if (scope.engine->hasException) {
qmlWarning(base, scope.engine->catchExceptionAsQmlError());
scope.engine->hasException = false;
return false;
}
+
+ removePendingQPropertyBinding(object, lastProperty, state.creator());
return true;
}
@@ -369,7 +405,12 @@ bool QQmlComponentPrivate::setInitialProperty(
prop = QQmlProperty(base, name, engine);
QQmlPropertyPrivate *privProp = QQmlPropertyPrivate::get(prop);
const bool isValid = prop.isValid();
- if (!isValid || !privProp->writeValueProperty(value, {})) {
+ if (isValid && privProp->writeValueProperty(value, {})) {
+ if (prop.isBindable()) {
+ if (QQmlObjectCreator *creator = state.creator())
+ creator->removePendingBinding(prop.object(), prop.index());
+ }
+ } else {
QQmlError error{};
error.setUrl(url);
if (isValid) {
@@ -428,13 +469,11 @@ QQmlComponent::~QQmlComponent()
This property holds the status of component loading. The status can be one of the
following:
- \list
- \li \c{Component.Null} - no data is available for the component
- \li \c{Component.Ready} - the component has been loaded, and can be used to create instances.
- \li \c{Component.Loading} - the component is currently being loaded
- \li \c{Component.Error} - an error occurred while loading the component.
+ \value Component.Null no data is available for the component
+ \value Component.Ready the component has been loaded, and can be used to create instances.
+ \value Component.Loading the component is currently being loaded
+ \value Component.Error an error occurred while loading the component.
Calling \l errorString() will provide a human-readable description of any errors.
- \endlist
*/
/*!
@@ -960,6 +999,15 @@ QObject *QQmlComponentPrivate::createWithProperties(QObject *parent, const QVari
The ownership of the returned object instance is transferred to the caller.
+ \note The categorization of bindings into constant values and actual
+ bindings is intentionally unspecified and may change between versions of Qt
+ and depending on whether and how you are using \l{qmlcachegen}. You should
+ not rely on any particular binding to be evaluated either before or after
+ beginCreate() returns. For example a constant expression like
+ \e{MyType.EnumValue} may be recognized as such at compile time or deferred
+ to be executed as binding. The same holds for constant expressions like
+ \e{-(5)} or \e{"a" + " constant string"}.
+
\sa completeCreate(), QQmlEngine::ObjectOwnership
*/
QObject *QQmlComponent::beginCreate(QQmlContext *context)
@@ -1001,11 +1049,7 @@ QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> conte
// filter out temporary errors as they do not really affect component's
// state (they are not part of the document compilation)
- state.errors.erase(std::remove_if(state.errors.begin(), state.errors.end(),
- [](const QQmlComponentPrivate::AnnotatedQmlError &e) {
- return e.isTransient;
- }),
- state.errors.end());
+ state.errors.removeIf([](const auto &e) { return e.isTransient; });
state.clearRequiredProperties();
if (!q->isReady()) {
@@ -1031,13 +1075,31 @@ QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> conte
if (!loadedType.isValid()) {
enginePriv->referenceScarceResources();
state.initCreator(std::move(context), compilationUnit, creationContext);
- rv = state.creator()->create(start, nullptr, nullptr, isInlineComponent ? QQmlObjectCreator::InlineComponent : QQmlObjectCreator::NormalObject);
+
+ QQmlObjectCreator::CreationFlags flags;
+ if (const QString *icName = inlineComponentName.get()) {
+ flags = QQmlObjectCreator::InlineComponent;
+ if (start == -1)
+ start = compilationUnit->inlineComponentId(*icName);
+ Q_ASSERT(start > 0);
+ } else {
+ flags = QQmlObjectCreator::NormalObject;
+ }
+
+ rv = state.creator()->create(start, nullptr, nullptr, flags);
if (!rv)
state.appendCreatorErrors();
enginePriv->dereferenceScarceResources();
} else {
+ // TODO: extract into function
rv = loadedType.createWithQQmlData();
QQmlPropertyCache::ConstPtr propertyCache = QQmlData::ensurePropertyCache(rv);
+ QQmlParserStatus *parserStatus = nullptr;
+ const int parserStatusCast = loadedType.parserStatusCast();
+ if (parserStatusCast != -1) {
+ parserStatus = reinterpret_cast<QQmlParserStatus*>(reinterpret_cast<char *>(rv) + parserStatusCast);
+ parserStatus->classBegin();
+ }
for (int i = 0, propertyCount = propertyCache->propertyCount(); i < propertyCount; ++i) {
if (const QQmlPropertyData *propertyData = propertyCache->property(i); propertyData->isRequired()) {
state.ensureRequiredPropertyStorage();
@@ -1046,6 +1108,12 @@ QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> conte
state.addPendingRequiredProperty(rv, propertyData, info);
}
}
+ if (parserStatus)
+ parserStatus->componentComplete();
+ if (const int finalizerCast = loadedType.finalizerCast(); finalizerCast != -1) {
+ auto* hook = reinterpret_cast<QQmlFinalizerHook *>(reinterpret_cast<char *>(rv) + finalizerCast);
+ hook->componentFinalized();
+ }
}
if (rv) {
@@ -1154,8 +1222,8 @@ QQmlProperty QQmlComponentPrivate::removePropertyFromRequired(
Q_ASSERT(data && data->propertyCache);
targetProp = data->propertyCache->property(targetProp->coreIndex());
}
- auto it = requiredProperties->find({createdComponent, targetProp});
- if (it != requiredProperties->end()) {
+ auto it = requiredProperties->constFind({createdComponent, targetProp});
+ if (it != requiredProperties->cend()) {
if (wasInRequiredProperties)
*wasInRequiredProperties = true;
requiredProperties->erase(it);
@@ -1266,56 +1334,90 @@ void QQmlComponent::loadFromModule(QAnyStringView uri, QAnyStringView typeName,
QQmlComponent::CompilationMode mode)
{
Q_D(QQmlComponent);
+ auto [status, type] = d->prepareLoadFromModule(uri, typeName);
+ d->completeLoadFromModule(uri, typeName, type, status, mode);
+}
- auto enginePriv = QQmlEnginePrivate::get(d->engine);
+LoadHelper::ResolveTypeResult QQmlComponentPrivate::prepareLoadFromModule(QAnyStringView uri,
+ QAnyStringView typeName)
+{
+ auto enginePriv = QQmlEnginePrivate::get(engine);
// LoadHelper must be on the Heap as it derives from QQmlRefCount
auto loadHelper = QQml::makeRefPointer<LoadHelper>(&enginePriv->typeLoader, uri);
- auto [moduleStatus, type] = loadHelper->resolveType(typeName);
+ return loadHelper->resolveType(typeName);
+}
+
+void QQmlComponentPrivate::completeLoadFromModule(QAnyStringView uri, QAnyStringView typeName, QQmlType type,
+ LoadHelper::ResolveTypeResult::Status moduleStatus,
+ QQmlComponent::CompilationMode mode)
+{
+ Q_Q(QQmlComponent);
+
+ // we always mimic the progressChanged behavior from loadUrl
auto reportError = [&](QString msg) {
QQmlError error;
error.setDescription(msg);
- d->state.errors.push_back(std::move(error));
- emit statusChanged(Error);
+ state.errors.push_back(std::move(error));
+ progress = 1;
+ emit q->progressChanged(1);
+ emit q->statusChanged(q->Error);
+ };
+ auto emitProgressReset = [&](){
+ if (progress != 0) {
+ progress = 0;
+ emit q->progressChanged(0);
+ }
};
+ auto emitComplete = [&]() {
+ progress = 1;
+ emit q->progressChanged(1);
+ emit q->statusChanged(q->status());
+ };
+ emitProgressReset();
if (moduleStatus == LoadHelper::ResolveTypeResult::NoSuchModule) {
- reportError(QLatin1String(R"(No module named "%1" found)")
- .arg(uri.toString()));
+ reportError(QLatin1String(R"(No module named "%1" found)").arg(uri.toString()));
} else if (!type.isValid()) {
reportError(QLatin1String(R"(Module "%1" contains no type named "%2")")
.arg(uri.toString(), typeName.toString()));
} else if (type.isCreatable()) {
- d->clear();
- // mimic the progressChanged behavior from loadUrl
- if (d->progress != 0) {
- d->progress = 0;
- emit progressChanged(0);
- }
- d->loadedType = type;
- d->progress = 1;
- emit progressChanged(1);
- emit statusChanged(status());
-
+ clear();
+ loadedType = type;
+ emitComplete();
} else if (type.isComposite()) {
+ // loadUrl takes care of signal emission
loadUrl(type.sourceUrl(), mode);
} else if (type.isInlineComponentType()) {
auto baseUrl = type.sourceUrl();
baseUrl.setFragment(QString());
- // if the outer type has not been resolved yet, we need to load the outer type synchronously
- // in order to get the correct object id
- mode = type.inlineComponentObjectId() > 0 ? mode : QQmlComponent::CompilationMode::PreferSynchronous;
- loadUrl(baseUrl, mode);
- if (!isError()) {
- d->isInlineComponent = true;
- d->start = type.inlineComponentObjectId();
- Q_ASSERT(d->start >= 0);
+ {
+ // we don't want to emit status changes from the "helper" loadUrl below
+ // because it would signal success to early
+ QSignalBlocker blockSignals(q);
+ // we really need to continue in a synchronous way, otherwise we can't check the CU
+ loadUrl(baseUrl, QQmlComponent::PreferSynchronous);
+ }
+ if (q->isError()) {
+ emitComplete();
+ return;
+ }
+ QString elementName = type.elementName();
+ if (compilationUnit->inlineComponentId(elementName) == -1) {
+ QString realTypeName = typeName.toString();
+ realTypeName.truncate(realTypeName.indexOf(u'.'));
+ QString errorMessage = R"(Type "%1" from module "%2" contains no inline component named "%3".)"_L1.arg(
+ realTypeName, uri.toString(), elementName);
+ if (elementName == u"qml")
+ errorMessage += " To load the type \"%1\", drop the \".qml\" extension."_L1.arg(realTypeName);
+ reportError(std::move(errorMessage));
+ } else {
+ inlineComponentName = std::make_unique<QString>(std::move(elementName));
+ emitComplete();
}
} else if (type.isSingleton() || type.isCompositeSingleton()) {
- reportError(QLatin1String(R"(%1 is a singleton, and cannot be loaded)")
- .arg(typeName.toString()));
+ reportError(QLatin1String(R"(%1 is a singleton, and cannot be loaded)").arg(typeName.toString()));
} else {
- reportError(QLatin1String("Could not load %1, as the type is uncreatable")
- .arg(typeName.toString()));
+ reportError(QLatin1String("Could not load %1, as the type is uncreatable").arg(typeName.toString()));
}
}
@@ -1426,6 +1528,13 @@ void QQmlComponentPrivate::incubateObject(
incubatorPriv->compilationUnit = componentPriv->compilationUnit;
incubatorPriv->enginePriv = enginePriv;
incubatorPriv->creator.reset(new QQmlObjectCreator(context, componentPriv->compilationUnit, componentPriv->creationContext));
+
+ if (start == -1) {
+ if (const QString *icName = componentPriv->inlineComponentName.get()) {
+ start = compilationUnit->inlineComponentId(*icName);
+ Q_ASSERT(start > 0);
+ }
+ }
incubatorPriv->subComponentToCreate = componentPriv->start;
enginePriv->incubate(*incubationTask, forContext);
@@ -1440,7 +1549,7 @@ namespace QV4 {
namespace Heap {
#define QmlIncubatorObjectMembers(class, Member) \
- Member(class, HeapValue, HeapValue, valuemap) \
+ Member(class, HeapValue, HeapValue, valuemapOrObject) \
Member(class, HeapValue, HeapValue, statusChanged) \
Member(class, Pointer, QmlContext *, qmlContext) \
Member(class, NoMark, QQmlComponentIncubator *, incubator) \
@@ -1565,7 +1674,10 @@ static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
*/
-void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v, RequiredProperties *requiredProperties, QObject *createdComponent)
+void QQmlComponentPrivate::setInitialProperties(
+ QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o,
+ const QV4::Value &v, RequiredProperties *requiredProperties, QObject *createdComponent,
+ QQmlObjectCreator *creator)
{
QV4::Scope scope(engine);
QV4::ScopedObject object(scope);
@@ -1605,7 +1717,8 @@ void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV
qmlWarning(createdComponent, error);
continue;
}
- name = engine->newString(properties.last());
+ const QString lastProperty = properties.last();
+ name = engine->newString(lastProperty);
object->put(name, val);
if (engine->hasException) {
qmlWarning(createdComponent, engine->catchExceptionAsQmlError());
@@ -1614,6 +1727,8 @@ void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV
auto prop = removePropertyFromRequired(createdComponent, name->toQString(),
requiredProperties, engine->qmlEngine());
}
+
+ removePendingQPropertyBinding(object, lastProperty, creator);
}
engine->hasException = false;
@@ -1651,7 +1766,7 @@ QQmlError QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(const RequiredP
/*!
\internal
*/
-void QQmlComponent::createObject(QQmlV4Function *args)
+void QQmlComponent::createObject(QQmlV4FunctionPtr args)
{
Q_D(QQmlComponent);
Q_ASSERT(d->engine);
@@ -1699,8 +1814,9 @@ void QQmlComponent::createObject(QQmlV4Function *args)
if (!valuemap->isUndefined()) {
QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->qmlContext());
- QQmlComponentPrivate::setInitialProperties(v4, qmlContext, object, valuemap,
- d->state.requiredProperties(), rv);
+ QQmlComponentPrivate::setInitialProperties(
+ v4, qmlContext, object, valuemap, d->state.requiredProperties(), rv,
+ d->state.creator());
}
if (d->state.hasUnsetRequiredProperties()) {
QList<QQmlError> errors;
@@ -1799,7 +1915,7 @@ QObject *QQmlComponent::createObject(QObject *parent, const QVariantMap &propert
/*!
\internal
*/
-void QQmlComponent::incubateObject(QQmlV4Function *args)
+void QQmlComponent::incubateObject(QQmlV4FunctionPtr args)
{
Q_D(QQmlComponent);
Q_ASSERT(d->engine);
@@ -1846,7 +1962,7 @@ void QQmlComponent::incubateObject(QQmlV4Function *args)
r->setPrototypeOf(p);
if (!valuemap->isUndefined())
- r->d()->valuemap.set(scope.engine, valuemap);
+ r->d()->valuemapOrObject.set(scope.engine, valuemap);
r->d()->qmlContext.set(scope.engine, v4->qmlContext());
r->d()->parent = parent;
@@ -1869,8 +1985,10 @@ void QQmlComponentPrivate::initializeObjectWithInitialProperties(QV4::QmlContext
QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(v4engine, toCreate));
Q_ASSERT(object->as<QV4::Object>());
- if (!valuemap.isUndefined())
- setInitialProperties(v4engine, qmlContext, object, valuemap, requiredProperties, toCreate);
+ if (!valuemap.isUndefined()) {
+ setInitialProperties(
+ v4engine, qmlContext, object, valuemap, requiredProperties, toCreate, state.creator());
+ }
}
QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4)
@@ -1947,7 +2065,7 @@ QQmlComponentExtension::~QQmlComponentExtension()
void QV4::Heap::QmlIncubatorObject::init(QQmlIncubator::IncubationMode m)
{
Object::init();
- valuemap.set(internalClass->engine, QV4::Value::undefinedValue());
+ valuemapOrObject.set(internalClass->engine, QV4::Value::undefinedValue());
statusChanged.set(internalClass->engine, QV4::Value::undefinedValue());
parent.init();
qmlContext.set(internalClass->engine, nullptr);
@@ -1964,25 +2082,32 @@ void QV4::QmlIncubatorObject::setInitialState(QObject *o, RequiredProperties *re
{
QQmlComponent_setQmlParent(o, d()->parent);
- if (!d()->valuemap.isUndefined()) {
+ if (!d()->valuemapOrObject.isUndefined()) {
QV4::ExecutionEngine *v4 = engine();
QV4::Scope scope(v4);
QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(v4, o));
QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, d()->qmlContext);
- QQmlComponentPrivate::setInitialProperties(v4, qmlCtxt, obj, d()->valuemap, requiredProperties, o);
+ QQmlComponentPrivate::setInitialProperties(
+ v4, qmlCtxt, obj, d()->valuemapOrObject, requiredProperties, o,
+ QQmlIncubatorPrivate::get(d()->incubator)->creator.data());
}
}
void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s)
{
QV4::Scope scope(engine());
- // hold the incubated object in a scoped value to prevent it's destruction before this method returns
- QV4::ScopedObject incubatedObject(scope, QV4::QObjectWrapper::wrap(scope.engine, d()->incubator->object()));
+
+ QObject *object = d()->incubator->object();
if (s == QQmlIncubator::Ready) {
- Q_ASSERT(QQmlData::get(d()->incubator->object()));
- QQmlData::get(d()->incubator->object())->explicitIndestructibleSet = false;
- QQmlData::get(d()->incubator->object())->indestructible = false;
+ // We don't need the arguments anymore, but we still want to hold on to the object so
+ // that it doesn't get gc'd
+ d()->valuemapOrObject.set(scope.engine, QV4::QObjectWrapper::wrap(scope.engine, object));
+
+ QQmlData *ddata = QQmlData::get(object);
+ Q_ASSERT(ddata);
+ ddata->explicitIndestructibleSet = false;
+ ddata->indestructible = false;
}
QV4::ScopedFunctionObject f(scope, d()->statusChanged);
diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h
index 0c7f7ce1ff..2d68c47c11 100644
--- a/src/qml/qml/qqmlcomponent.h
+++ b/src/qml/qml/qqmlcomponent.h
@@ -21,7 +21,6 @@ class QByteArray;
class QQmlEngine;
class QQmlComponent;
class QQmlIncubator;
-class QQmlV4Function;
class QQmlComponentPrivate;
class QQmlComponentAttached;
@@ -37,10 +36,6 @@ class Q_QML_EXPORT QQmlComponent : public QObject
Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged)
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
Q_PROPERTY(QUrl url READ url CONSTANT)
- QML_NAMED_ELEMENT(Component)
- QML_ADDED_IN_VERSION(2, 0)
- QML_ATTACHED(QQmlComponentAttached)
- Q_CLASSINFO("QML.OmitFromQmlTypes", "true")
public:
enum CompilationMode { PreferSynchronous, Asynchronous };
@@ -106,12 +101,12 @@ protected:
#if QT_DEPRECATED_SINCE(6, 3)
QT_DEPRECATED_X("Use the overload with proper arguments")
- Q_INVOKABLE void createObject(QQmlV4Function *);
+ Q_INVOKABLE void createObject(QQmlV4FunctionPtr);
#endif
Q_INVOKABLE QObject *createObject(
QObject *parent = nullptr, const QVariantMap &properties = {});
- Q_INVOKABLE void incubateObject(QQmlV4Function *);
+ Q_INVOKABLE void incubateObject(QQmlV4FunctionPtr);
private:
QQmlComponent(QQmlEngine *, QV4::ExecutableCompilationUnit *compilationUnit, int,
@@ -143,8 +138,6 @@ struct OverridableAttachedType<QQmlComponent, QQmlComponentAttached>
} // namespace QQmlPrivate
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQmlComponent)
#endif // QQMLCOMPONENT_H
diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h
index d76e1c24a3..21fdee3f7a 100644
--- a/src/qml/qml/qqmlcomponent_p.h
+++ b/src/qml/qml/qqmlcomponent_p.h
@@ -36,7 +36,7 @@ class QQmlComponent;
class QQmlEngine;
class QQmlComponentAttached;
-class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public QQmlTypeData::TypeDataCallback
+class Q_QML_EXPORT QQmlComponentPrivate : public QObjectPrivate, public QQmlTypeData::TypeDataCallback
{
Q_DECLARE_PUBLIC(QQmlComponent)
@@ -49,7 +49,10 @@ public:
QObject *beginCreate(QQmlRefPointer<QQmlContextData>);
void completeCreate();
void initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate, RequiredProperties *requiredProperties);
- static void setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v, RequiredProperties *requiredProperties, QObject *createdComponent);
+ static void setInitialProperties(
+ QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o,
+ const QV4::Value &v, RequiredProperties *requiredProperties, QObject *createdComponent,
+ QQmlObjectCreator *creator);
static QQmlError unsetRequiredPropertyToQQmlError(const RequiredPropertyInfo &unsetRequiredProperty);
virtual void incubateObject(
@@ -67,11 +70,12 @@ public:
QUrl url;
qreal progress;
+ std::unique_ptr<QString> inlineComponentName;
/* points to the sub-object in a QML file that should be instantiated
- used for inline components and to create instances of QtQml's Component type */
+ used create instances of QtQml's Component type and indirectly for inline components */
int start;
- bool isInlineComponent = false;
+
bool hadTopLevelRequiredProperties() const;
// TODO: merge compilation unit and type
QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
@@ -163,9 +167,12 @@ public:
QObject *createWithProperties(QObject *parent, const QVariantMap &properties,
QQmlContext *context, CreateBehavior behavior = CreateDefault);
- bool isBound() const {
- return compilationUnit->unitData()->flags & QV4::CompiledData::Unit::ComponentsBound;
- }
+ bool isBound() const { return compilationUnit && (compilationUnit->componentsAreBound()); }
+ LoadHelper::ResolveTypeResult prepareLoadFromModule(QAnyStringView uri,
+ QAnyStringView typeName);
+ void completeLoadFromModule(QAnyStringView uri, QAnyStringView typeName, QQmlType type,
+ LoadHelper::ResolveTypeResult::Status moduleStatus,
+ QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous);
};
QQmlComponentPrivate::ConstructionState::~ConstructionState()
diff --git a/src/qml/qml/qqmlcomponentandaliasresolver_p.h b/src/qml/qml/qqmlcomponentandaliasresolver_p.h
new file mode 100644
index 0000000000..439c7d62d4
--- /dev/null
+++ b/src/qml/qml/qqmlcomponentandaliasresolver_p.h
@@ -0,0 +1,484 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QQMLCOMPONENTANDALIASRESOLVER_P_H
+#define QQMLCOMPONENTANDALIASRESOLVER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlerror.h>
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qhash.h>
+
+#include <private/qqmltypeloader_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcQmlTypeCompiler);
+
+// This class primarily resolves component boundaries in a document.
+// With the information about boundaries, it then goes on to resolve aliases and generalized
+// group properties. Both rely on IDs as first part of their expressions and the IDs have
+// to be located in surrounding components. That's why we have to do this with the component
+// boundaries in mind.
+
+class QQmlComponentAndAliasResolverBase
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlComponentAndAliasResolverBase)
+};
+
+template<typename ObjectContainer>
+class QQmlComponentAndAliasResolver : public QQmlComponentAndAliasResolverBase
+{
+public:
+ using CompiledObject = typename ObjectContainer::CompiledObject;
+ using CompiledBinding = typename ObjectContainer::CompiledBinding;
+
+ QQmlComponentAndAliasResolver(
+ ObjectContainer *compiler,
+ QQmlEnginePrivate *enginePrivate,
+ QQmlPropertyCacheVector *propertyCaches);
+
+ [[nodiscard]] QQmlError resolve(int root = 0);
+
+private:
+ enum AliasResolutionResult {
+ NoAliasResolved,
+ SomeAliasesResolved,
+ AllAliasesResolved
+ };
+
+ // To be specialized for each container
+ void allocateNamedObjects(CompiledObject *object) const;
+ void setObjectId(int index) const;
+ [[nodiscard]] bool markAsComponent(int index) const;
+ [[nodiscard]] AliasResolutionResult resolveAliasesInObject(
+ const CompiledObject &component, int objectIndex, QQmlError *error);
+ void resolveGeneralizedGroupProperty(const CompiledObject &component, CompiledBinding *binding);
+ [[nodiscard]] bool wrapImplicitComponent(CompiledBinding *binding);
+
+ [[nodiscard]] QQmlError findAndRegisterImplicitComponents(
+ const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &propertyCache);
+ [[nodiscard]] QQmlError collectIdsAndAliases(int objectIndex);
+ [[nodiscard]] QQmlError resolveAliases(int componentIndex);
+ void resolveGeneralizedGroupProperties(int componentIndex);
+ [[nodiscard]] QQmlError resolveComponentsInInlineComponentRoot(int root);
+
+ QString stringAt(int idx) const { return m_compiler->stringAt(idx); }
+ QV4::ResolvedTypeReference *resolvedType(int id) const { return m_compiler->resolvedType(id); }
+
+ [[nodiscard]] QQmlError error(
+ const QV4::CompiledData::Location &location,
+ const QString &description)
+ {
+ QQmlError error;
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
+ error.setDescription(description);
+ error.setUrl(m_compiler->url());
+ return error;
+ }
+
+ template<typename Token>
+ [[nodiscard]] QQmlError error(Token token, const QString &description)
+ {
+ return error(token->location, description);
+ }
+
+ static bool isUsableComponent(const QMetaObject *metaObject)
+ {
+ // The metaObject is a component we're interested in if it either is a QQmlComponent itself
+ // or if any of its parents is a QQmlAbstractDelegateComponent. We don't want to include
+ // qqmldelegatecomponent_p.h because it belongs to QtQmlModels.
+
+ if (metaObject == &QQmlComponent::staticMetaObject)
+ return true;
+
+ for (; metaObject; metaObject = metaObject->superClass()) {
+ if (qstrcmp(metaObject->className(), "QQmlAbstractDelegateComponent") == 0)
+ return true;
+ }
+
+ return false;
+ }
+
+ ObjectContainer *m_compiler = nullptr;
+ QQmlEnginePrivate *m_enginePrivate = nullptr;
+
+ // Implicit component insertion may have added objects and thus we also need
+ // to extend the symmetric propertyCaches. Therefore, non-const propertyCaches.
+ QQmlPropertyCacheVector *m_propertyCaches = nullptr;
+
+ // indices of the objects that are actually Component {}
+ QVector<quint32> m_componentRoots;
+ QVector<int> m_objectsWithAliases;
+ QVector<CompiledBinding *> m_generalizedGroupProperties;
+ typename ObjectContainer::IdToObjectMap m_idToObjectIndex;
+};
+
+template<typename ObjectContainer>
+QQmlComponentAndAliasResolver<ObjectContainer>::QQmlComponentAndAliasResolver(
+ ObjectContainer *compiler,
+ QQmlEnginePrivate *enginePrivate,
+ QQmlPropertyCacheVector *propertyCaches)
+ : m_compiler(compiler)
+ , m_enginePrivate(enginePrivate)
+ , m_propertyCaches(propertyCaches)
+{
+}
+
+template<typename ObjectContainer>
+QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::findAndRegisterImplicitComponents(
+ const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &propertyCache)
+{
+ QQmlPropertyResolver propertyResolver(propertyCache);
+
+ const QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1
+ ? propertyCache->parent()->defaultProperty()
+ : propertyCache->defaultProperty();
+
+ for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) {
+ if (binding->type() != QV4::CompiledData::Binding::Type_Object)
+ continue;
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerObject))
+ continue;
+
+ auto targetObject = m_compiler->objectAt(binding->value.objectIndex);
+ auto typeReference = resolvedType(targetObject->inheritedTypeNameIndex);
+ Q_ASSERT(typeReference);
+
+ const QMetaObject *firstMetaObject = nullptr;
+ const auto type = typeReference->type();
+ if (type.isValid())
+ firstMetaObject = type.metaObject();
+ else if (const auto compilationUnit = typeReference->compilationUnit())
+ firstMetaObject = compilationUnit->rootPropertyCache()->firstCppMetaObject();
+ if (isUsableComponent(firstMetaObject))
+ continue;
+
+ // if here, not a QQmlComponent, so needs wrapping
+ const QQmlPropertyData *pd = nullptr;
+ if (binding->propertyNameIndex != quint32(0)) {
+ bool notInRevision = false;
+ pd = propertyResolver.property(stringAt(binding->propertyNameIndex), &notInRevision);
+ } else {
+ pd = defaultProperty;
+ }
+ if (!pd || !pd->isQObject())
+ continue;
+
+ // If the version is given, use it and look up by QQmlType.
+ // Otherwise, make sure we look up by metaobject.
+ // TODO: Is this correct?
+ QQmlPropertyCache::ConstPtr pc = pd->typeVersion().hasMinorVersion()
+ ? QQmlMetaType::rawPropertyCacheForType(pd->propType(), pd->typeVersion())
+ : QQmlMetaType::rawPropertyCacheForType(pd->propType());
+ const QMetaObject *mo = pc ? pc->firstCppMetaObject() : nullptr;
+ while (mo) {
+ if (mo == &QQmlComponent::staticMetaObject)
+ break;
+ mo = mo->superClass();
+ }
+
+ if (!mo)
+ continue;
+
+ if (!wrapImplicitComponent(binding))
+ return error(binding, QQmlComponentAndAliasResolverBase::tr("Cannot wrap implicit component"));
+ }
+
+ return QQmlError();
+}
+
+template<typename ObjectContainer>
+QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::resolveComponentsInInlineComponentRoot(
+ int root)
+{
+ // Find implicit components in the inline component itself. Also warn about inline
+ // components being explicit components.
+
+ const auto rootObj = m_compiler->objectAt(root);
+ Q_ASSERT(rootObj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot));
+
+ if (const int typeName = rootObj->inheritedTypeNameIndex) {
+ const auto *tref = resolvedType(typeName);
+ Q_ASSERT(tref);
+ if (tref->type().metaObject() == &QQmlComponent::staticMetaObject) {
+ qCWarning(lcQmlTypeCompiler).nospace().noquote()
+ << m_compiler->url().toString() << ":" << rootObj->location.line() << ":"
+ << rootObj->location.column()
+ << ": Using a Component as the root of an inline component is deprecated: "
+ "inline components are "
+ "automatically wrapped into Components when needed.";
+ return QQmlError();
+ }
+ }
+
+ const QQmlPropertyCache::ConstPtr rootCache = m_propertyCaches->at(root);
+ Q_ASSERT(rootCache);
+
+ return findAndRegisterImplicitComponents(rootObj, rootCache);
+}
+
+// Resolve ignores everything relating to inline components, except for implicit components.
+template<typename ObjectContainer>
+QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::resolve(int root)
+{
+ // Detect real Component {} objects as well as implicitly defined components, such as
+ // someItemDelegate: Item {}
+ // In the implicit case Item is surrounded by a synthetic Component {} because the property
+ // on the left hand side is of QQmlComponent type.
+ const int objCountWithoutSynthesizedComponents = m_compiler->objectCount();
+
+ if (root != 0) {
+ const QQmlError error = resolveComponentsInInlineComponentRoot(root);
+ if (error.isValid())
+ return error;
+ }
+
+ // root+1, as ic root is handled at the end
+ const int startObjectIndex = root == 0 ? root : root+1;
+
+ for (int i = startObjectIndex; i < objCountWithoutSynthesizedComponents; ++i) {
+ auto obj = m_compiler->objectAt(i);
+ const bool isInlineComponentRoot
+ = obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot);
+ const bool isPartOfInlineComponent
+ = obj->hasFlag(QV4::CompiledData::Object::IsPartOfInlineComponent);
+ QQmlPropertyCache::ConstPtr cache = m_propertyCaches->at(i);
+
+ if (root == 0) {
+ // normal component root, skip over anything inline component related
+ if (isInlineComponentRoot || isPartOfInlineComponent)
+ continue;
+ } else if (!isPartOfInlineComponent || isInlineComponentRoot) {
+ // When handling an inline component, stop where the inline component ends
+ // Note: We do not support nested inline components. Therefore, isInlineComponentRoot
+ // tells us that the element after the current inline component is again an
+ // inline component
+ break;
+ }
+
+ if (obj->inheritedTypeNameIndex == 0 && !cache)
+ continue;
+
+ bool isExplicitComponent = false;
+ if (obj->inheritedTypeNameIndex) {
+ auto *tref = resolvedType(obj->inheritedTypeNameIndex);
+ Q_ASSERT(tref);
+ if (tref->type().metaObject() == &QQmlComponent::staticMetaObject)
+ isExplicitComponent = true;
+ }
+
+ if (!isExplicitComponent) {
+ if (cache) {
+ const QQmlError error = findAndRegisterImplicitComponents(obj, cache);
+ if (error.isValid())
+ return error;
+ }
+ continue;
+ }
+
+ if (!markAsComponent(i))
+ return error(obj, QQmlComponentAndAliasResolverBase::tr("Cannot mark object as component"));
+
+ // check if this object is the root
+ if (i == 0) {
+ if (isExplicitComponent)
+ qCWarning(lcQmlTypeCompiler).nospace().noquote()
+ << m_compiler->url().toString() << ":" << obj->location.line() << ":"
+ << obj->location.column()
+ << ": Using a Component as the root of a QML document is deprecated: types "
+ "defined in qml documents are "
+ "automatically wrapped into Components when needed.";
+ }
+
+ if (obj->functionCount() > 0)
+ return error(obj, QQmlComponentAndAliasResolverBase::tr("Component objects cannot declare new functions."));
+ if (obj->propertyCount() > 0 || obj->aliasCount() > 0)
+ return error(obj, QQmlComponentAndAliasResolverBase::tr("Component objects cannot declare new properties."));
+ if (obj->signalCount() > 0)
+ return error(obj, QQmlComponentAndAliasResolverBase::tr("Component objects cannot declare new signals."));
+
+ if (obj->bindingCount() == 0)
+ return error(obj, QQmlComponentAndAliasResolverBase::tr("Cannot create empty component specification"));
+
+ const auto rootBinding = obj->bindingsBegin();
+ const auto bindingsEnd = obj->bindingsEnd();
+
+ // Produce the more specific "no properties" error rather than the "invalid body" error
+ // where possible.
+ for (auto b = rootBinding; b != bindingsEnd; ++b) {
+ if (b->propertyNameIndex == 0)
+ continue;
+
+ return error(b, QQmlComponentAndAliasResolverBase::tr("Component elements may not contain properties other than id"));
+ }
+
+ if (auto b = rootBinding;
+ b->type() != QV4::CompiledData::Binding::Type_Object || ++b != bindingsEnd) {
+ return error(obj, QQmlComponentAndAliasResolverBase::tr("Invalid component body specification"));
+ }
+
+ // For the root object, we are going to collect ids/aliases and resolve them for as a
+ // separate last pass.
+ if (i != 0)
+ m_componentRoots.append(i);
+ }
+
+ for (int i = 0; i < m_componentRoots.size(); ++i) {
+ CompiledObject *component = m_compiler->objectAt(m_componentRoots.at(i));
+ const auto rootBinding = component->bindingsBegin();
+
+ m_idToObjectIndex.clear();
+ m_objectsWithAliases.clear();
+ m_generalizedGroupProperties.clear();
+
+ if (const QQmlError error = collectIdsAndAliases(rootBinding->value.objectIndex);
+ error.isValid()) {
+ return error;
+ }
+
+ allocateNamedObjects(component);
+
+ if (const QQmlError error = resolveAliases(m_componentRoots.at(i)); error.isValid())
+ return error;
+
+ resolveGeneralizedGroupProperties(m_componentRoots.at(i));
+ }
+
+ // Collect ids and aliases for root
+ m_idToObjectIndex.clear();
+ m_objectsWithAliases.clear();
+ m_generalizedGroupProperties.clear();
+
+ if (const QQmlError error = collectIdsAndAliases(root); error.isValid())
+ return error;
+
+ allocateNamedObjects(m_compiler->objectAt(root));
+ if (const QQmlError error = resolveAliases(root); error.isValid())
+ return error;
+
+ resolveGeneralizedGroupProperties(root);
+ return QQmlError();
+}
+
+template<typename ObjectContainer>
+QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::collectIdsAndAliases(int objectIndex)
+{
+ auto obj = m_compiler->objectAt(objectIndex);
+
+ if (obj->idNameIndex != 0) {
+ if (m_idToObjectIndex.contains(obj->idNameIndex))
+ return error(obj->locationOfIdProperty, QQmlComponentAndAliasResolverBase::tr("id is not unique"));
+ setObjectId(objectIndex);
+ m_idToObjectIndex.insert(obj->idNameIndex, objectIndex);
+ }
+
+ if (obj->aliasCount() > 0)
+ m_objectsWithAliases.append(objectIndex);
+
+ // Stop at Component boundary
+ if (obj->hasFlag(QV4::CompiledData::Object::IsComponent) && objectIndex != /*root object*/0)
+ return QQmlError();
+
+ for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd();
+ binding != end; ++binding) {
+ switch (binding->type()) {
+ case QV4::CompiledData::Binding::Type_GroupProperty: {
+ const auto *inner = m_compiler->objectAt(binding->value.objectIndex);
+ if (m_compiler->stringAt(inner->inheritedTypeNameIndex).isEmpty()) {
+ const auto cache = m_propertyCaches->at(objectIndex);
+ if (!cache || !cache->property(
+ m_compiler->stringAt(binding->propertyNameIndex), nullptr, nullptr)) {
+ m_generalizedGroupProperties.append(binding);
+ }
+ }
+ }
+ Q_FALLTHROUGH();
+ case QV4::CompiledData::Binding::Type_Object:
+ case QV4::CompiledData::Binding::Type_AttachedProperty:
+ if (const QQmlError error = collectIdsAndAliases(binding->value.objectIndex);
+ error.isValid()) {
+ return error;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return QQmlError();
+}
+
+template<typename ObjectContainer>
+QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::resolveAliases(int componentIndex)
+{
+ if (m_objectsWithAliases.isEmpty())
+ return QQmlError();
+
+ QQmlPropertyCacheAliasCreator<ObjectContainer> aliasCacheCreator(m_propertyCaches, m_compiler);
+
+ bool atLeastOneAliasResolved;
+ do {
+ atLeastOneAliasResolved = false;
+ QVector<int> pendingObjects;
+
+ for (int objectIndex: std::as_const(m_objectsWithAliases)) {
+
+ QQmlError error;
+ const auto &component = *m_compiler->objectAt(componentIndex);
+ const auto result = resolveAliasesInObject(component, objectIndex, &error);
+ if (error.isValid())
+ return error;
+
+ if (result == AllAliasesResolved) {
+ QQmlError error = aliasCacheCreator.appendAliasesToPropertyCache(
+ component, objectIndex, m_enginePrivate);
+ if (error.isValid())
+ return error;
+ atLeastOneAliasResolved = true;
+ } else if (result == SomeAliasesResolved) {
+ atLeastOneAliasResolved = true;
+ pendingObjects.append(objectIndex);
+ } else {
+ pendingObjects.append(objectIndex);
+ }
+ }
+ qSwap(m_objectsWithAliases, pendingObjects);
+ } while (!m_objectsWithAliases.isEmpty() && atLeastOneAliasResolved);
+
+ if (!atLeastOneAliasResolved && !m_objectsWithAliases.isEmpty()) {
+ const CompiledObject *obj = m_compiler->objectAt(m_objectsWithAliases.first());
+ for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
+ if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved))
+ return error(alias->location, QQmlComponentAndAliasResolverBase::tr("Circular alias reference detected"));
+ }
+ }
+
+ return QQmlError();
+}
+
+template<typename ObjectContainer>
+void QQmlComponentAndAliasResolver<ObjectContainer>::resolveGeneralizedGroupProperties(
+ int componentIndex)
+{
+ const auto &component = *m_compiler->objectAt(componentIndex);
+ for (CompiledBinding *binding : m_generalizedGroupProperties)
+ resolveGeneralizedGroupProperty(component, binding);
+}
+
+QT_END_NAMESPACE
+
+#endif // QQMLCOMPONENTANDALIASRESOLVER_P_H
diff --git a/src/qml/qml/qqmlcomponentattached_p.h b/src/qml/qml/qqmlcomponentattached_p.h
index 14874ab9cb..303fea7b46 100644
--- a/src/qml/qml/qqmlcomponentattached_p.h
+++ b/src/qml/qml/qqmlcomponentattached_p.h
@@ -16,6 +16,7 @@
//
#include <QtQml/qqml.h>
+#include <QtQml/qqmlcomponent.h>
#include <private/qtqmlglobal_p.h>
#include <QtCore/QObject>
@@ -23,16 +24,9 @@ QT_BEGIN_NAMESPACE
// implemented in qqmlcomponent.cpp
-class Q_QML_PRIVATE_EXPORT QQmlComponentAttached : public QObject
+class Q_QML_EXPORT QQmlComponentAttached : public QObject
{
Q_OBJECT
-
- // Used as attached object for QQmlComponent. We want qqmlcomponentattached_p.h to be #include'd
- // when registering QQmlComponent, but we cannot #include it from qqmlcomponent.h. Therefore we
- // force an anonymous type registration here.
- QML_ANONYMOUS
- QML_ADDED_IN_VERSION(2, 0)
- Q_CLASSINFO("QML.OmitFromQmlTypes", "true")
public:
QQmlComponentAttached(QObject *parent = nullptr);
~QQmlComponentAttached();
@@ -68,4 +62,7 @@ private:
QT_END_NAMESPACE
+// TODO: We still need this because we cannot properly use QML_ATTACHED with QML_FOREIGN.
+QML_DECLARE_TYPEINFO(QQmlComponent, QML_HAS_ATTACHED_PROPERTIES)
+
#endif // QQMLCOMPONENTATTACHED_P_H
diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp
index a43bded441..cf6736deb9 100644
--- a/src/qml/qml/qqmlcontext.cpp
+++ b/src/qml/qml/qqmlcontext.cpp
@@ -23,30 +23,44 @@ QT_BEGIN_NAMESPACE
\brief The QQmlContext class defines a context within a QML engine.
\inmodule QtQml
- Contexts allow data to be exposed to the QML components instantiated by the
- QML engine.
+ Contexts hold the objects identified by \e id in a QML document. You
+ can use \l{nameForObject()} and \l{objectForName()} to retrieve them.
- Each QQmlContext contains a set of properties, distinct from its QObject
- properties, that allow data to be explicitly bound to a context by name. The
- context properties are defined and updated by calling
- QQmlContext::setContextProperty(). The following example shows a Qt model
- being bound to a context and then accessed from a QML file.
+ \note It is the responsibility of the creator to delete any QQmlContext it
+ constructs. If a QQmlContext is no longer needed, it must be destroyed
+ explicitly. The simplest way to ensure this is to give the QQmlContext a
+ \l{QObject::setParent()}{parent}.
- \code
- QQmlEngine engine;
- QStringListModel modelData;
- QQmlContext *context = new QQmlContext(engine.rootContext());
- context->setContextProperty("myModel", &modelData);
+ \section2 The Context Hierarchy
- QQmlComponent component(&engine);
- component.setData("import QtQuick 2.0; ListView { model: myModel }", QUrl());
- QObject *window = component.create(context);
- \endcode
+ Contexts form a hierarchy. The root of this hierarchy is the QML engine's
+ \l {QQmlEngine::rootContext()}{root context}. Each QML component creates its
+ own context when instantiated and some QML elements create extra contexts
+ for themselves.
- \note It is the responsibility of the creator to delete any QQmlContext it
- constructs. If the \c context object in the example is no longer needed when the
- \c window component instance is destroyed, the \c context must be destroyed explicitly.
- The simplest way to ensure this is to set \c window as the parent of \c context.
+ While QML objects instantiated in a context are not strictly owned by that
+ context, their bindings are. If a context is destroyed, the property bindings of
+ outstanding QML objects will stop evaluating.
+
+ \section2 Context Properties
+
+ Contexts also allow data to be exposed to the QML components instantiated
+ by the QML engine. Such data is invisible to any tooling, including the
+ \l{Qt Quick Compiler} and to future readers of the QML documents in
+ question. It will only be exposed if the QML component is instantiated in
+ the specific C++ context you are envisioning. In other places, different
+ context data may be exposed instead.
+
+ Instead of using the QML context to expose data to your QML components, you
+ should either create additional object properties to hold the data or use
+ \l{QML_SINGLETON}{singletons}. See
+ \l{qtqml-cppintegration-exposecppstate.html}{Exposing C++ State to QML} for
+ a detailed explanation.
+
+ Each QQmlContext contains a set of properties, distinct from its QObject
+ properties, that allow data to be explicitly bound to a context by name. The
+ context properties can be defined and updated by calling
+ QQmlContext::setContextProperty().
To simplify binding and maintaining larger data sets, a context object can be set
on a QQmlContext. All the properties of the context object are available
@@ -55,59 +69,17 @@ QT_BEGIN_NAMESPACE
detected through the property's notify signal. Setting a context object is both
faster and easier than manually adding and maintaining context property values.
- The following example has the same effect as the previous one, but it uses a context
- object.
-
- \code
- class MyDataSet : public QObject {
- // ...
- Q_PROPERTY(QAbstractItemModel *myModel READ model NOTIFY modelChanged)
- // ...
- };
-
- MyDataSet myDataSet;
- QQmlEngine engine;
- QQmlContext *context = new QQmlContext(engine.rootContext());
- context->setContextObject(&myDataSet);
-
- QQmlComponent component(&engine);
- component.setData("import QtQuick 2.0; ListView { model: myModel }", QUrl());
- component.create(context);
- \endcode
-
All properties added explicitly by QQmlContext::setContextProperty() take
precedence over the context object's properties.
- \section2 The Context Hierarchy
-
- Contexts form a hierarchy. The root of this hierarchy is the QML engine's
- \l {QQmlEngine::rootContext()}{root context}. Child contexts inherit
- the context properties of their parents; if a child context sets a context property
- that already exists in its parent, the new context property overrides that of the
- parent.
-
- The following example defines two contexts - \c context1 and \c context2. The
- second context overrides the "b" context property inherited from the first with a
- new value.
-
- \code
- QQmlEngine engine;
- QQmlContext *context1 = new QQmlContext(engine.rootContext());
- QQmlContext *context2 = new QQmlContext(context1);
-
- context1->setContextProperty("a", 9001);
- context1->setContextProperty("b", 9001);
-
- context2->setContextProperty("b", 42);
- \endcode
-
- While QML objects instantiated in a context are not strictly owned by that
- context, their bindings are. If a context is destroyed, the property bindings of
- outstanding QML objects will stop evaluating.
+ Child contexts inherit the context properties of their parents; if a child
+ context sets a context property that already exists in its parent, the new
+ context property overrides that of the parent.
- \warning Setting the context object or adding new context properties after an object
- has been created in that context is an expensive operation (essentially forcing all bindings
- to reevaluate). Thus whenever possible you should complete "setup" of the context
+ \warning Setting the context object or adding new context properties after
+ an object has been created in that context is an expensive operation
+ (essentially forcing all bindings to re-evaluate). Thus, if you need to use
+ context properties, you should at least complete the "setup" of the context
before using it to create any objects.
\sa {qtqml-cppintegration-exposecppattributes.html}{Exposing Attributes of C++ Types to QML}
@@ -208,6 +180,9 @@ QObject *QQmlContext::contextObject() const
/*!
Set the context \a object.
+
+ \note You should not use context objects to inject values into your QML
+ components. Use singletons or regular object properties instead.
*/
void QQmlContext::setContextObject(QObject *object)
{
@@ -231,6 +206,9 @@ void QQmlContext::setContextObject(QObject *object)
/*!
Set a the \a value of the \a name property on this context.
+
+ \note You should not use context properties to inject values into your QML
+ components. Use singletons or regular object properties instead.
*/
void QQmlContext::setContextProperty(const QString &name, const QVariant &value)
{
@@ -250,6 +228,11 @@ void QQmlContext::setContextProperty(const QString &name, const QVariant &value)
return;
}
+ if (bool isNumber = false; name.toUInt(&isNumber), isNumber) {
+ qWarning("QQmlContext: Using numbers as context properties will be disallowed in a future Qt version.");
+ QT7_ONLY(return;)
+ }
+
int idx = data->propertyIndex(name);
if (idx == -1) {
data->addPropertyNameAndIndex(name, data->numIdValues() + d->numPropertyValues());
@@ -271,6 +254,9 @@ void QQmlContext::setContextProperty(const QString &name, const QVariant &value)
Set the \a value of the \a name property on this context.
QQmlContext does \b not take ownership of \a value.
+
+ \note You should not use context properties to inject values into your QML
+ components. Use singletons or regular object properties instead.
*/
void QQmlContext::setContextProperty(const QString &name, QObject *value)
{
@@ -286,6 +272,9 @@ void QQmlContext::setContextProperty(const QString &name, QObject *value)
refreshing expressions, and is therefore recommended
instead of calling \l setContextProperty() for each individual property.
+ \note You should not use context properties to inject values into your QML
+ components. Use singletons or regular object properties instead.
+
\sa QQmlContext::setContextProperty()
*/
void QQmlContext::setContextProperties(const QList<PropertyPair> &properties)
@@ -464,7 +453,7 @@ QJSValue QQmlContext::importedScript(const QString &name) const
{
Q_D(const QQmlContext);
- QQmlTypeNameCache::Result r = d->m_data->imports()->query(name);
+ QQmlTypeNameCache::Result r = d->m_data->imports()->query(name, QQmlTypeLoader::get(engine()));
QV4::Scope scope(engine()->handle());
QV4::ScopedObject scripts(scope, d->m_data->importedScripts().valueRef());
return scripts ? QJSValuePrivate::fromReturnedValue(scripts->get(r.scriptIndex))
diff --git a/src/qml/qml/qqmlcontext.h b/src/qml/qml/qqmlcontext.h
index eb6210d5b3..b02aefe1d6 100644
--- a/src/qml/qml/qqmlcontext.h
+++ b/src/qml/qml/qqmlcontext.h
@@ -17,7 +17,6 @@ QT_BEGIN_NAMESPACE
class QString;
class QQmlEngine;
-class QQmlRefCount;
class QQmlContextPrivate;
class QQmlCompositeTypeData;
class QQmlContextData;
diff --git a/src/qml/qml/qqmlcontextdata_p.h b/src/qml/qml/qqmlcontextdata_p.h
index 99f2467dd9..3aeabf72fa 100644
--- a/src/qml/qml/qqmlcontextdata_p.h
+++ b/src/qml/qml/qqmlcontextdata_p.h
@@ -30,7 +30,7 @@ class QQmlGuardedContextData;
class QQmlJavaScriptExpression;
class QQmlIncubatorPrivate;
-class Q_QML_PRIVATE_EXPORT QQmlContextData
+class Q_QML_EXPORT QQmlContextData
{
public:
static QQmlRefPointer<QQmlContextData> createRefCounted(
@@ -128,6 +128,28 @@ public:
QObject *contextObject() const { return m_contextObject; }
void setContextObject(QObject *contextObject) { m_contextObject = contextObject; }
+ template<typename HandleSelf, typename HandleLinked>
+ void deepClearContextObject(
+ QObject *contextObject, HandleSelf &&handleSelf, HandleLinked &&handleLinked) {
+ for (QQmlContextData *lc = m_linkedContext.data(); lc; lc = lc->m_linkedContext.data()) {
+ handleLinked(lc);
+ if (lc->m_contextObject == contextObject)
+ lc->m_contextObject = nullptr;
+ }
+
+ handleSelf(this);
+ if (m_contextObject == contextObject)
+ m_contextObject = nullptr;
+ }
+
+ void deepClearContextObject(QObject *contextObject)
+ {
+ deepClearContextObject(
+ contextObject,
+ [](QQmlContextData *self) { self->emitDestruction(); },
+ [](QQmlContextData *){});
+ }
+
QQmlEngine *engine() const { return m_engine; }
void setEngine(QQmlEngine *engine) { m_engine = engine; }
@@ -266,6 +288,14 @@ public:
void addExpression(QQmlJavaScriptExpression *expression);
+ bool valueTypesAreAddressable() const {
+ return m_typeCompilationUnit && m_typeCompilationUnit->valueTypesAreAddressable();
+ }
+
+ bool valueTypesAreAssertable() const {
+ return m_typeCompilationUnit && m_typeCompilationUnit->valueTypesAreAssertable();
+ }
+
private:
friend class QQmlGuardedContextData;
friend class QQmlContextPrivate;
@@ -358,7 +388,7 @@ private:
quint32 m_ownedByParent:1;
quint32 m_ownedByPublicContext:1;
quint32 m_hasExtraObject:1; // used in QQmlDelegateModelItem::dataForObject to find the corresponding QQmlDelegateModelItem of an object
- quint32 m_dummy:23;
+ Q_DECL_UNUSED_MEMBER quint32 m_dummy:23;
QQmlContext *m_publicContext = nullptr;
union {
diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp
index d3f43cb8c4..bd632d25a6 100644
--- a/src/qml/qml/qqmlcustomparser.cpp
+++ b/src/qml/qml/qqmlcustomparser.cpp
@@ -103,10 +103,13 @@ int QQmlCustomParser::evaluateEnum(const QString &script, bool *ok) const
const QString scope = script.left(dot);
if (scope != QLatin1String("Qt")) {
- if (imports.isNull())
+ if (!engine || imports.isNull())
return -1;
QQmlType type;
+ QQmlTypeLoader *typeLoader = QQmlTypeLoader::get(engine);
+ Q_ASSERT(typeLoader);
+
if (imports.isT1()) {
QQmlImportNamespace *ns = nullptr;
@@ -115,7 +118,7 @@ int QQmlCustomParser::evaluateEnum(const QString &script, bool *ok) const
bool recursionDetected = false;
if (!imports.asT1()->resolveType(
- scope, &type, nullptr, &ns, nullptr,
+ typeLoader, scope, &type, nullptr, &ns, nullptr,
QQmlType::AnyRegistrationType, &recursionDetected)) {
return -1;
}
@@ -123,7 +126,7 @@ int QQmlCustomParser::evaluateEnum(const QString &script, bool *ok) const
if (!type.isValid() && ns != nullptr) {
dot = nextDot(dot);
if (dot == -1 || !imports.asT1()->resolveType(
- script.left(dot), &type, nullptr, nullptr, nullptr,
+ typeLoader, script.left(dot), &type, nullptr, nullptr, nullptr,
QQmlType::AnyRegistrationType, &recursionDetected)) {
return -1;
}
@@ -131,13 +134,15 @@ int QQmlCustomParser::evaluateEnum(const QString &script, bool *ok) const
} else {
// Allow recursion so that we can find enums from the same document.
const QQmlTypeNameCache::Result result
- = imports.asT2()->query<QQmlImport::AllowRecursion>(scope);
+ = imports.asT2()->query<QQmlImport::AllowRecursion>(scope, typeLoader);
if (result.isValid()) {
type = result.type;
} else if (result.importNamespace) {
dot = nextDot(dot);
- if (dot != -1)
- type = imports.asT2()->query<QQmlImport::AllowRecursion>(script.left(dot)).type;
+ if (dot != -1) {
+ type = imports.asT2()->query<QQmlImport::AllowRecursion>(
+ script.left(dot), typeLoader).type;
+ }
}
}
@@ -196,10 +201,10 @@ int QQmlCustomParser::evaluateEnum(const QString &script, bool *ok) const
*/
const QMetaObject *QQmlCustomParser::resolveType(const QString& name) const
{
- if (!imports.isT1())
+ if (!engine || !imports.isT1())
return nullptr;
QQmlType qmltype;
- if (!imports.asT1()->resolveType(name, &qmltype, nullptr, nullptr, nullptr))
+ if (!imports.asT1()->resolveType(QQmlTypeLoader::get(engine), name, &qmltype, nullptr, nullptr))
return nullptr;
return qmltype.metaObject();
}
diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h
index fc0a814af6..4a3628fdb1 100644
--- a/src/qml/qml/qqmlcustomparser_p.h
+++ b/src/qml/qml/qqmlcustomparser_p.h
@@ -20,14 +20,13 @@
#include <private/qv4compileddata_p.h>
#include <QtCore/qbytearray.h>
-#include <QtCore/qxmlstream.h>
QT_BEGIN_NAMESPACE
class QQmlPropertyValidator;
class QQmlEnginePrivate;
-class Q_QML_PRIVATE_EXPORT QQmlCustomParser
+class Q_QML_EXPORT QQmlCustomParser
{
public:
enum Flag {
diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h
index 5b7c05c61e..7055ca94e6 100644
--- a/src/qml/qml/qqmldata_p.h
+++ b/src/qml/qml/qqmldata_p.h
@@ -52,10 +52,12 @@ struct Binding;
// default state for elemental object allocations. This is crucial in the
// workings of the QQmlInstruction::CreateSimpleObject instruction.
// Don't change anything here without first considering that case!
-class Q_QML_PRIVATE_EXPORT QQmlData : public QAbstractDeclarativeData
+class Q_QML_EXPORT QQmlData : public QAbstractDeclarativeData
{
public:
- QQmlData();
+ enum Ownership { DoesNotOwnMemory, OwnsMemory };
+
+ QQmlData(Ownership ownership);
~QQmlData();
static inline void init() {
@@ -122,24 +124,24 @@ public:
};
struct NotifyList {
- quint64 connectionMask;
-
- quint16 maximumTodoIndex;
- quint16 notifiesSize;
-
- QQmlNotifierEndpoint *todo;
- QQmlNotifierEndpoint**notifies;
+ QAtomicInteger<quint64> connectionMask;
+ QQmlNotifierEndpoint *todo = nullptr;
+ QQmlNotifierEndpoint**notifies = nullptr;
+ quint16 maximumTodoIndex = 0;
+ quint16 notifiesSize = 0;
void layout();
private:
void layout(QQmlNotifierEndpoint*);
};
- NotifyList *notifyList;
+ QAtomicPointer<NotifyList> notifyList;
- inline QQmlNotifierEndpoint *notify(int index);
+ inline QQmlNotifierEndpoint *notify(int index) const;
void addNotify(int index, QQmlNotifierEndpoint *);
int endpointCount(int index);
bool signalHasEndpoint(int index) const;
- void disconnectNotifiers();
+
+ enum class DeleteNotifyList { Yes, No };
+ void disconnectNotifiers(DeleteNotifyList doDelete);
// The context that created the C++ object; not refcounted to prevent cycles
QQmlContextData *context = nullptr;
@@ -147,13 +149,13 @@ public:
QQmlContextData *outerContext = nullptr;
QQmlRefPointer<QQmlContextData> ownContext;
- QQmlAbstractBinding *bindings;
- QQmlBoundSignal *signalHandlers;
+ QQmlAbstractBinding *bindings = nullptr;
+ QQmlBoundSignal *signalHandlers = nullptr;
std::vector<QQmlPropertyObserver> propertyObservers;
// Linked list for QQmlContext::contextObjects
- QQmlData *nextContextObject;
- QQmlData**prevContextObject;
+ QQmlData *nextContextObject = nullptr;
+ QQmlData**prevContextObject = nullptr;
inline bool hasBindingBit(int) const;
inline void setBindingBit(QObject *obj, int);
@@ -163,10 +165,10 @@ public:
inline void setPendingBindingBit(QObject *obj, int);
inline void clearPendingBindingBit(int);
- quint16 lineNumber;
- quint16 columnNumber;
+ quint16 lineNumber = 0;
+ quint16 columnNumber = 0;
- quint32 jsEngineId; // id of the engine that created the jsWrapper
+ quint32 jsEngineId = 0; // id of the engine that created the jsWrapper
struct DeferredData {
DeferredData();
@@ -192,7 +194,7 @@ public:
QQmlPropertyCache::ConstPtr propertyCache;
- QQmlGuardImpl *guards;
+ QQmlGuardImpl *guards = nullptr;
static QQmlData *get(QObjectPrivate *priv, bool create) {
// If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has
@@ -260,7 +262,7 @@ public:
private:
// For attachedProperties
- mutable QQmlDataExtended *extendedData;
+ mutable QQmlDataExtended *extendedData = nullptr;
Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv);
Q_NEVER_INLINE static QQmlPropertyCache::ConstPtr createPropertyCache(QObject *object);
@@ -295,7 +297,7 @@ private:
Q_NEVER_INLINE BindingBitsType *growBits(QObject *obj, int bit);
- Q_DISABLE_COPY(QQmlData);
+ Q_DISABLE_COPY_MOVE(QQmlData);
};
bool QQmlData::wasDeleted(const QObjectPrivate *priv)
@@ -316,23 +318,31 @@ bool QQmlData::wasDeleted(const QObject *object)
return QQmlData::wasDeleted(priv);
}
-QQmlNotifierEndpoint *QQmlData::notify(int index)
+inline bool isIndexInConnectionMask(quint64 connectionMask, int index)
+{
+ return connectionMask & (1ULL << quint64(index % 64));
+}
+
+QQmlNotifierEndpoint *QQmlData::notify(int index) const
{
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+
Q_ASSERT(index <= 0xFFFF);
- if (!notifyList || !(notifyList->connectionMask & (1ULL << quint64(index % 64)))) {
+ NotifyList *list = notifyList.loadRelaxed();
+ if (!list || !isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index))
return nullptr;
- } else if (index < notifyList->notifiesSize) {
- return notifyList->notifies[index];
- } else if (index <= notifyList->maximumTodoIndex) {
- notifyList->layout();
- }
- if (index < notifyList->notifiesSize) {
- return notifyList->notifies[index];
- } else {
- return nullptr;
+ if (index < list->notifiesSize)
+ return list->notifies[index];
+
+ if (index <= list->maximumTodoIndex) {
+ list->layout();
+ if (index < list->notifiesSize)
+ return list->notifies[index];
}
+
+ return nullptr;
}
/*
@@ -341,7 +351,19 @@ QQmlNotifierEndpoint *QQmlData::notify(int index)
*/
inline bool QQmlData::signalHasEndpoint(int index) const
{
- return notifyList && (notifyList->connectionMask & (1ULL << quint64(index % 64)));
+ // This can be called from any thread.
+ // We still use relaxed semantics. If we're on a thread different from the "home" thread
+ // of the QQmlData, two interesting things might happen:
+ //
+ // 1. The list might go away while we hold it. In that case we are dealing with an object whose
+ // QObject dtor is being executed concurrently. This is UB already without the notify lists.
+ // Therefore, we don't need to consider it.
+ // 2. The connectionMask may be amended or zeroed while we are looking at it. In that case
+ // we "misreport" the endpoint. Since ordering of events across threads is inherently
+ // nondeterministic, either result is correct in that case. We can accept it.
+
+ NotifyList *list = notifyList.loadRelaxed();
+ return list && isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index);
}
bool QQmlData::hasBindingBit(int coreIndex) const
diff --git a/src/qml/qml/qqmldatablob.cpp b/src/qml/qml/qqmldatablob.cpp
index 054317b5ad..6b354c337f 100644
--- a/src/qml/qml/qqmldatablob.cpp
+++ b/src/qml/qml/qqmldatablob.cpp
@@ -39,16 +39,14 @@ The QQmlTypeLoader invokes callbacks on the QQmlDataBlob as data becomes availab
This enum describes the status of the data blob.
-\list
-\li Null The blob has not yet been loaded by a QQmlTypeLoader
-\li Loading The blob is loading network data. The QQmlDataBlob::setData() callback has not yet been
- invoked or has not yet returned.
-\li WaitingForDependencies The blob is waiting for dependencies to be done before continuing.
- This status only occurs after the QQmlDataBlob::setData() callback has been made, and when the
- blob has outstanding dependencies.
-\li Complete The blob's data has been loaded and all dependencies are done.
-\li Error An error has been set on this blob.
-\endlist
+\value Null The blob has not yet been loaded by a QQmlTypeLoader
+\value Loading The blob is loading network data. The QQmlDataBlob::setData() callback has
+ not yet been invoked or has not yet returned.
+\value WaitingForDependencies The blob is waiting for dependencies to be done before continuing.
+ This status only occurs after the QQmlDataBlob::setData() callback has been made,
+ and when the blob has outstanding dependencies.
+\value Complete The blob's data has been loaded and all dependencies are done.
+\value Error An error has been set on this blob.
*/
/*!
@@ -56,11 +54,9 @@ This enum describes the status of the data blob.
This enum describes the type of the data blob.
-\list
-\li QmlFile This is a QQmlTypeData
-\li JavaScriptFile This is a QQmlScriptData
-\li QmldirFile This is a QQmlQmldirData
-\endlist
+\value QmlFile This is a QQmlTypeData
+\value JavaScriptFile This is a QQmlScriptData
+\value QmldirFile This is a QQmlQmldirData
*/
/*!
diff --git a/src/qml/qml/qqmldatablob_p.h b/src/qml/qml/qqmldatablob_p.h
index b54f71c79e..87a7e8e2e8 100644
--- a/src/qml/qml/qqmldatablob_p.h
+++ b/src/qml/qml/qqmldatablob_p.h
@@ -17,7 +17,6 @@
#include <private/qqmlrefcount_p.h>
#include <private/qqmljsdiagnosticmessage_p.h>
-#include <private/qv4compileddata_p.h>
#if QT_CONFIG(qml_network)
#include <QtNetwork/qnetworkreply.h>
@@ -35,7 +34,7 @@
QT_BEGIN_NAMESPACE
class QQmlTypeLoader;
-class Q_QML_PRIVATE_EXPORT QQmlDataBlob : public QQmlRefCount
+class Q_QML_EXPORT QQmlDataBlob : public QQmlRefCounted<QQmlDataBlob>
{
public:
using Ptr = QQmlRefPointer<QQmlDataBlob>;
@@ -56,7 +55,7 @@ public:
};
QQmlDataBlob(const QUrl &, Type, QQmlTypeLoader* manager);
- ~QQmlDataBlob() override;
+ virtual ~QQmlDataBlob();
void startLoading();
diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp
index 96a5679599..ead8a717f5 100644
--- a/src/qml/qml/qqmldelayedcallqueue.cpp
+++ b/src/qml/qml/qqmldelayedcallqueue.cpp
@@ -70,7 +70,7 @@ void QQmlDelayedCallQueue::init(QV4::ExecutionEngine* engine)
m_tickedMethod = metaObject.method(methodIndex);
}
-QV4::ReturnedValue QQmlDelayedCallQueue::addUniquelyAndExecuteLater(QV4::ExecutionEngine *engine, QQmlV4Function *args)
+QV4::ReturnedValue QQmlDelayedCallQueue::addUniquelyAndExecuteLater(QV4::ExecutionEngine *engine, QQmlV4FunctionPtr args)
{
QQmlDelayedCallQueue *self = engine->delayedCallQueue();
@@ -126,8 +126,9 @@ QV4::ReturnedValue QQmlDelayedCallQueue::addUniquelyAndExecuteLater(QV4::Executi
// if it's a qobject function wrapper, guard against qobject deletion
dfc.m_objectGuard = QQmlGuard<QObject>(functionData.first);
dfc.m_guarded = true;
- } else if (func->scope()->type == QV4::Heap::ExecutionContext::Type_QmlContext) {
- QV4::QmlContext::Data *g = static_cast<QV4::QmlContext::Data *>(func->scope());
+ } else if (const auto *js = func->as<QV4::JavaScriptFunctionObject>();
+ js && js->scope()->type == QV4::Heap::ExecutionContext::Type_QmlContext) {
+ QV4::QmlContext::Data *g = static_cast<QV4::QmlContext::Data *>(js->scope());
Q_ASSERT(g->qml()->scopeObject);
dfc.m_objectGuard = QQmlGuard<QObject>(g->qml()->scopeObject);
dfc.m_guarded = true;
@@ -142,7 +143,7 @@ QV4::ReturnedValue QQmlDelayedCallQueue::addUniquelyAndExecuteLater(QV4::Executi
return QV4::Encode::undefined();
}
-void QQmlDelayedCallQueue::storeAnyArguments(DelayedFunctionCall &dfc, QQmlV4Function *args, int offset, QV4::ExecutionEngine *engine)
+void QQmlDelayedCallQueue::storeAnyArguments(DelayedFunctionCall &dfc, QQmlV4FunctionPtr args, int offset, QV4::ExecutionEngine *engine)
{
const int length = args->length() - offset;
if (length == 0) {
diff --git a/src/qml/qml/qqmldelayedcallqueue_p.h b/src/qml/qml/qqmldelayedcallqueue_p.h
index 9b66e85a14..88f0c4d118 100644
--- a/src/qml/qml/qqmldelayedcallqueue_p.h
+++ b/src/qml/qml/qqmldelayedcallqueue_p.h
@@ -24,8 +24,6 @@
QT_BEGIN_NAMESPACE
-class QQmlV4Function;
-
class QQmlDelayedCallQueue : public QObject
{
Q_OBJECT
@@ -36,7 +34,7 @@ public:
void init(QV4::ExecutionEngine *);
static QV4::ReturnedValue addUniquelyAndExecuteLater(QV4::ExecutionEngine *engine,
- QQmlV4Function *args);
+ QQmlV4FunctionPtr args);
public Q_SLOTS:
void ticked();
@@ -56,7 +54,7 @@ private:
bool m_guarded;
};
- void storeAnyArguments(DelayedFunctionCall& dfc, QQmlV4Function *args, int offset, QV4::ExecutionEngine *engine);
+ void storeAnyArguments(DelayedFunctionCall& dfc, QQmlV4FunctionPtr args, int offset, QV4::ExecutionEngine *engine);
void executeAllExpired_Later();
QV4::ExecutionEngine *m_engine;
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 7c218276c2..14408c735b 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -4,57 +4,45 @@
#include "qqmlengine_p.h"
#include "qqmlengine.h"
-#include "qqmlcontext_p.h"
-#include "qqml.h"
-#include "qqmlcontext.h"
-#include "qqmlscriptstring.h"
-#include "qqmlglobal_p.h"
-#include "qqmlnotifier_p.h"
-#include "qqmlincubator.h"
-#include "qqmlabstracturlinterceptor.h"
-
-#include <private/qqmldirparser_p.h>
+#include <private/qqmlabstractbinding_p.h>
#include <private/qqmlboundsignal_p.h>
-#include <private/qqmljsdiagnosticmessage_p.h>
-#include <private/qqmltype_p_p.h>
+#include <private/qqmlcontext_p.h>
+#include <private/qqmlnotifier_p.h>
#include <private/qqmlpluginimporter_p.h>
-#include <QtCore/qstandardpaths.h>
-#include <QtCore/qmetaobject.h>
-#include <QDebug>
+#include <private/qqmlprofiler_p.h>
+#include <private/qqmlscriptdata_p.h>
+#include <private/qqmlsourcecoordinate_p.h>
+#include <private/qqmltype_p.h>
+#include <private/qqmltypedata_p.h>
+#include <private/qqmlvmemetaobject_p.h>
+#include <private/qqmlcomponent_p.h>
+
+#include <private/qobject_p.h>
+#include <private/qthread_p.h>
+
+#include <QtQml/qqml.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlincubator.h>
+#include <QtQml/qqmlscriptstring.h>
+
#include <QtCore/qcoreapplication.h>
#include <QtCore/qcryptographichash.h>
#include <QtCore/qdir.h>
+#include <QtCore/qmetaobject.h>
#include <QtCore/qmutex.h>
+#include <QtCore/qstandardpaths.h>
#include <QtCore/qthread.h>
-#include <private/qthread_p.h>
-#include <private/qqmlscriptdata_p.h>
-#include <QtQml/private/qqmlcomponentattached_p.h>
-#include <QtQml/private/qqmlsourcecoordinate_p.h>
-#include <QtQml/private/qqmlcomponent_p.h>
#if QT_CONFIG(qml_network)
-#include "qqmlnetworkaccessmanagerfactory.h"
-#include <QNetworkAccessManager>
+#include <QtQml/qqmlnetworkaccessmanagerfactory.h>
+#include <QtNetwork/qnetworkaccessmanager.h>
#endif
-#include <private/qobject_p.h>
-#include <private/qmetaobject_p.h>
-#if QT_CONFIG(qml_locale)
-#include <private/qqmllocale_p.h>
-#endif
-#include <private/qqmlbind_p.h>
-#include <private/qqmlconnections_p.h>
-#if QT_CONFIG(qml_animation)
-#include <private/qqmltimer_p.h>
-#endif
-#include <private/qqmlplatform_p.h>
-#include <private/qqmlloggingcategory_p.h>
-#include <private/qv4sequenceobject_p.h>
-
#ifdef Q_OS_WIN // for %APPDATA%
# include <qt_windows.h>
# include <shlobj.h>
-# include <qlibrary.h>
+# include <QtCore/qlibrary.h>
# ifndef CSIDL_APPDATA
# define CSIDL_APPDATA 0x001a // <username>\Application Data
# endif
@@ -62,6 +50,8 @@
QT_BEGIN_NAMESPACE
+void qml_register_types_QML();
+
/*!
\qmltype QtObject
\instantiates QObject
@@ -215,23 +205,16 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
{
QObjectPrivate *p = QObjectPrivate::get(o);
if (QQmlData *d = QQmlData::get(p)) {
+ const auto invalidate = [](QQmlContextData *c) {c->invalidate();};
if (d->ownContext) {
- for (QQmlRefPointer<QQmlContextData> lc = d->ownContext->linkedContext(); lc;
- lc = lc->linkedContext()) {
- lc->invalidate();
- if (lc->contextObject() == o)
- lc->setContextObject(nullptr);
- }
- d->ownContext->invalidate();
- if (d->ownContext->contextObject() == o)
- d->ownContext->setContextObject(nullptr);
+ d->ownContext->deepClearContextObject(o, invalidate, invalidate);
d->ownContext.reset();
d->context = nullptr;
+ Q_ASSERT(!d->outerContext || d->outerContext->contextObject() != o);
+ } else if (d->outerContext && d->outerContext->contextObject() == o) {
+ d->outerContext->deepClearContextObject(o, invalidate, invalidate);
}
- if (d->outerContext && d->outerContext->contextObject() == o)
- d->outerContext->setContextObject(nullptr);
-
if (d->hasVMEMetaObject || d->hasInterceptorMetaObject) {
// This is somewhat dangerous because another thread might concurrently
// try to resolve the dynamic metaobject. In practice this will then
@@ -255,14 +238,11 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
}
}
-QQmlData::QQmlData()
- : ownMemory(true), indestructible(true), explicitIndestructibleSet(false),
+QQmlData::QQmlData(Ownership ownership)
+ : ownMemory(ownership == OwnsMemory), indestructible(true), explicitIndestructibleSet(false),
hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false),
- hasInterceptorMetaObject(false), hasVMEMetaObject(false), hasConstWrapper(false),
- bindingBitsArraySize(InlineBindingArraySize), notifyList(nullptr),
- bindings(nullptr), signalHandlers(nullptr), nextContextObject(nullptr), prevContextObject(nullptr),
- lineNumber(0), columnNumber(0), jsEngineId(0),
- guards(nullptr), extendedData(nullptr)
+ hasInterceptorMetaObject(false), hasVMEMetaObject(false), hasConstWrapper(false), dummy(0),
+ bindingBitsArraySize(InlineBindingArraySize)
{
memset(bindingBitsValue, 0, sizeof(bindingBitsValue));
init();
@@ -312,7 +292,10 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in
// QQmlEngine to emit signals from a different thread. These signals are then automatically
// marshalled back onto the QObject's thread and handled by QML from there. This is tested
// by the qqmlecmascript::threadSignal() autotest.
- if (!ddata->notifyList)
+
+ // Relaxed semantics here. If we're on a different thread we might schedule a useless event,
+ // but that should be rare.
+ if (!ddata->notifyList.loadRelaxed())
return;
auto objectThreadData = QObjectPrivate::get(object)->threadData.loadRelaxed();
@@ -386,11 +369,15 @@ int QQmlData::endpointCount(int index)
void QQmlData::markAsDeleted(QObject *o)
{
- QQmlData::setQueuedForDeletion(o);
-
- QObjectPrivate *p = QObjectPrivate::get(o);
- for (QList<QObject *>::const_iterator it = p->children.constBegin(), end = p->children.constEnd(); it != end; ++it) {
- QQmlData::markAsDeleted(*it);
+ QVarLengthArray<QObject *> workStack;
+ workStack.push_back(o);
+ while (!workStack.isEmpty()) {
+ auto currentObject = workStack.last();
+ workStack.pop_back();
+ QQmlData::setQueuedForDeletion(currentObject);
+ auto currentObjectPriv = QObjectPrivate::get(currentObject);
+ for (QObject *child: std::as_const(currentObjectPriv->children))
+ workStack.push_back(child);
}
}
@@ -400,9 +387,7 @@ void QQmlData::setQueuedForDeletion(QObject *object)
if (QQmlData *ddata = QQmlData::get(object)) {
if (ddata->ownContext) {
Q_ASSERT(ddata->ownContext.data() == ddata->context);
- ddata->context->emitDestruction();
- if (ddata->ownContext->contextObject() == object)
- ddata->ownContext->setContextObject(nullptr);
+ ddata->ownContext->deepClearContextObject(object);
ddata->ownContext.reset();
ddata->context = nullptr;
}
@@ -413,7 +398,7 @@ void QQmlData::setQueuedForDeletion(QObject *object)
// possible to get the metaobject anymore.
// Also, there is no point in evaluating bindings in order to set properties on
// half-deleted objects.
- ddata->disconnectNotifiers();
+ ddata->disconnectNotifiers(DeleteNotifyList::No);
}
}
}
@@ -437,38 +422,57 @@ void QQmlData::flushPendingBinding(int coreIndex)
QQmlData::DeferredData::DeferredData() = default;
QQmlData::DeferredData::~DeferredData() = default;
+template<>
+int qmlRegisterType<void>(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
+{
+ QQmlPrivate::RegisterType type = {
+ QQmlPrivate::RegisterType::CurrentVersion,
+ QMetaType(),
+ QMetaType(),
+ 0, nullptr, nullptr,
+ QString(),
+ nullptr,
+ uri,
+ QTypeRevision::fromVersion(versionMajor, versionMinor),
+ qmlName,
+ nullptr,
+ nullptr,
+ nullptr,
+ -1,
+ -1,
+ -1,
+ nullptr,
+ nullptr,
+ nullptr,
+ QTypeRevision::zero(),
+ -1,
+ QQmlPrivate::ValueTypeCreationMethod::None,
+ };
+
+ return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
+}
+
bool QQmlEnginePrivate::baseModulesUninitialized = true;
void QQmlEnginePrivate::init()
{
Q_Q(QQmlEngine);
if (baseModulesUninitialized) {
-
- // required for the Compiler.
- qmlRegisterType<QObject>("QML", 1, 0, "QtObject");
- qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component");
- qmlRegisterAnonymousSequentialContainer<QList<QVariant>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<bool>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<int>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<float>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<double>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<QString>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<QUrl>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<QDateTime>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<QRegularExpression>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<QByteArray>>("QML", 1);
+ // Register builtins
+ qml_register_types_QML();
// No need to specifically register those.
static_assert(std::is_same_v<QStringList, QList<QString>>);
static_assert(std::is_same_v<QVariantList, QList<QVariant>>);
- qRegisterMetaType<QVariant>();
qRegisterMetaType<QQmlScriptString>();
- qRegisterMetaType<QJSValue>();
qRegisterMetaType<QQmlComponent::Status>();
qRegisterMetaType<QList<QObject*> >();
qRegisterMetaType<QQmlBinding*>();
+ // Protect the module: We don't want any URL interceptor to mess with the builtins.
+ qmlProtectModule("QML", 1);
+
QQmlData::init();
baseModulesUninitialized = false;
}
@@ -484,29 +488,16 @@ void QQmlEnginePrivate::init()
\inmodule QtQml
\brief The QQmlEngine class provides an environment for instantiating QML components.
- Each QML component is instantiated in a QQmlContext.
- QQmlContext's are essential for passing data to QML
- components. In QML, contexts are arranged hierarchically and this
- hierarchy is managed by the QQmlEngine.
+ A QQmlEngine is used to manage \l{QQmlComponent}{components} and objects created from
+ them and execute their bindings and functions. QQmlEngine also inherits from
+ \l{QJSEngine} which allows seamless integration between your QML components and
+ JavaScript code.
- Prior to creating any QML components, an application must have
- created a QQmlEngine to gain access to a QML context. The
- following example shows how to create a simple Text item.
+ Each QML component is instantiated in a QQmlContext. In QML, contexts are arranged
+ hierarchically and this hierarchy is managed by the QQmlEngine. By default,
+ components are instantiated in the \l {QQmlEngine::rootContext()}{root context}.
- \code
- QQmlEngine engine;
- QQmlComponent component(&engine);
- component.setData("import QtQuick 2.0\nText { text: \"Hello world!\" }", QUrl());
- QQuickItem *item = qobject_cast<QQuickItem *>(component.create());
-
- //add item to view, etc
- ...
- \endcode
-
- In this case, the Text item will be created in the engine's
- \l {QQmlEngine::rootContext()}{root context}.
-
- \sa QQmlComponent, QQmlContext, {QML Global Object}
+ \sa QQmlComponent, QQmlContext, {QML Global Object}, QQmlApplicationEngine
*/
/*!
@@ -537,11 +528,12 @@ QQmlEngine::QQmlEngine(QQmlEnginePrivate &dd, QObject *parent)
invalidated, but not destroyed (unless they are parented to the
QQmlEngine object).
- See QJSEngine docs for details on cleaning up the JS engine.
+ See ~QJSEngine() for details on cleaning up the JS engine.
*/
QQmlEngine::~QQmlEngine()
{
Q_D(QQmlEngine);
+ handle()->inShutdown = true;
QJSEnginePrivate::removeFromDebugServer(this);
// Emit onDestruction signals for the root context before
@@ -610,9 +602,20 @@ QQmlEngine::~QQmlEngine()
void QQmlEngine::clearComponentCache()
{
Q_D(QQmlEngine);
+
+ // Contexts can hold on to CUs but live on the JS heap.
+ // Use a non-incremental GC run to get rid of those.
+ QV4::MemoryManager *mm = handle()->memoryManager;
+ auto oldLimit = mm->gcStateMachine->timeLimit;
+ mm->setGCTimeLimit(-1);
+ mm->runGC();
+ mm->gcStateMachine->timeLimit = std::move(oldLimit);
+
+ handle()->clearCompilationUnits();
d->typeLoader.lock();
d->typeLoader.clearCache();
d->typeLoader.unlock();
+ QQmlMetaType::freeUnusedTypesAndCaches();
}
/*!
@@ -630,6 +633,7 @@ void QQmlEngine::clearComponentCache()
void QQmlEngine::trimComponentCache()
{
Q_D(QQmlEngine);
+ handle()->trimCompilationUnits();
d->typeLoader.trimCache();
}
@@ -927,6 +931,45 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled)
d->outputWarningsToMsgLog = enabled;
}
+
+/*!
+ \since 6.6
+ If this method is called inside of a function that is part of
+ a binding in QML, the binding will be treated as a translation binding.
+
+ \code
+ class I18nAwareClass : public QObject {
+
+ //...
+
+ QString text() const
+ {
+ if (auto engine = qmlEngine(this))
+ engine->markCurrentFunctionAsTranslationBinding();
+ return tr("Hello, world!");
+ }
+ };
+ \endcode
+
+ \note This function is mostly useful if you wish to provide your
+ own alternative to the qsTr function. To ensure that properties
+ exposed from C++ classes are updated on language changes, it is
+ instead recommended to react to \c LanguageChange events. That
+ is a more general mechanism which also works when the class is
+ used in a non-QML context, and has slightly less overhead. However,
+ using \c markCurrentFunctionAsTranslationBinding can be acceptable
+ when the class is already closely tied to the QML engine.
+ For more details, see \l {Prepare for Dynamic Language Changes}
+
+ \sa QQmlEngine::retranslate
+*/
+void QQmlEngine::markCurrentFunctionAsTranslationBinding()
+{
+ Q_D(QQmlEngine);
+ if (auto propertyCapture = d->propertyCapture)
+ propertyCapture->captureTranslation();
+}
+
/*!
\internal
@@ -1194,11 +1237,12 @@ void QQmlData::deferData(
deferData->context = context;
const QV4::CompiledData::Object *compiledObject = compilationUnit->objectAt(objectIndex);
- const QV4::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(objectIndex);
+ const QV4::CompiledData::BindingPropertyData *propertyData
+ = compilationUnit->bindingPropertyDataPerObjectAt(objectIndex);
const QV4::CompiledData::Binding *binding = compiledObject->bindingTable();
for (quint32 i = 0; i < compiledObject->nBindings; ++i, ++binding) {
- const QQmlPropertyData *property = propertyData.at(i);
+ const QQmlPropertyData *property = propertyData->at(i);
if (binding->hasFlag(QV4::CompiledData::Binding::IsDeferredBinding))
deferData->bindings.insert(property ? property->coreIndex() : -1, binding);
}
@@ -1222,49 +1266,73 @@ void QQmlData::releaseDeferredData()
void QQmlData::addNotify(int index, QQmlNotifierEndpoint *endpoint)
{
- if (!notifyList) {
- notifyList = (NotifyList *)malloc(sizeof(NotifyList));
- notifyList->connectionMask = 0;
- notifyList->maximumTodoIndex = 0;
- notifyList->notifiesSize = 0;
- notifyList->todo = nullptr;
- notifyList->notifies = nullptr;
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+
+ NotifyList *list = notifyList.loadRelaxed();
+
+ if (!list) {
+ list = new NotifyList;
+ // We don't really care when this change takes effect on other threads. The notifyList can
+ // only become non-null once in the life time of a QQmlData. It becomes null again when the
+ // underlying QObject is deleted. At that point any interaction with the QQmlData is UB
+ // anyway. So, for all intents and purposese, the list becomes non-null once and then stays
+ // non-null "forever". We can apply relaxed semantics.
+ notifyList.storeRelaxed(list);
}
Q_ASSERT(!endpoint->isConnected());
index = qMin(index, 0xFFFF - 1);
- notifyList->connectionMask |= (1ULL << quint64(index % 64));
- if (index < notifyList->notifiesSize) {
+ // Likewise, we don't really care _when_ the change in the connectionMask is propagated to other
+ // threads. Cross-thread event ordering is inherently nondeterministic. Therefore, when querying
+ // the conenctionMask in the presence of concurrent modification, any result is correct.
+ list->connectionMask.storeRelaxed(
+ list->connectionMask.loadRelaxed() | (1ULL << quint64(index % 64)));
- endpoint->next = notifyList->notifies[index];
+ if (index < list->notifiesSize) {
+ endpoint->next = list->notifies[index];
if (endpoint->next) endpoint->next->prev = &endpoint->next;
- endpoint->prev = &notifyList->notifies[index];
- notifyList->notifies[index] = endpoint;
-
+ endpoint->prev = &list->notifies[index];
+ list->notifies[index] = endpoint;
} else {
- notifyList->maximumTodoIndex = qMax(int(notifyList->maximumTodoIndex), index);
+ list->maximumTodoIndex = qMax(int(list->maximumTodoIndex), index);
- endpoint->next = notifyList->todo;
+ endpoint->next = list->todo;
if (endpoint->next) endpoint->next->prev = &endpoint->next;
- endpoint->prev = &notifyList->todo;
- notifyList->todo = endpoint;
+ endpoint->prev = &list->todo;
+ list->todo = endpoint;
}
}
-void QQmlData::disconnectNotifiers()
+void QQmlData::disconnectNotifiers(QQmlData::DeleteNotifyList doDelete)
{
- if (notifyList) {
- while (notifyList->todo)
- notifyList->todo->disconnect();
- for (int ii = 0; ii < notifyList->notifiesSize; ++ii) {
- while (QQmlNotifierEndpoint *ep = notifyList->notifies[ii])
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+ if (NotifyList *list = notifyList.loadRelaxed()) {
+ while (QQmlNotifierEndpoint *todo = list->todo)
+ todo->disconnect();
+ for (int ii = 0; ii < list->notifiesSize; ++ii) {
+ while (QQmlNotifierEndpoint *ep = list->notifies[ii])
ep->disconnect();
}
- free(notifyList->notifies);
- free(notifyList);
- notifyList = nullptr;
+ free(list->notifies);
+
+ if (doDelete == DeleteNotifyList::Yes) {
+ // We can only get here from QQmlData::destroyed(), and that can only come from the
+ // the QObject dtor. If you're still sending signals at that point you have UB already
+ // without any threads. Therefore, it's enough to apply relaxed semantics.
+ notifyList.storeRelaxed(nullptr);
+ delete list;
+ } else {
+ // We can use relaxed semantics here. The worst thing that can happen is that some
+ // signal is falsely reported as connected. Signal connectedness across threads
+ // is not quite deterministic anyway.
+ list->connectionMask.storeRelaxed(0);
+ list->maximumTodoIndex = 0;
+ list->notifiesSize = 0;
+ list->notifies = nullptr;
+
+ }
}
}
@@ -1349,7 +1417,7 @@ void QQmlData::destroyed(QObject *object)
guard->objectDestroyed(guard);
}
- disconnectNotifiers();
+ disconnectNotifiers(DeleteNotifyList::Yes);
if (extendedData)
delete extendedData;
@@ -1390,7 +1458,7 @@ QQmlData *QQmlData::createQQmlData(QObjectPrivate *priv)
{
Q_ASSERT(priv);
Q_ASSERT(!priv->isDeletingChildren);
- priv->declarativeData = new QQmlData;
+ priv->declarativeData = new QQmlData(OwnsMemory);
return static_cast<QQmlData *>(priv->declarativeData);
}
@@ -1545,7 +1613,8 @@ void QQmlEnginePrivate::cleanupScarceResources()
The newly added \a path will be first in the importPathList().
- \sa setImportPathList(), {QML Modules}
+ \b {See also} \l setImportPathList(), \l {QML Modules},
+ and \l [QtQml] {QML Import Path}
*/
void QQmlEngine::addImportPath(const QString& path)
{
@@ -1563,9 +1632,8 @@ void QQmlEngine::addImportPath(const QString& path)
provided by that module. A \c qmldir file is required for defining the
type version mapping and possibly QML extensions plugins.
- By default, the list contains the directory of the application executable,
- paths specified in the \c QML_IMPORT_PATH environment variable,
- and the builtin \c QmlImportsPath from QLibraryInfo.
+ By default, this list contains the paths mentioned in
+ \l {QML Import Path}.
\sa addImportPath(), setImportPathList()
*/
@@ -1579,9 +1647,11 @@ QStringList QQmlEngine::importPathList() const
Sets \a paths as the list of directories where the engine searches for
installed modules in a URL-based directory structure.
- By default, the list contains the directory of the application executable,
- paths specified in the \c QML_IMPORT_PATH environment variable,
- and the builtin \c QmlImportsPath from QLibraryInfo.
+ By default, this list contains the paths mentioned in
+ \l {QML Import Path}.
+
+ \warning Calling setImportPathList does not preserve the default
+ import paths.
\sa importPathList(), addImportPath()
*/
@@ -1745,14 +1815,13 @@ QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
{
Q_Q(QQmlEngine);
- QJSValue value = singletonInstances.value(type);
- if (!value.isUndefined()) {
- return value;
- }
-
- QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo();
+ QQmlType::SingletonInstanceInfo::ConstPtr siinfo = type.singletonInstanceInfo();
Q_ASSERT(siinfo != nullptr);
+ QJSValue value = singletonInstances.value(siinfo);
+ if (!value.isUndefined())
+ return value;
+
if (siinfo->scriptCallback) {
value = siinfo->scriptCallback(q, q);
if (value.isQObject()) {
@@ -1761,7 +1830,7 @@ QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
// should behave identically to QML singleton types.
q->setContextForObject(o, new QQmlContext(q->rootContext(), q));
}
- singletonInstances.convertAndInsert(v4engine(), type, &value);
+ singletonInstances.convertAndInsert(v4engine(), siinfo, &value);
} else if (siinfo->qobjectCallback) {
QObject *o = siinfo->qobjectCallback(q, q);
@@ -1790,7 +1859,7 @@ QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
}
value = q->newQObject(o);
- singletonInstances.convertAndInsert(v4engine(), type, &value);
+ singletonInstances.convertAndInsert(v4engine(), siinfo, &value);
} else if (!siinfo->url.isEmpty()) {
QQmlComponent component(q, siinfo->url, QQmlComponent::PreferSynchronous);
if (component.isError()) {
@@ -1800,8 +1869,26 @@ QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
return QJSValue(QJSValue::UndefinedValue);
}
QObject *o = component.beginCreate(q->rootContext());
+ auto *compPriv = QQmlComponentPrivate::get(&component);
+ if (compPriv->state.hasUnsetRequiredProperties()) {
+ /* We would only get the errors from the component after (complete)Create.
+ We can't call create, as we need to convertAndInsert before completeCreate (otherwise
+ tst_qqmllanguage::compositeSingletonCircular fails).
+ On the other hand, we don't want to call cnovertAndInsert if we have an error
+ So create the unset required component errors manually.
+ */
+ delete o;
+ const auto requiredProperties = compPriv->state.requiredProperties();
+ QList<QQmlError> errors (requiredProperties->size());
+ for (const auto &reqProp: *requiredProperties)
+ errors.push_back(QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(reqProp));
+ warning(errors);
+ v4engine()->throwError(QLatin1String("Due to the preceding error(s), Singleton \"%1\" could not be loaded.").arg(QString::fromUtf8(type.typeName())));
+ return QJSValue(QJSValue::UndefinedValue);
+ }
+
value = q->newQObject(o);
- singletonInstances.convertAndInsert(v4engine(), type, &value);
+ singletonInstances.convertAndInsert(v4engine(), siinfo, &value);
component.completeCreate();
}
@@ -1864,7 +1951,7 @@ void QQmlEnginePrivate::executeRuntimeFunction(const QV4::ExecutableCompilationU
// different version of ExecutionEngine::callInContext() that returns a
// QV4::ReturnedValue with no arguments since they are not needed by the
// outer function anyhow
- QV4::ScopedFunctionObject result(scope,
+ QV4::Scoped<QV4::JavaScriptFunctionObject> result(scope,
v4->callInContext(function, thisObject, callContext, 0, nullptr));
Q_ASSERT(result->function());
Q_ASSERT(result->function()->compilationUnit == function->compilationUnit);
@@ -1879,12 +1966,20 @@ void QQmlEnginePrivate::executeRuntimeFunction(const QV4::ExecutableCompilationU
QV4::ExecutableCompilationUnit *QQmlEnginePrivate::compilationUnitFromUrl(const QUrl &url)
{
+ QV4::ExecutionEngine *v4 = v4engine();
+ if (auto unit = v4->compilationUnitForUrl(url)) {
+ if (!unit->runtimeStrings)
+ unit->populate();
+ return unit.data();
+ }
+
auto unit = typeLoader.getType(url)->compilationUnit();
if (!unit)
return nullptr;
- if (!unit->engine)
- unit->linkToEngine(v4engine());
- return unit;
+
+ auto executable = v4->executableCompilationUnit(std::move(unit));
+ executable->populate();
+ return executable.data();
}
QQmlRefPointer<QQmlContextData>
@@ -1897,21 +1992,21 @@ QQmlEnginePrivate::createInternalContext(const QQmlRefPointer<QV4::ExecutableCom
QQmlRefPointer<QQmlContextData> context;
context = QQmlContextData::createRefCounted(parentContext);
context->setInternal(true);
- context->setImports(unit->typeNameCache);
+ context->setImports(unit->typeNameCache());
context->initFromTypeCompilationUnit(unit, subComponentIndex);
- if (isComponentRoot && unit->dependentScripts.size()) {
+ const auto *dependentScripts = unit->dependentScriptsPtr();
+ const qsizetype dependentScriptsSize = dependentScripts->size();
+ if (isComponentRoot && dependentScriptsSize) {
QV4::ExecutionEngine *v4 = v4engine();
Q_ASSERT(v4);
QV4::Scope scope(v4);
- QV4::ScopedObject scripts(scope, v4->newArrayObject(unit->dependentScripts.size()));
+ QV4::ScopedObject scripts(scope, v4->newArrayObject(dependentScriptsSize));
context->setImportedScripts(QV4::PersistentValue(v4, scripts.asReturnedValue()));
QV4::ScopedValue v(scope);
- for (int i = 0; i < unit->dependentScripts.size(); ++i) {
- QQmlRefPointer<QQmlScriptData> s = unit->dependentScripts.at(i);
- scripts->put(i, (v = s->scriptValueForContext(context)));
- }
+ for (qsizetype i = 0; i < dependentScriptsSize; ++i)
+ scripts->put(i, (v = dependentScripts->at(i)->scriptValueForContext(context)));
}
return context;
@@ -1953,7 +2048,7 @@ static inline QString shellNormalizeFileName(const QString &name)
bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */)
{
-#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
+#if defined(Q_OS_DARWIN) || defined(Q_OS_WIN)
QFileInfo info(fileName);
const QString absolute = info.absoluteFilePath();
@@ -2032,10 +2127,12 @@ LoadHelper::LoadHelper(QQmlTypeLoader *loader, QAnyStringView uri)
{
auto import = std::make_shared<PendingImport>();
- import->uri = uri.toString();
+ import->uri = m_uri;
QList<QQmlError> errorList;
- if (!Blob::addImport(import, &errorList))
- m_uri = QString(); // reset m_uri to remember the failure
+ if (!Blob::addImport(import, &errorList)) {
+ qCDebug(lcQmlImport) << "LoadHelper: Errors loading " << m_uri << errorList;
+ m_uri.clear(); // reset m_uri to remember the failure
+ }
}
LoadHelper::ResolveTypeResult LoadHelper::resolveType(QAnyStringView typeName)
@@ -2054,9 +2151,8 @@ LoadHelper::ResolveTypeResult LoadHelper::resolveType(QAnyStringView typeName)
QTypeRevision versionReturn;
QList<QQmlError> errors;
QQmlImportNamespace *ns_return = nullptr;
- m_importCache->resolveType(typeName.toString(), &type, &versionReturn,
- &ns_return,
- &errors);
+ m_importCache->resolveType(
+ typeLoader(), typeName.toString(), &type, &versionReturn, &ns_return, &errors);
return {ResolveTypeResult::ModuleFound, type};
}
@@ -2072,7 +2168,4 @@ bool LoadHelper::couldFindModule() const
QT_END_NAMESPACE
-#include "moc_qqmlengine_p.cpp"
-
#include "moc_qqmlengine.cpp"
-
diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h
index a8b19e41c6..9c20090613 100644
--- a/src/qml/qml/qqmlengine.h
+++ b/src/qml/qml/qqmlengine.h
@@ -123,6 +123,8 @@ public:
bool outputWarningsToStandardError() const;
void setOutputWarningsToStandardError(bool);
+ void markCurrentFunctionAsTranslationBinding();
+
template<typename T>
T singletonInstance(int qmlTypeId);
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 26969467b8..7c820679ba 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -39,6 +39,7 @@
#include <QtCore/qmetaobject.h>
#include <QtCore/qmutex.h>
#include <QtCore/qpair.h>
+#include <QtCore/qpointer.h>
#include <QtCore/qproperty.h>
#include <QtCore/qstack.h>
#include <QtCore/qstring.h>
@@ -57,14 +58,6 @@ class QQmlObjectCreator;
class QQmlProfiler;
class QQmlPropertyCapture;
-struct QObjectForeign {
- Q_GADGET
- QML_FOREIGN(QObject)
- QML_NAMED_ELEMENT(QtObject)
- QML_ADDED_IN_VERSION(2, 0)
- Q_CLASSINFO("QML.OmitFromQmlTypes", "true")
-};
-
// This needs to be declared here so that the pool for it can live in QQmlEnginePrivate.
// The inline method definitions are in qqmljavascriptexpression_p.h
class QQmlJavaScriptExpressionGuard : public QQmlNotifierEndpoint
@@ -81,9 +74,16 @@ public:
};
struct QPropertyChangeTrigger : QPropertyObserver {
- QPropertyChangeTrigger(QQmlJavaScriptExpression *expression) : QPropertyObserver(&QPropertyChangeTrigger::trigger), m_expression(expression) {}
- QQmlJavaScriptExpression * m_expression;
- QObject *target = nullptr;
+ Q_DISABLE_COPY_MOVE(QPropertyChangeTrigger)
+
+ QPropertyChangeTrigger(QQmlJavaScriptExpression *expression)
+ : QPropertyObserver(&QPropertyChangeTrigger::trigger)
+ , m_expression(expression)
+ {
+ }
+
+ QPointer<QObject> target;
+ QQmlJavaScriptExpression *m_expression;
int propertyIndex = 0;
static void trigger(QPropertyObserver *, QUntypedPropertyData *);
@@ -97,7 +97,7 @@ struct TriggerList : QPropertyChangeTrigger {
TriggerList *next = nullptr;
};
-class Q_QML_PRIVATE_EXPORT QQmlEnginePrivate : public QJSEnginePrivate
+class Q_QML_EXPORT QQmlEnginePrivate : public QJSEnginePrivate
{
Q_DECLARE_PUBLIC(QQmlEngine)
public:
@@ -207,8 +207,8 @@ public:
QQmlGadgetPtrWrapper *valueTypeInstance(QMetaType type)
{
int typeIndex = type.id();
- auto it = cachedValueTypeInstances.find(typeIndex);
- if (it != cachedValueTypeInstances.end())
+ auto it = cachedValueTypeInstances.constFind(typeIndex);
+ if (it != cachedValueTypeInstances.cend())
return *it;
if (QQmlValueType *valueType = QQmlMetaType::valueType(type)) {
@@ -245,35 +245,48 @@ public:
}
private:
- class SingletonInstances : private QHash<QQmlType, QJSValue>
+ class SingletonInstances : private QHash<QQmlType::SingletonInstanceInfo::ConstPtr, QJSValue>
{
public:
- void convertAndInsert(QV4::ExecutionEngine *engine, const QQmlType &type, QJSValue *value)
+ void convertAndInsert(
+ QV4::ExecutionEngine *engine, const QQmlType::SingletonInstanceInfo::ConstPtr &type,
+ QJSValue *value)
{
QJSValuePrivate::manageStringOnV4Heap(engine, value);
insert(type, *value);
}
- void clear() {
- for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
- QObject *instance = it.value().toQObject();
+ void clear()
+ {
+ const auto canDelete = [](QObject *instance, const auto &siinfo) -> bool {
if (!instance)
- continue;
+ return false;
+
+ if (!siinfo->url.isEmpty())
+ return true;
+
+ const auto *ddata = QQmlData::get(instance, false);
+ return !(ddata && ddata->indestructible && ddata->explicitIndestructibleSet);
+ };
+
+ for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
+ auto *instance = it.value().toQObject();
+ if (canDelete(instance, it.key()))
+ QQmlData::markAsDeleted(instance);
+ }
- if (it.key().singletonInstanceInfo()->url.isEmpty()) {
- const QQmlData *ddata = QQmlData::get(instance, false);
- if (ddata && ddata->indestructible && ddata->explicitIndestructibleSet)
- continue;
- }
+ for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
+ QObject *instance = it.value().toQObject();
- delete instance;
+ if (canDelete(instance, it.key()))
+ delete instance;
}
- QHash<QQmlType, QJSValue>::clear();
+ QHash<QQmlType::SingletonInstanceInfo::ConstPtr, QJSValue>::clear();
}
- using QHash<QQmlType, QJSValue>::value;
- using QHash<QQmlType, QJSValue>::take;
+ using QHash<QQmlType::SingletonInstanceInfo::ConstPtr, QJSValue>::value;
+ using QHash<QQmlType::SingletonInstanceInfo::ConstPtr, QJSValue>::take;
};
SingletonInstances singletonInstances;
@@ -371,7 +384,7 @@ QQmlEnginePrivate *QQmlEnginePrivate::get(QV4::ExecutionEngine *e)
}
template<>
-Q_QML_PRIVATE_EXPORT QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type);
+Q_QML_EXPORT QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type);
template<typename T>
T QQmlEnginePrivate::singletonInstance(const QQmlType &type) {
diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp
index 7e4483a604..1cc734206e 100644
--- a/src/qml/qml/qqmlexpression.cpp
+++ b/src/qml/qml/qqmlexpression.cpp
@@ -105,6 +105,14 @@ QQmlExpression::QQmlExpression(const QQmlScriptString &script, QQmlContext *ctxt
return;
const QQmlScriptStringPrivate *scriptPrivate = script.d.data();
+ if (!scriptPrivate) {
+ // A null QQmlScriptStringPrivate is an empty expression without context.
+ // We may still want the explicitly passed context, though.
+ if (ctxt)
+ d->init(QQmlContextData::get(ctxt), QString(), scope);
+ return;
+ }
+
if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid()))
return;
diff --git a/src/qml/qml/qqmlexpression.h b/src/qml/qml/qqmlexpression.h
index 912ad9173f..c029c2069f 100644
--- a/src/qml/qml/qqmlexpression.h
+++ b/src/qml/qml/qqmlexpression.h
@@ -14,7 +14,6 @@ QT_BEGIN_NAMESPACE
class QString;
-class QQmlRefCount;
class QQmlEngine;
class QQmlContext;
class QQmlExpressionPrivate;
diff --git a/src/qml/qml/qqmlextensionplugin.cpp b/src/qml/qml/qqmlextensionplugin.cpp
index 319accb768..5af51769ab 100644
--- a/src/qml/qml/qqmlextensionplugin.cpp
+++ b/src/qml/qml/qqmlextensionplugin.cpp
@@ -42,8 +42,8 @@ QT_BEGIN_NAMESPACE
\fn void QQmlExtensionPlugin::registerTypes(const char *uri)
Registers the QML types in the given \a uri. Subclasses should implement
- this to call qmlRegisterType() for all types which are provided by the extension
- plugin.
+ this to call \l {QQmlEngine::}{qmlRegisterType()} for all types which are
+ provided by the extension plugin.
The \a uri is an identifier for the plugin generated by the QML engine
based on the name and path of the extension's plugin library.
@@ -166,8 +166,20 @@ void QQmlEngineExtensionPlugin::initializeEngine(QQmlEngine *engine, const char
\since 6.2
\relates QQmlEngineExtensionPlugin
- Ensures the plugin whose metadata-declaring class is named \a PluginName
- is linked into static builds.
+ Ensures the plugin whose metadata-declaring plugin extension class is named
+ \a PluginName is linked into static builds. For the modules created using
+ \l qt_add_qml_module, the default plugin extension class name is computed
+ from the QML module URI by replacing dots with underscores, unless the
+ \c CLASS_NAME argument is specified.
+
+ For example:
+ \badcode
+ qt_add_qml_module(myplugin
+ # The plugin extension class name in this case is my_Company_QmlComponents.
+ URI my.Company.QmlComponents
+ ...
+ )
+ \endcode
\sa Q_IMPORT_PLUGIN
*/
diff --git a/src/qml/qml/qqmlfile.cpp b/src/qml/qml/qqmlfile.cpp
index d616616ebd..bac70e69bb 100644
--- a/src/qml/qml/qqmlfile.cpp
+++ b/src/qml/qml/qqmlfile.cpp
@@ -10,18 +10,27 @@
#include <private/qqmlengine_p.h>
#include <private/qqmlglobal_p.h>
-/*!
-\class QQmlFile
-\brief The QQmlFile class gives access to local and remote files.
+QT_BEGIN_NAMESPACE
-\internal
+/*!
+ \class QQmlFile
+ \inmodule QtQml
+ \since 5.0
+ \brief The QQmlFile class provides static utility methods to categorize URLs.
-Supports file:// and qrc:/ uris and whatever QNetworkAccessManager supports.
+ QQmlFile provides some static utility methods to categorize URLs
+ and file names the way \l{QQmlEngine} does when loading content from them.
*/
-#define QQMLFILE_MAX_REDIRECT_RECURSION 16
+/*!
+ \internal
-QT_BEGIN_NAMESPACE
+ \enum QQmlFile::Status
+ \value Null
+ \value Ready
+ \value Error
+ \value Loading
+ */
static char qrc_string[] = "qrc";
static char file_string[] = "file";
@@ -64,7 +73,6 @@ private:
QQmlEngine *m_engine;
QQmlFilePrivate *m_p;
- int m_redirectCount;
QNetworkReply *m_reply;
};
#endif
@@ -99,7 +107,7 @@ int QQmlFileNetworkReply::replyFinishedIndex = -1;
int QQmlFileNetworkReply::replyDownloadProgressIndex = -1;
QQmlFileNetworkReply::QQmlFileNetworkReply(QQmlEngine *e, QQmlFilePrivate *p, const QUrl &url)
-: m_engine(e), m_p(p), m_redirectCount(0), m_reply(nullptr)
+: m_engine(e), m_p(p), m_reply(nullptr)
{
if (finishedIndex == -1) {
finishedIndex = QMetaMethod::fromSignal(&QQmlFileNetworkReply::finished).methodIndex();
@@ -133,27 +141,6 @@ QQmlFileNetworkReply::~QQmlFileNetworkReply()
void QQmlFileNetworkReply::networkFinished()
{
- ++m_redirectCount;
- if (m_redirectCount < QQMLFILE_MAX_REDIRECT_RECURSION) {
- QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
- if (redirect.isValid()) {
- QUrl url = m_reply->url().resolved(redirect.toUrl());
-
- QNetworkRequest req(url);
- req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
-
- m_reply->deleteLater();
- m_reply = m_engine->networkAccessManager()->get(req);
-
- QMetaObject::connect(m_reply, replyFinishedIndex,
- this, networkFinishedIndex);
- QMetaObject::connect(m_reply, replyDownloadProgressIndex,
- this, networkDownloadProgressIndex);
-
- return;
- }
- }
-
if (m_reply->error()) {
m_p->errorString = m_reply->errorString();
m_p->error = QQmlFilePrivate::Network;
@@ -183,22 +170,36 @@ QQmlFilePrivate::QQmlFilePrivate()
{
}
+/*!
+ \internal
+ */
QQmlFile::QQmlFile()
: d(new QQmlFilePrivate)
{
}
-QQmlFile::QQmlFile(QQmlEngine *e, const QUrl &url)
+/*!
+ \internal
+ Constructs a QQmlFile for content at \a url, using \a engine to retrieve it.
+ */
+QQmlFile::QQmlFile(QQmlEngine *engine, const QUrl &url)
: d(new QQmlFilePrivate)
{
- load(e, url);
+ load(engine, url);
}
-QQmlFile::QQmlFile(QQmlEngine *e, const QString &url)
- : QQmlFile(e, QUrl(url))
+/*!
+ \internal
+ Constructs a QQmlFile for content at \a url, using \a engine to retrieve it.
+ */
+QQmlFile::QQmlFile(QQmlEngine *engine, const QString &url)
+ : QQmlFile(engine, QUrl(url))
{
}
+/*!
+ \internal
+ */
QQmlFile::~QQmlFile()
{
#if QT_CONFIG(qml_network)
@@ -208,26 +209,41 @@ QQmlFile::~QQmlFile()
d = nullptr;
}
+/*!
+ \internal
+ */
bool QQmlFile::isNull() const
{
return status() == Null;
}
+/*!
+ \internal
+ */
bool QQmlFile::isReady() const
{
return status() == Ready;
}
+/*!
+ \internal
+ */
bool QQmlFile::isError() const
{
return status() == Error;
}
+/*!
+ \internal
+ */
bool QQmlFile::isLoading() const
{
return status() == Loading;
}
+/*!
+ \internal
+ */
QUrl QQmlFile::url() const
{
if (!d->urlString.isEmpty()) {
@@ -237,6 +253,9 @@ QUrl QQmlFile::url() const
return d->url;
}
+/*!
+ \internal
+ */
QQmlFile::Status QQmlFile::status() const
{
if (d->url.isEmpty() && d->urlString.isEmpty())
@@ -251,6 +270,9 @@ QQmlFile::Status QQmlFile::status() const
return Ready;
}
+/*!
+ \internal
+ */
QString QQmlFile::error() const
{
switch (d->error) {
@@ -264,21 +286,34 @@ QString QQmlFile::error() const
}
}
+/*!
+ \internal
+ */
qint64 QQmlFile::size() const
{
return d->data.size();
}
+/*!
+ \internal
+ */
const char *QQmlFile::data() const
{
return d->data.constData();
}
+/*!
+ \internal
+ */
QByteArray QQmlFile::dataByteArray() const
{
return d->data;
}
+/*!
+ \internal
+ Loads content at \a url using \a engine.
+ */
void QQmlFile::load(QQmlEngine *engine, const QUrl &url)
{
Q_ASSERT(engine);
@@ -309,6 +344,10 @@ void QQmlFile::load(QQmlEngine *engine, const QUrl &url)
}
}
+/*!
+ \internal
+ Loads content at \a url using \a engine.
+ */
void QQmlFile::load(QQmlEngine *engine, const QString &url)
{
Q_ASSERT(engine);
@@ -343,6 +382,9 @@ void QQmlFile::load(QQmlEngine *engine, const QString &url)
}
}
+/*!
+ \internal
+ */
void QQmlFile::clear()
{
d->url = QUrl();
@@ -351,12 +393,22 @@ void QQmlFile::clear()
d->error = QQmlFilePrivate::None;
}
-void QQmlFile::clear(QObject *)
+/*!
+ \internal
+ Redirects to the other clear() overload, ignoring \a object.
+ */
+void QQmlFile::clear(QObject *object)
{
+ Q_UNUSED(object);
clear();
}
#if QT_CONFIG(qml_network)
+
+/*!
+ \internal
+ Connects \a method of \a object to the internal \c{finished} signal.
+ */
bool QQmlFile::connectFinished(QObject *object, const char *method)
{
if (!d || !d->reply) {
@@ -364,10 +416,13 @@ bool QQmlFile::connectFinished(QObject *object, const char *method)
return false;
}
- return QObject::connect(d->reply, SIGNAL(finished()),
- object, method);
+ return QObject::connect(d->reply, SIGNAL(finished()), object, method);
}
+/*!
+ \internal
+ Connects \a method of \a object to the internal \c{finished} signal.
+ */
bool QQmlFile::connectFinished(QObject *object, int method)
{
if (!d || !d->reply) {
@@ -379,6 +434,10 @@ bool QQmlFile::connectFinished(QObject *object, int method)
object, method);
}
+/*!
+ \internal
+ Connects \a method of \a object to the internal \c{downloadProgress} signal.
+ */
bool QQmlFile::connectDownloadProgress(QObject *object, const char *method)
{
if (!d || !d->reply) {
@@ -390,6 +449,10 @@ bool QQmlFile::connectDownloadProgress(QObject *object, const char *method)
object, method);
}
+/*!
+ \internal
+ Connects \a method of \a object to the internal \c{downloadProgress} signal.
+ */
bool QQmlFile::connectDownloadProgress(QObject *object, int method)
{
if (!d || !d->reply) {
@@ -403,11 +466,14 @@ bool QQmlFile::connectDownloadProgress(QObject *object, int method)
#endif
/*!
-Returns true if QQmlFile will open \a url synchronously.
+ \internal
-Synchronous urls have a qrc:/ or file:// scheme.
+ Returns \c true if QQmlFile will open \a url synchronously.
+ Otherwise returns \c false. Synchronous urls have a \c{qrc:} or \c{file:}
+ scheme.
-\note On Android, urls with assets:/ scheme are also considered synchronous.
+ \note On Android, urls with \c{assets:} or \c{content:} scheme are also
+ considered synchronous.
*/
bool QQmlFile::isSynchronous(const QUrl &url)
{
@@ -430,11 +496,14 @@ bool QQmlFile::isSynchronous(const QUrl &url)
}
/*!
-Returns true if QQmlFile will open \a url synchronously.
+ \internal
-Synchronous urls have a qrc:/ or file:// scheme.
+ Returns \c true if QQmlFile will open \a url synchronously.
+ Otherwise returns \c false. Synchronous urls have a \c{qrc:} or \c{file:}
+ scheme.
-\note On Android, urls with assets:/ scheme are also considered synchronous.
+ \note On Android, urls with \c{assets:} or \c{content:} scheme are also
+ considered synchronous.
*/
bool QQmlFile::isSynchronous(const QString &url)
{
@@ -484,11 +553,12 @@ static bool hasLocalContentAuthority(const QUrl &url)
#endif
/*!
-Returns true if \a url is a local file that can be opened with QFile.
-
-Local file urls have either a qrc:/ or file:// scheme.
+ Returns \c true if \a url is a local file that can be opened with \l{QFile}.
+ Otherwise returns \c false. Local file urls have either a \c{qrc:} or
+ \c{file:} scheme.
-\note On Android, urls with assets:/ scheme are also considered local files.
+ \note On Android, urls with \c{assets:} or \c{content:} scheme are also
+ considered local files.
*/
bool QQmlFile::isLocalFile(const QUrl &url)
{
@@ -564,11 +634,12 @@ static bool hasLocalContentAuthority(const QString &url, qsizetype schemeLength)
#endif
/*!
-Returns true if \a url is a local file that can be opened with QFile.
-
-Local file urls have either a qrc: or file: scheme.
+ Returns \c true if \a url is a local file that can be opened with \l{QFile}.
+ Otherwise returns \c false. Local file urls have either a \c{qrc:} or
+ \c{file:} scheme.
-\note On Android, urls with assets: or content: scheme are also considered local files.
+ \note On Android, urls with \c{assets:} or \c{content:} scheme are also considered
+ local files.
*/
bool QQmlFile::isLocalFile(const QString &url)
{
@@ -609,8 +680,10 @@ bool QQmlFile::isLocalFile(const QString &url)
}
/*!
-If \a url is a local file returns a path suitable for passing to QFile. Otherwise returns an
-empty string.
+ If \a url is a local file returns a path suitable for passing to \l{QFile}.
+ Otherwise returns an empty string.
+
+ \sa isLocalFile
*/
QString QQmlFile::urlToLocalFileOrQrc(const QUrl& url)
{
@@ -661,8 +734,10 @@ static bool isDoubleSlashed(const QString &url, qsizetype offset)
}
/*!
-If \a url is a local file returns a path suitable for passing to QFile. Otherwise returns an
-empty string.
+ If \a url is a local file returns a path suitable for passing to \l{QFile}.
+ Otherwise returns an empty string.
+
+ \sa isLocalFile
*/
QString QQmlFile::urlToLocalFileOrQrc(const QString& url)
{
diff --git a/src/qml/qml/qqmlfile.h b/src/qml/qml/qqmlfile.h
index 3d51132bfd..af90e671a9 100644
--- a/src/qml/qml/qqmlfile.h
+++ b/src/qml/qml/qqmlfile.h
@@ -18,8 +18,8 @@ class Q_QML_EXPORT QQmlFile
{
public:
QQmlFile();
- QQmlFile(QQmlEngine *, const QUrl &);
- QQmlFile(QQmlEngine *, const QString &);
+ QQmlFile(QQmlEngine *engine, const QUrl &url);
+ QQmlFile(QQmlEngine *engine, const QString &url);
~QQmlFile();
enum Status { Null, Ready, Error, Loading };
@@ -42,7 +42,7 @@ public:
void load(QQmlEngine *, const QString &);
void clear();
- void clear(QObject *);
+ void clear(QObject *object);
#if QT_CONFIG(qml_network)
bool connectFinished(QObject *, const char *);
diff --git a/src/qml/qml/qqmlfileselector_p.h b/src/qml/qml/qqmlfileselector_p.h
index 31f18cc27e..58696acef7 100644
--- a/src/qml/qml/qqmlfileselector_p.h
+++ b/src/qml/qml/qqmlfileselector_p.h
@@ -21,11 +21,13 @@
#include <private/qobject_p.h>
#include <private/qtqmlglobal_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QFileSelector;
class QQmlFileSelectorInterceptor;
-class Q_QML_PRIVATE_EXPORT QQmlFileSelectorPrivate : public QObjectPrivate
+class Q_QML_EXPORT QQmlFileSelectorPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QQmlFileSelector)
public:
@@ -38,7 +40,7 @@ public:
QScopedPointer<QQmlFileSelectorInterceptor> myInstance;
};
-class Q_QML_PRIVATE_EXPORT QQmlFileSelectorInterceptor : public QQmlAbstractUrlInterceptor
+class Q_QML_EXPORT QQmlFileSelectorInterceptor : public QQmlAbstractUrlInterceptor
{
public:
QQmlFileSelectorInterceptor(QQmlFileSelectorPrivate* pd);
diff --git a/src/qml/qml/qqmlfinalizer_p.h b/src/qml/qml/qqmlfinalizer_p.h
index 57f5fdb61b..66fd85ddad 100644
--- a/src/qml/qml/qqmlfinalizer_p.h
+++ b/src/qml/qml/qqmlfinalizer_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlFinalizerHook
+class Q_QML_EXPORT QQmlFinalizerHook
{
public:
virtual ~QQmlFinalizerHook();
diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp
index 265b26b1e0..2e3fa5f86c 100644
--- a/src/qml/qml/qqmlglobal.cpp
+++ b/src/qml/qml/qqmlglobal.cpp
@@ -1,15 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include <private/qqmlglobal_p.h>
-#include <QtQml/private/qqmlmetatype_p.h>
#include <QtQml/private/qjsvalue_p.h>
-
+#include <QtQml/private/qqmlglobal_p.h>
+#include <QtQml/private/qqmlmetatype_p.h>
#include <QtQml/qqmlengine.h>
-#include <QtCore/qvariant.h>
-#include <QtCore/qstringlist.h>
+
+#include <QtCore/private/qvariant_p.h>
+#include <QtCore/qcoreapplication.h>
#include <QtCore/qdebug.h>
-#include <QtCore/QCoreApplication>
+#include <QtCore/qstringlist.h>
QT_BEGIN_NAMESPACE
@@ -70,43 +70,78 @@ static bool isConstructibleMetaType(const QMetaType metaType)
return true;
}
+static void *createVariantData(QMetaType type, QVariant *variant)
+{
+ const QtPrivate::QMetaTypeInterface *iface = type.iface();
+ QVariant::Private *d = &variant->data_ptr();
+ Q_ASSERT(d->is_null && !d->is_shared);
+ *d = QVariant::Private(iface);
+ if (QVariant::Private::canUseInternalSpace(iface))
+ return d->data.data;
+
+ // This is not exception safe.
+ // If your value type throws an exception from its ctor bad things will happen anyway.
+ d->data.shared = QVariant::PrivateShared::create(iface->size, iface->alignment);
+ d->is_shared = true;
+ return d->data.shared->data();
+}
+
static void callConstructor(
- const QMetaObject *mo, int i, void *parameter, QMetaType metaType, void *data)
+ const QMetaObject *targetMetaObject, int i, void *source, void *target)
{
- // Unfortunately CreateInstance unconditionally creates the instance on the heap.
- void *gadget = nullptr;
- void *p[] = { &gadget, parameter };
- mo->static_metacall(QMetaObject::CreateInstance, i, p);
- Q_ASSERT(gadget);
- metaType.destruct(data);
- metaType.construct(data, gadget);
- metaType.destroy(gadget);
+ void *p[] = { target, source };
+ targetMetaObject->static_metacall(QMetaObject::ConstructInPlace, i, p);
}
+template<typename Allocate>
+static void fromVerifiedType(
+ const QMetaObject *targetMetaObject, int ctorIndex, void *source, Allocate &&allocate)
+{
+ const QMetaMethod ctor = targetMetaObject->constructor(ctorIndex);
+ Q_ASSERT_X(ctor.parameterCount() == 1, "fromVerifiedType",
+ "Value type constructor must take exactly one argument");
+ callConstructor(targetMetaObject, ctorIndex, source, allocate());
+}
+
+
+template<typename Allocate, typename Retrieve>
static bool fromMatchingType(
- const QMetaObject *mo, const QV4::Value &s, const QMetaType metaType, void *data)
+ const QMetaObject *targetMetaObject, Allocate &&allocate, Retrieve &&retrieve)
{
- for (int i = 0, end = mo->constructorCount(); i < end; ++i) {
- const QMetaMethod ctor = mo->constructor(i);
+ for (int i = 0, end = targetMetaObject->constructorCount(); i < end; ++i) {
+ const QMetaMethod ctor = targetMetaObject->constructor(i);
if (ctor.parameterCount() != 1)
continue;
const QMetaType parameterType = ctor.parameterMetaType(0);
- QVariant parameter = QV4::ExecutionEngine::toVariant(s, parameterType);
- if (parameter.metaType() == parameterType) {
- callConstructor(mo, i, parameter.data(), metaType, data);
- return true;
- }
- QVariant converted(parameterType);
- if (QQmlValueTypeProvider::createValueType(s, parameterType, converted.data())) {
- callConstructor(mo, i, converted.data(), metaType, data);
- return true;
- }
+ if (retrieve(parameterType, [&](QMetaType sourceMetaType, void *sourceData) {
+ if (sourceMetaType == parameterType) {
+ callConstructor(targetMetaObject, i, sourceData, allocate());
+ return true;
+ }
+
+ if (const QMetaObject *parameterMetaObject = parameterType.metaObject()) {
+ if (const QMetaObject *sourceMetaObject = sourceMetaType.metaObject();
+ sourceMetaObject && sourceMetaObject->inherits(parameterMetaObject)) {
+ // Allow construction from derived types.
+ callConstructor(targetMetaObject, i, sourceData, allocate());
+ return true;
+ }
+ }
+
+ // Do not recursively try to create parameters here. This may end up in infinite recursion.
+
+ // At this point, s should be a builtin type. For builtin types
+ // the QMetaType converters are good enough.
+ QVariant converted(parameterType);
+ if (QMetaType::convert(sourceMetaType, sourceData, parameterType, converted.data())) {
+ callConstructor(targetMetaObject, i, converted.data(), allocate());
+ return true;
+ }
- if (QMetaType::convert(parameter.metaType(), parameter.constData(),
- parameterType, converted.data())) {
- callConstructor(mo, i, converted.data(), metaType, data);
+ return false;
+ })) {
return true;
}
}
@@ -114,37 +149,38 @@ static bool fromMatchingType(
return false;
}
+template<typename Allocate>
static bool fromMatchingType(
- const QMetaObject *mo, QVariant s, const QMetaType metaType, void *data)
+ const QMetaObject *targetMetaObject, const QV4::Value &source, Allocate &&allocate)
{
- const QMetaType sourceMetaType = s.metaType();
- if (sourceMetaType == QMetaType::fromType<QJSValue>()) {
- QJSValue val = s.value<QJSValue>();
- return fromMatchingType(
- mo, QV4::Value(QJSValuePrivate::asReturnedValue(&val)), metaType, data);
- }
+ return fromMatchingType(
+ targetMetaObject, std::forward<Allocate>(allocate),
+ [&](QMetaType parameterType, auto callback) {
+ QVariant variant = QV4::ExecutionEngine::toVariant(source, parameterType);
+ return callback(variant.metaType(), variant.data());
+ });
+}
+template<typename Allocate>
+static bool fromMatchingType(
+ const QMetaObject *targetMetaObject, QVariant source, Allocate &&allocate)
+{
+ return fromMatchingType(targetMetaObject, std::forward<Allocate>(allocate),
+ [&](QMetaType, auto callback) {
+ return callback(source.metaType(), source.data());
+ });
+}
+
+template<typename Allocate>
+static bool fromString(const QMetaObject *mo, QString s, Allocate &&allocate)
+{
for (int i = 0, end = mo->constructorCount(); i < end; ++i) {
const QMetaMethod ctor = mo->constructor(i);
if (ctor.parameterCount() != 1)
continue;
- const QMetaType parameterType = ctor.parameterMetaType(0);
- if (sourceMetaType == parameterType) {
- callConstructor(mo, i, s.data(), metaType, data);
- return true;
- }
-
- QVariant parameter(parameterType);
- if (QQmlValueTypeProvider::createValueType(s, parameterType, parameter.data())) {
- callConstructor(mo, i, parameter.data(), metaType, data);
- return true;
- }
-
- // At this point, s should be a builtin type. For builtin types
- // the QMetaType converters are good enough.
- if (QMetaType::convert(sourceMetaType, s.constData(), parameterType, parameter.data())) {
- callConstructor(mo, i, parameter.data(), metaType, data);
+ if (ctor.parameterMetaType(0) == QMetaType::fromType<QString>()) {
+ callConstructor(mo, i, &s, allocate());
return true;
}
}
@@ -152,39 +188,42 @@ static bool fromMatchingType(
return false;
}
-static bool fromString(
- const QMetaObject *mo, QString s, const QMetaType metaType, void *data)
+template<typename Get, typename Convert>
+static bool doWriteProperty(const QMetaProperty &metaProperty, void *target,
+ Get &&get, Convert &&convert)
{
- for (int i = 0, end = mo->constructorCount(); i < end; ++i) {
- const QMetaMethod ctor = mo->constructor(i);
- if (ctor.parameterCount() != 1)
- continue;
+ const QMetaType propertyType = metaProperty.metaType();
+ QVariant property = get(propertyType);
+ if (property.metaType() == propertyType) {
+ metaProperty.writeOnGadget(target, std::move(property));
+ return true;
+ }
- if (ctor.parameterMetaType(0) == QMetaType::fromType<QString>()) {
- callConstructor(mo, i, &s, metaType, data);
- return true;
- }
+ QVariant converted = convert(propertyType);
+ if (converted.isValid()) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ return true;
}
+ converted = QVariant(propertyType);
+ if (QMetaType::convert(property.metaType(), property.constData(),
+ propertyType, converted.data())) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ return true;
+ }
return false;
}
-static bool byProperties(
- const QMetaObject *mo, const QV4::Value &s, void *data)
+static void doWriteProperties(
+ const QMetaObject *targetMetaObject, void *target, const QV4::Value &source)
{
- if (!s.isObject())
- return false;
-
- if (!mo)
- return false;
-
- const QV4::Object *o = static_cast<const QV4::Object *>(&s);
+ const QV4::Object *o = static_cast<const QV4::Object *>(&source);
QV4::Scope scope(o->engine());
QV4::ScopedObject object(scope, o);
- for (int i = 0; i < mo->propertyCount(); ++i) {
- const QMetaProperty metaProperty = mo->property(i);
+ for (int i = 0; i < targetMetaObject->propertyCount(); ++i) {
+ const QMetaProperty metaProperty = targetMetaObject->property(i);
const QString propertyName = QString::fromUtf8(metaProperty.name());
QV4::ScopedString v4PropName(scope, scope.engine->newString(propertyName));
@@ -195,22 +234,31 @@ static bool byProperties(
if (v4PropValue->isUndefined())
continue;
+ if (doWriteProperty(metaProperty, target, [&](const QMetaType &propertyType) {
+ return QV4::ExecutionEngine::toVariant(v4PropValue, propertyType);
+ }, [&](const QMetaType &propertyType) {
+ return QQmlValueTypeProvider::createValueType(v4PropValue, propertyType);
+ })) {
+ continue;
+ }
+
const QMetaType propertyType = metaProperty.metaType();
QVariant property = QV4::ExecutionEngine::toVariant(v4PropValue, propertyType);
if (property.metaType() == propertyType) {
- metaProperty.writeOnGadget(data, property);
+ metaProperty.writeOnGadget(target, std::move(property));
continue;
}
- QVariant converted(propertyType);
- if (QQmlValueTypeProvider::createValueType(v4PropValue, propertyType, converted.data())) {
- metaProperty.writeOnGadget(data, converted);
+ QVariant converted = QQmlValueTypeProvider::createValueType(v4PropValue, propertyType);
+ if (converted.isValid()) {
+ metaProperty.writeOnGadget(target, std::move(converted));
continue;
}
+ converted = QVariant(propertyType);
if (QMetaType::convert(property.metaType(), property.constData(),
propertyType, converted.data())) {
- metaProperty.writeOnGadget(data, converted);
+ metaProperty.writeOnGadget(target, std::move(converted));
continue;
}
@@ -219,102 +267,449 @@ static bool byProperties(
.arg(v4PropValue->toQStringNoThrow(), QString::fromUtf8(propertyType.name()),
propertyName);
}
- return true;
}
-static bool byProperties(
- const QMetaObject *mo, const QVariant &s, void *data)
+static QVariant byProperties(
+ const QMetaObject *targetMetaObject, QMetaType metaType, const QV4::Value &source)
{
- if (!mo)
- return false;
+ if (!source.isObject() || !targetMetaObject)
+ return QVariant();
+
+ QVariant result(metaType);
+ doWriteProperties(targetMetaObject, result.data(), source);
+ return result;
+}
+
+template<typename Read>
+static void doWriteProperties(
+ const QMetaObject *targetMetaObject, void *target,
+ const QMetaObject *sourceMetaObject, Read &&read)
+{
+ for (int i = 0; i < targetMetaObject->propertyCount(); ++i) {
+ const QMetaProperty metaProperty = targetMetaObject->property(i);
+
+ const int sourceProperty = sourceMetaObject->indexOfProperty(metaProperty.name());
+
+ // We assume that data is freshly constructed.
+ // There is no point in reset()'ing properties of a freshly created object.
+ if (sourceProperty == -1)
+ continue;
+
+ const QMetaType propertyType = metaProperty.metaType();
+ QVariant property = read(sourceMetaObject, sourceProperty);
+ if (property.metaType() == propertyType) {
+ metaProperty.writeOnGadget(target, std::move(property));
+ continue;
+ }
+
+ QVariant converted = QQmlValueTypeProvider::createValueType(property, propertyType);
+ if (converted.isValid()) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ continue;
+ }
+
+ converted = QVariant(propertyType);
+ if (QMetaType::convert(property.metaType(), property.constData(),
+ propertyType, converted.data())) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ continue;
+ }
- if (s.metaType() == QMetaType::fromType<QJSValue>()) {
- QJSValue val = s.value<QJSValue>();
- return byProperties(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&val)), data);
+ qWarning().noquote()
+ << QLatin1String("Could not convert %1 to %2 for property %3")
+ .arg(property.toString(), QString::fromUtf8(propertyType.name()),
+ QString::fromUtf8(metaProperty.name()));
}
+}
- return false;
+
+static void doWriteProperties(const QMetaObject *targetMeta, void *target, QObject *source)
+{
+ doWriteProperties(
+ targetMeta, target, source->metaObject(),
+ [source](const QMetaObject *sourceMetaObject, int sourceProperty) {
+ return sourceMetaObject->property(sourceProperty).read(source);
+ });
}
-static bool fromJSValue(
- const QQmlType &type, const QJSValue &s, QMetaType metaType, void *data)
+static QVariant byProperties(
+ const QMetaObject *targetMetaObject, QMetaType targetMetaType, QObject *source)
{
- if (const auto valueTypeFunction = type.createValueTypeFunction()) {
- QVariant result = valueTypeFunction(s);
- if (result.metaType() == metaType) {
- metaType.destruct(data);
- metaType.construct(data, result.constData());
- return true;
+ if (!source || !targetMetaObject)
+ return QVariant();
+
+ QVariant result(targetMetaType);
+ doWriteProperties(targetMetaObject, result.data(), source);
+ return result;
+}
+
+static QVariant byProperties(
+ const QMetaObject *targetMetaObject, QMetaType targetMetaType,
+ const QMetaObject *sourceMetaObject, const void *source)
+{
+ if (!source || !sourceMetaObject || !targetMetaObject)
+ return QVariant();
+
+ QVariant result(targetMetaType);
+ doWriteProperties(
+ targetMetaObject, result.data(), sourceMetaObject,
+ [source](const QMetaObject *sourceMetaObject, int sourceProperty) {
+ return sourceMetaObject->property(sourceProperty).readOnGadget(source);
+ });
+ return result;
+}
+
+template<typename Map>
+void doWriteProperties(const QMetaObject *targetMetaObject, void *target, const Map &source)
+{
+ for (int i = 0; i < targetMetaObject->propertyCount(); ++i) {
+ const QMetaProperty metaProperty = targetMetaObject->property(i);
+
+ // We assume that data is freshly constructed.
+ // There is no point in reset()'ing properties of a freshly created object.
+ const auto it = source.constFind(QString::fromUtf8(metaProperty.name()));
+ if (it == source.constEnd())
+ continue;
+
+ const QMetaType propertyType = metaProperty.metaType();
+ QVariant property = *it;
+ if (property.metaType() == propertyType) {
+ metaProperty.writeOnGadget(target, std::move(property));
+ continue;
+ }
+
+ QVariant converted = QQmlValueTypeProvider::createValueType(property, propertyType);
+ if (converted.isValid()) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ continue;
}
+
+ converted = QVariant(propertyType);
+ if (QMetaType::convert(property.metaType(), property.constData(),
+ propertyType, converted.data())) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ continue;
+ }
+
+ qWarning().noquote()
+ << QLatin1String("Could not convert %1 to %2 for property %3")
+ .arg(property.toString(), QString::fromUtf8(propertyType.name()),
+ QString::fromUtf8(metaProperty.name()));
}
+}
- return false;
+template<typename Map>
+QVariant byProperties(
+ const QMetaObject *targetMetaObject, QMetaType targetMetaType, const Map &source)
+{
+ QVariant result(targetMetaType);
+ doWriteProperties(targetMetaObject, result.data(), source);
+ return result;
}
-bool QQmlValueTypeProvider::constructFromJSValue(
- const QJSValue &s, QMetaType metaType, void *data)
+static QVariant byProperties(
+ const QMetaObject *targetMetaObject, QMetaType targetMetaType, const QVariant &source)
{
- return isConstructibleMetaType(metaType)
- && fromJSValue(QQmlMetaType::qmlType(metaType), s, metaType, data);
+ if (!targetMetaObject)
+ return QVariant();
+
+ if (source.metaType() == QMetaType::fromType<QJSValue>()) {
+ QJSValue val = source.value<QJSValue>();
+ // Generally, the GC might collect a Value at any point so that
+ // a `ScopedValue` should be used.
+ // In this case, the Value is tied to a `QJSValue` which is
+ // persistent to the GC and thus the cast is safe.
+ return byProperties(
+ targetMetaObject, targetMetaType, QV4::Value(QJSValuePrivate::asReturnedValue(&val)));
+ }
+
+ if (source.metaType() == QMetaType::fromType<QVariantMap>()) {
+ return byProperties(
+ targetMetaObject, targetMetaType,
+ *static_cast<const QVariantMap *>(source.constData()));
+ }
+
+ if (source.metaType() == QMetaType::fromType<QVariantHash>()) {
+ return byProperties(
+ targetMetaObject, targetMetaType,
+ *static_cast<const QVariantHash *>(source.constData()));
+ }
+
+ if (source.metaType().flags() & QMetaType::PointerToQObject)
+ return byProperties(targetMetaObject, targetMetaType, source.value<QObject *>());
+
+ if (const QMetaObject *sourceMeta = QQmlMetaType::metaObjectForValueType(source.metaType()))
+ return byProperties(targetMetaObject, targetMetaType, sourceMeta, source.constData());
+
+ return QVariant();
}
-bool QQmlValueTypeProvider::createValueType(
- const QString &s, QMetaType metaType, void *data)
+template<typename Allocate, typename DefaultConstruct>
+bool createOrConstructValueType(
+ const QQmlType &targetType, const QV4::Value &source,
+ Allocate &&allocate, DefaultConstruct &&defaultConstruct)
{
- if (!isConstructibleMetaType(metaType))
- return false;
- const QQmlType type = QQmlMetaType::qmlType(metaType);
- const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type);
- if (mo && type.canConstructValueType()) {
- if (fromString(mo, s, metaType, data))
+ const auto warn = [&](const QMetaObject *targetMetaObject) {
+ qWarning().noquote()
+ << "Could not find any constructor for value type"
+ << targetMetaObject->className() << "to call with value"
+ << source.toQStringNoThrow();
+ };
+
+ if (targetType.canPopulateValueType()) {
+ if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) {
+ if (source.isObject()) {
+ doWriteProperties(targetMetaObject, defaultConstruct(), source);
+ return true;
+ }
+ if (targetType.canConstructValueType()) {
+ if (fromMatchingType(targetMetaObject, source, allocate))
+ return true;
+ warn(targetMetaObject);
+ }
+ }
+ } else if (targetType.canConstructValueType()) {
+ if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) {
+ if (fromMatchingType(targetMetaObject, source, allocate))
+ return true;
+ warn(targetMetaObject);
+ }
+ }
+
+ if (const auto valueTypeFunction = targetType.createValueTypeFunction()) {
+ const QVariant result
+ = valueTypeFunction(QJSValuePrivate::fromReturnedValue(source.asReturnedValue()));
+ const QMetaType resultType = result.metaType();
+ if (resultType == targetType.typeId()) {
+ resultType.construct(allocate(), result.constData());
return true;
+ }
}
- return fromJSValue(type, s, metaType, data);
+ return false;
}
-bool QQmlValueTypeProvider::createValueType(
- const QJSValue &s, QMetaType metaType, void *data)
+template<typename Allocate, typename DefaultConstruct>
+bool createOrConstructValueType(
+ const QQmlType &targetType, QMetaType sourceMetaType, void *source,
+ Allocate &&allocate, DefaultConstruct &&defaultConstruct)
{
- if (!isConstructibleMetaType(metaType))
- return false;
- const QQmlType type = QQmlMetaType::qmlType(metaType);
- if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) {
- if (type.canPopulateValueType()
- && byProperties(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&s)), data)) {
+
+ const auto warn = [&](const QMetaObject *targetMetaObject) {
+ qWarning().noquote()
+ << "Could not find any constructor for value type"
+ << targetMetaObject->className() << "to call with value" << source;
+ };
+
+ if (targetType.canPopulateValueType()) {
+ if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) {
+ if (const QMetaObject *sourceMetaObject
+ = QQmlMetaType::metaObjectForValueType(sourceMetaType)) {
+ doWriteProperties(
+ targetMetaObject, defaultConstruct(), sourceMetaObject,
+ [&source](const QMetaObject *sourceMetaObject, int sourceProperty) {
+ return sourceMetaObject->property(sourceProperty).readOnGadget(source);
+ });
return true;
+ }
+
+ if (sourceMetaType == QMetaType::fromType<QVariantMap>()) {
+ doWriteProperties(
+ targetMetaObject, defaultConstruct(),
+ *static_cast<const QVariantMap *>(source));
+ return true;
+ }
+
+ if (sourceMetaType == QMetaType::fromType<QVariantHash>()) {
+ doWriteProperties(
+ targetMetaObject, defaultConstruct(),
+ *static_cast<const QVariantHash *>(source));
+ return true;
+ }
+
+ if (sourceMetaType.flags() & QMetaType::PointerToQObject) {
+ doWriteProperties(
+ targetMetaObject, defaultConstruct(),
+ *static_cast<QObject *const *>(source));
+ return true;
+ }
}
+ }
- if (type.canConstructValueType()
- && fromMatchingType(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&s)),
- metaType, data)) {
+ if (targetType.canConstructValueType()) {
+ if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) {
+ if (fromMatchingType(targetMetaObject, std::forward<Allocate>(allocate),
+ [&](QMetaType, auto callback) {
+ return callback(sourceMetaType, source);
+ })) {
return true;
+ }
+ warn(targetMetaObject);
}
}
- return fromJSValue(type, s, metaType, data);
+ return false;
}
-bool QQmlValueTypeProvider::createValueType(
- const QV4::Value &s, QMetaType metaType, void *data)
+/*!
+ * \internal
+ * Populate the value type in place at \a target, which is expected to be
+ * allocated and default-constructed, for example the result of a QVariant(QMetaType).
+ * This is efficient if we can do byProperties() since it can use the pre-constructed object.
+ * It also avoids the creation of a QVariant in most cases. It is not
+ * efficient if you're going to create a QVariant anyway.
+ */
+bool QQmlValueTypeProvider::populateValueType(
+ QMetaType targetMetaType, void *target, QMetaType sourceMetaType, void *source)
{
- if (!isConstructibleMetaType(metaType))
+ if (sourceMetaType == QMetaType::fromType<QJSValue>()) {
+ const QJSValue *val = static_cast<const QJSValue *>(source);
+ // Generally, the GC might collect a Value at any point so that
+ // a `ScopedValue` should be used.
+ // In this case, the Value is tied to a `QJSValue` which is
+ // persistent to the GC and thus the cast is safe.
+ return populateValueType(
+ targetMetaType, target, QV4::Value(QJSValuePrivate::asReturnedValue(val)));
+ }
+
+ if (!isConstructibleMetaType(targetMetaType))
return false;
+
+ return createOrConstructValueType(
+ QQmlMetaType::qmlType(targetMetaType), sourceMetaType, source,
+ [targetMetaType, target]() {
+ targetMetaType.destruct(target);
+ return target;
+ }, [target]() {
+ return target;
+ });
+}
+
+/*!
+ * \internal
+ * Populate the value type in place at \a target, which is expected to be
+ * allocated and default-constructed, for example the result of a QVariant(QMetaType).
+ * This is efficient if we can do byProperties() since it can use the pre-constructed object.
+ * It also avoids the creation of a QVariant in most cases. It is not
+ * efficient if you're going to create a QVariant anyway.
+ */
+bool QQmlValueTypeProvider::populateValueType(
+ QMetaType targetMetaType, void *target, const QV4::Value &source)
+{
+ if (!isConstructibleMetaType(targetMetaType))
+ return false;
+
+ return createOrConstructValueType(
+ QQmlMetaType::qmlType(targetMetaType), source, [targetMetaType, target]() {
+ targetMetaType.destruct(target);
+ return target;
+ }, [target]() {
+ return target;
+ });
+}
+
+/*!
+ * \internal
+ * Specialization that constructs the value type on the heap using new and returns a pointer to it.
+ */
+void *QQmlValueTypeProvider::heapCreateValueType(
+ const QQmlType &targetType, const QV4::Value &source)
+{
+ void *target = nullptr;
+ if (createOrConstructValueType(
+ targetType, source, [&]() {
+ const QMetaType metaType = targetType.typeId();
+ const ushort align = metaType.alignOf();
+ target = align > __STDCPP_DEFAULT_NEW_ALIGNMENT__
+ ? operator new(metaType.sizeOf(), std::align_val_t(align))
+ : operator new(metaType.sizeOf());
+ return target;
+ }, [&]() {
+ target = targetType.typeId().create();
+ return target;
+ })) {
+ Q_ASSERT(target != nullptr);
+ }
+
+ return target;
+}
+
+QVariant QQmlValueTypeProvider::constructValueType(
+ QMetaType targetMetaType, const QMetaObject *targetMetaObject,
+ int ctorIndex, void *ctorArg)
+{
+ QVariant result;
+ fromVerifiedType(targetMetaObject, ctorIndex, ctorArg,
+ [&]() { return createVariantData(targetMetaType, &result); });
+ return result;
+}
+
+static QVariant fromJSValue(const QQmlType &type, const QJSValue &s, QMetaType metaType)
+{
+ if (const auto valueTypeFunction = type.createValueTypeFunction()) {
+ const QVariant result = valueTypeFunction(s);
+ if (result.metaType() == metaType)
+ return result;
+ }
+
+ return QVariant();
+}
+
+QVariant QQmlValueTypeProvider::createValueType(const QJSValue &s, QMetaType metaType)
+{
+ if (!isConstructibleMetaType(metaType))
+ return QVariant();
+ return fromJSValue(QQmlMetaType::qmlType(metaType), s, metaType);
+}
+
+QVariant QQmlValueTypeProvider::createValueType(const QString &s, QMetaType metaType)
+{
+ if (!isConstructibleMetaType(metaType))
+ return QVariant();
const QQmlType type = QQmlMetaType::qmlType(metaType);
- if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) {
- if (type.canPopulateValueType() && byProperties(mo, s, data))
- return true;
- if (type.canConstructValueType()) {
- if (fromMatchingType(mo, s, metaType, data))
- return true;
- qWarning().noquote()
- << "Could not find any constructor for value type"
- << mo->className() << "to call with value" << s.toQStringNoThrow();
+ if (type.canConstructValueType()) {
+ if (const QMetaObject *mo = type.metaObjectForValueType()) {
+ QVariant result;
+ if (fromString(mo, s, [&]() { return createVariantData(metaType, &result); }))
+ return result;
}
}
- return fromJSValue(
- type, QJSValuePrivate::fromReturnedValue(s.asReturnedValue()), metaType, data);
+ return fromJSValue(type, s, metaType);
+}
+
+QVariant QQmlValueTypeProvider::createValueType(const QV4::Value &s, QMetaType metaType)
+{
+ if (!isConstructibleMetaType(metaType))
+ return QVariant();
+ const QQmlType type = QQmlMetaType::qmlType(metaType);
+ const auto warn = [&](const QMetaObject *mo) {
+ qWarning().noquote()
+ << "Could not find any constructor for value type"
+ << mo->className() << "to call with value" << s.toQStringNoThrow();
+ };
+
+ if (type.canPopulateValueType()) {
+ if (const QMetaObject *mo = type.metaObject()) {
+ QVariant result = byProperties(mo, metaType, s);
+ if (result.isValid())
+ return result;
+ if (type.canConstructValueType()) {
+ if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); }))
+ return result;
+ warn(mo);
+ }
+ }
+ } else if (type.canConstructValueType()) {
+ if (const QMetaObject *mo = type.metaObject()) {
+ QVariant result;
+ if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); }))
+ return result;
+ warn(mo);
+ }
+ }
+
+ return fromJSValue(type, QJSValuePrivate::fromReturnedValue(s.asReturnedValue()), metaType);
}
@@ -322,25 +717,38 @@ bool QQmlValueTypeProvider::createValueType(
* \internal
* This should only be called with either builtin types or wrapped QJSValues as source.
*/
-bool QQmlValueTypeProvider::createValueType(
- const QVariant &s, QMetaType metaType, void *data)
+QVariant QQmlValueTypeProvider::createValueType(const QVariant &s, QMetaType metaType)
{
if (!isConstructibleMetaType(metaType))
- return false;
+ return QVariant();
const QQmlType type = QQmlMetaType::qmlType(metaType);
- if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) {
- if (type.canPopulateValueType() && byProperties(mo, s, data))
- return true;
- if (type.canConstructValueType()) {
- if (fromMatchingType(mo, s, metaType, data))
- return true;
- qWarning().noquote()
- << "Could not find any constructor for value type"
- << mo->className() << "to call with value" << s;
+ const auto warn = [&](const QMetaObject *mo) {
+ qWarning().noquote()
+ << "Could not find any constructor for value type"
+ << mo->className() << "to call with value" << s;
+ };
+
+ if (type.canPopulateValueType()) {
+ if (const QMetaObject *mo = type.metaObjectForValueType()) {
+ QVariant result = byProperties(mo, metaType, s);
+ if (result.isValid())
+ return result;
+ if (type.canConstructValueType()) {
+ if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); }))
+ return result;
+ warn(mo);
+ }
+ }
+ } else if (type.canConstructValueType()) {
+ if (const QMetaObject *mo = type.metaObjectForValueType()) {
+ QVariant result;
+ if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); }))
+ return result;
+ warn(mo);
}
}
- return false;
+ return QVariant();
}
QQmlColorProvider::~QQmlColorProvider() {}
@@ -359,7 +767,7 @@ QVariant QQmlColorProvider::tint(const QVariant &, const QVariant &) { return QV
static QQmlColorProvider *colorProvider = nullptr;
-Q_QML_PRIVATE_EXPORT QQmlColorProvider *QQml_setColorProvider(QQmlColorProvider *newProvider)
+Q_QML_EXPORT QQmlColorProvider *QQml_setColorProvider(QQmlColorProvider *newProvider)
{
QQmlColorProvider *old = colorProvider;
colorProvider = newProvider;
@@ -413,7 +821,7 @@ QString QQmlGuiProvider::pluginName() const { return QString(); }
static QQmlGuiProvider *guiProvider = nullptr;
-Q_QML_PRIVATE_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *newProvider)
+Q_QML_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *newProvider)
{
QQmlGuiProvider *old = guiProvider;
guiProvider = newProvider;
@@ -438,18 +846,8 @@ Q_AUTOTEST_EXPORT QQmlGuiProvider *QQml_guiProvider(void)
//Docs in qqmlengine.cpp
QQmlApplication::QQmlApplication(QObject *parent)
- : QObject(*(new QQmlApplicationPrivate),parent)
+ : QQmlApplication(*(new QQmlApplicationPrivate), parent)
{
- connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()),
- this, SIGNAL(aboutToQuit()));
- connect(QCoreApplication::instance(), SIGNAL(applicationNameChanged()),
- this, SIGNAL(nameChanged()));
- connect(QCoreApplication::instance(), SIGNAL(applicationVersionChanged()),
- this, SIGNAL(versionChanged()));
- connect(QCoreApplication::instance(), SIGNAL(organizationNameChanged()),
- this, SIGNAL(organizationChanged()));
- connect(QCoreApplication::instance(), SIGNAL(organizationDomainChanged()),
- this, SIGNAL(domainChanged()));
}
QQmlApplication::QQmlApplication(QQmlApplicationPrivate &dd, QObject *parent)
@@ -517,6 +915,62 @@ void QQmlApplication::setDomain(const QString &arg)
QCoreApplication::instance()->setOrganizationDomain(arg);
}
+static const QQmlData *ddata_for_cast(QObject *object)
+{
+ Q_ASSERT(object);
+ auto ddata = QQmlData::get(object, false);
+ return (ddata && ddata->propertyCache) ? ddata : nullptr;
+}
+
+bool qmlobject_can_cpp_cast(QObject *object, const QMetaObject *mo)
+{
+ Q_ASSERT(mo);
+ if (const QQmlData *ddata = ddata_for_cast(object))
+ return ddata->propertyCache->firstCppMetaObject()->inherits(mo);
+ return object->metaObject()->inherits(mo);
+}
+
+bool qmlobject_can_qml_cast(QObject *object, const QQmlType &type)
+{
+ Q_ASSERT(type.isValid());
+
+ // A non-composite type will always have a metaobject.
+ const QMetaObject *typeMetaObject = type.metaObject();
+ const QQmlPropertyCache::ConstPtr typePropertyCache = typeMetaObject
+ ? QQmlPropertyCache::ConstPtr()
+ : QQmlMetaType::findPropertyCacheInCompositeTypes(type.typeId());
+
+ if (const QQmlData *ddata = ddata_for_cast(object)) {
+ for (const QQmlPropertyCache *propertyCache = ddata->propertyCache.data(); propertyCache;
+ propertyCache = propertyCache->parent().data()) {
+
+ if (typeMetaObject) {
+ // Prefer the metaobject inheritance mechanism, since it is more accurate.
+ //
+ // Assume the object can be casted to the type. Then, if we have a type metaobject,
+ // the object's property cache inheritance has to contain it. Otherwise we would
+ // end up with diverging metaobject hierarchies if we created the object's
+ // metaobject. This would be a disaster.
+ if (const QMetaObject *objectMetaObject = propertyCache->metaObject())
+ return objectMetaObject->inherits(typeMetaObject);
+ } else {
+ // This is a best effort attempt. There are a number of ways for the
+ // property caches to be unrelated but the types still convertible.
+ // Multiple property caches can hold the same metaobject, for example for
+ // versions of non-composite types.
+ if (propertyCache == typePropertyCache.data())
+ return true;
+ }
+ }
+ }
+
+ // If nothing else works, we have to create the metaobjects.
+
+ return object->metaObject()->inherits(typeMetaObject
+ ? typeMetaObject
+ : (typePropertyCache ? typePropertyCache->createMetaObject() : nullptr));
+}
+
QT_END_NAMESPACE
#include "moc_qqmlglobal_p.cpp"
diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h
index 918f95993f..5aa2f3ee6f 100644
--- a/src/qml/qml/qqmlglobal_p.h
+++ b/src/qml/qml/qqmlglobal_p.h
@@ -16,8 +16,9 @@
//
#include <private/qmetaobject_p.h>
-#include <private/qtqmlglobal_p.h>
#include <private/qqmlmetaobject_p.h>
+#include <private/qqmltype_p.h>
+#include <private/qtqmlglobal_p.h>
#include <QtQml/qqml.h>
#include <QtCore/qobject.h>
@@ -48,7 +49,7 @@ T qmlGetConfigOption(const char *var)
Connect \a Signal of \a Sender to \a Method of \a Receiver. \a Signal must be
of type \a SenderType and \a Receiver of type \a ReceiverType.
- Unlike QObject::connect(), this method caches the lookup of the signal and method
+ Unlike QObject::connect(), this macro caches the lookup of the signal and method
indexes. It also does not require lazy QMetaObjects to be built so should be
preferred in all QML code that might interact with QML built objects.
@@ -87,7 +88,7 @@ do { \
Disconnect \a Signal of \a Sender from \a Method of \a Receiver. \a Signal must be
of type \a SenderType and \a Receiver of type \a ReceiverType.
- Unlike QObject::disconnect(), this method caches the lookup of the signal and method
+ Unlike QObject::disconnect(), this macro caches the lookup of the signal and method
indexes. It also does not require lazy QMetaObjects to be built so should be
preferred in all QML code that might interact with QML built objects.
@@ -122,6 +123,9 @@ do { \
QMetaObject::disconnect(sender, signalIdx, receiver, methodIdx); \
} while (0)
+Q_QML_EXPORT bool qmlobject_can_cpp_cast(QObject *object, const QMetaObject *mo);
+Q_QML_EXPORT bool qmlobject_can_qml_cast(QObject *object, const QQmlType &type);
+
/*!
This method is identical to qobject_cast<T>() except that it does not require lazy
QMetaObjects to be built, so should be preferred in all QML code that might interact
@@ -137,12 +141,25 @@ do { \
template<class T>
T qmlobject_cast(QObject *object)
{
- if (object && QQmlMetaObject::canConvert(object, &reinterpret_cast<T>(object)->staticMetaObject))
+ if (!object)
+ return nullptr;
+ if (qmlobject_can_cpp_cast(object, &(std::remove_pointer_t<T>::staticMetaObject)))
return static_cast<T>(object);
else
return nullptr;
}
+class QQuickItem;
+template<>
+inline QQuickItem *qmlobject_cast<QQuickItem *>(QObject *object)
+{
+ if (!object || !object->isQuickItemType())
+ return nullptr;
+ // QQuickItem is incomplete here -> can't use static_cast
+ // but we don't need any pointer adjustment, so reinterpret is safe
+ return reinterpret_cast<QQuickItem *>(object);
+}
+
#define IS_SIGNAL_CONNECTED(Sender, SenderType, Name, Arguments) \
do { \
QObject *sender = (Sender); \
@@ -189,15 +206,24 @@ inline void QQml_setParent_noEvent(QObject *object, QObject *parent)
class QQmlValueTypeProvider
{
public:
- static bool constructFromJSValue(const QJSValue &, QMetaType, void *);
-
- static bool createValueType(const QString &, QMetaType, void *);
- static bool createValueType(const QJSValue &, QMetaType, void *);
- static bool createValueType(const QV4::Value &, QMetaType, void *);
- static bool createValueType(const QVariant &, QMetaType, void *);
+ static bool populateValueType(
+ QMetaType targetMetaType, void *target, const QV4::Value &source);
+ static bool populateValueType(
+ QMetaType targetMetaType, void *target, QMetaType sourceMetaType, void *source);
+
+ static Q_QML_EXPORT void *heapCreateValueType(
+ const QQmlType &targetType, const QV4::Value &source);
+ static QVariant constructValueType(
+ QMetaType targetMetaType, const QMetaObject *targetMetaObject,
+ int ctorIndex, void *ctorArg);
+
+ static QVariant createValueType(const QJSValue &, QMetaType);
+ static QVariant createValueType(const QString &, QMetaType);
+ static QVariant createValueType(const QV4::Value &, QMetaType);
+ static QVariant createValueType(const QVariant &, QMetaType);
};
-class Q_QML_PRIVATE_EXPORT QQmlColorProvider
+class Q_QML_EXPORT QQmlColorProvider
{
public:
virtual ~QQmlColorProvider();
@@ -213,11 +239,11 @@ public:
virtual QVariant tint(const QVariant &, const QVariant &);
};
-Q_QML_PRIVATE_EXPORT QQmlColorProvider *QQml_setColorProvider(QQmlColorProvider *);
-Q_QML_PRIVATE_EXPORT QQmlColorProvider *QQml_colorProvider();
+Q_QML_EXPORT QQmlColorProvider *QQml_setColorProvider(QQmlColorProvider *);
+Q_QML_EXPORT QQmlColorProvider *QQml_colorProvider();
class QQmlApplication;
-class Q_QML_PRIVATE_EXPORT QQmlGuiProvider
+class Q_QML_EXPORT QQmlGuiProvider
{
public:
virtual ~QQmlGuiProvider();
@@ -229,12 +255,12 @@ public:
virtual QString pluginName() const;
};
-Q_QML_PRIVATE_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *);
+Q_QML_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *);
Q_AUTOTEST_EXPORT QQmlGuiProvider *QQml_guiProvider();
class QQmlApplicationPrivate;
-class Q_QML_PRIVATE_EXPORT QQmlApplication : public QObject
+class Q_QML_EXPORT QQmlApplication : public QObject
{
//Application level logic, subclassed by Qt Quick if available via QQmlGuiProvider
Q_OBJECT
@@ -244,7 +270,6 @@ class Q_QML_PRIVATE_EXPORT QQmlApplication : public QObject
Q_PROPERTY(QString organization READ organization WRITE setOrganization NOTIFY organizationChanged)
Q_PROPERTY(QString domain READ domain WRITE setDomain NOTIFY domainChanged)
QML_ANONYMOUS
- QML_ADDED_IN_VERSION(2, 0)
public:
QQmlApplication(QObject* parent=nullptr);
diff --git a/src/qml/qml/qqmlguard_p.h b/src/qml/qml/qqmlguard_p.h
index 3486b4f74f..685293868e 100644
--- a/src/qml/qml/qqmlguard_p.h
+++ b/src/qml/qml/qqmlguard_p.h
@@ -28,8 +28,10 @@ public:
inline QQmlGuardImpl();
inline QQmlGuardImpl(QObject *);
inline QQmlGuardImpl(const QQmlGuardImpl &);
+protected:
inline ~QQmlGuardImpl();
+public: // ### make so it can be private
QObject *o = nullptr;
QQmlGuardImpl *next = nullptr;
QQmlGuardImpl **prev = nullptr;
@@ -48,10 +50,10 @@ class QQmlGuard : protected QQmlGuardImpl
{
friend class QQmlData;
public:
- inline QQmlGuard();
- inline QQmlGuard(ObjectDestroyedFn objectDestroyed, T *);
- inline QQmlGuard(T *);
- inline QQmlGuard(const QQmlGuard<T> &);
+ Q_NODISCARD_CTOR inline QQmlGuard();
+ Q_NODISCARD_CTOR inline QQmlGuard(ObjectDestroyedFn objectDestroyed, T *);
+ Q_NODISCARD_CTOR inline QQmlGuard(T *);
+ Q_NODISCARD_CTOR inline QQmlGuard(const QQmlGuard<T> &);
inline QQmlGuard<T> &operator=(const QQmlGuard<T> &o);
inline QQmlGuard<T> &operator=(T *);
@@ -72,10 +74,10 @@ public:
* We save it in objectDestroyFn to save space
* (implemented in qqmlengine.cpp)
*/
-void Q_QML_PRIVATE_EXPORT hasJsOwnershipIndicator(QQmlGuardImpl *);
+void Q_QML_EXPORT hasJsOwnershipIndicator(QQmlGuardImpl *);
template <typename T>
-class QQmlStrongJSQObjectReference : protected QQmlGuardImpl
+class QQmlStrongJSQObjectReference final : protected QQmlGuardImpl
{
public:
T *object() const noexcept { return static_cast<T *>(o); }
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index 98d9aee61b..217cb44669 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -85,12 +85,6 @@ QTypeRevision relevantVersion(const QString &uri, QTypeRevision version)
return QQmlMetaType::latestModuleVersion(uri).isValid() ? version : QTypeRevision();
}
-QTypeRevision validVersion(QTypeRevision version = QTypeRevision())
-{
- // If the given version is invalid, return a valid but useless version to signal "It's OK".
- return version.isValid() ? version : QTypeRevision::fromMinorVersion(0);
-}
-
QQmlError moduleNotFoundError(const QString &uri, QTypeRevision version)
{
QQmlError error;
@@ -203,6 +197,12 @@ bool isPathAbsolute(const QString &path)
\internal
*/
+QTypeRevision QQmlImports::validVersion(QTypeRevision version)
+{
+ // If the given version is invalid, return a valid but useless version to signal "It's OK".
+ return version.isValid() ? version : QTypeRevision::fromMinorVersion(0);
+}
+
/*!
Sets the base URL to be used for all relative file imports added.
*/
@@ -450,8 +450,8 @@ QString QQmlImports::versionString(QTypeRevision version, ImportVersion versionM
\sa addFileImport(), addLibraryImport
*/
bool QQmlImports::resolveType(
- const QHashedStringRef &type, QQmlType *type_return, QTypeRevision *version_return,
- QQmlImportNamespace **ns_return, QList<QQmlError> *errors,
+ QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QQmlType *type_return,
+ QTypeRevision *version_return, QQmlImportNamespace **ns_return, QList<QQmlError> *errors,
QQmlType::RegistrationType registrationType, bool *typeRecursionDetected) const
{
QQmlImportNamespace *ns = findQualifiedNamespace(type);
@@ -461,7 +461,7 @@ bool QQmlImports::resolveType(
return true;
}
if (type_return) {
- if (resolveType(type, version_return, type_return, errors, registrationType,
+ if (resolveType(typeLoader, type, version_return, type_return, errors, registrationType,
typeRecursionDetected)) {
if (lcQmlImport().isDebugEnabled()) {
#define RESOLVE_TYPE_DEBUG qCDebug(lcQmlImport) \
@@ -489,18 +489,8 @@ bool QQmlImportInstance::setQmldirContent(const QString &resolvedUrl,
const QQmlTypeLoaderQmldirContent &qmldir,
QQmlImportNamespace *nameSpace, QList<QQmlError> *errors)
{
-
- const QString preferredPath = qmldir.preferredPath();
- if (preferredPath.isEmpty()) {
- Q_ASSERT(resolvedUrl.endsWith(Slash));
- url = resolvedUrl;
- } else {
- Q_ASSERT(preferredPath.endsWith(Slash));
- if (preferredPath.startsWith(u':'))
- url = QStringLiteral("qrc") + preferredPath;
- else
- url = QUrl::fromLocalFile(preferredPath).toString();
- }
+ Q_ASSERT(resolvedUrl.endsWith(Slash));
+ url = resolvedUrl;
qmlDirComponents = qmldir.components();
@@ -537,8 +527,8 @@ QQmlDirScripts QQmlImportInstance::getVersionedScripts(const QQmlDirScripts &qml
&& (!version.hasMinorVersion()
|| (sit->version.minorVersion() <= version.minorVersion()))) {
// Load the highest version that matches
- QMap<QString, QQmlDirParser::Script>::iterator vit = versioned.find(sit->nameSpace);
- if (vit == versioned.end()
+ const auto vit = versioned.constFind(sit->nameSpace);
+ if (vit == versioned.cend()
|| (vit->version.minorVersion() < sit->version.minorVersion())) {
versioned.insert(sit->nameSpace, *sit);
}
@@ -582,37 +572,14 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
bool ret = uri == typeStr;
if (ret) {
Q_ASSERT(!type_return->isValid());
- auto createICType = [&]() {
- auto typePriv = new QQmlTypePrivate {QQmlType::RegistrationType::InlineComponentType};
- bool ok = false;
- typePriv->extraData.id->objectId = QUrl(this->url).fragment().toInt(&ok);
- Q_ASSERT(ok);
- typePriv->extraData.id->url = QUrl(this->url);
- auto icType = QQmlType(typePriv);
- typePriv->release();
- return icType;
- };
- if (containingType.isValid()) {
- // we currently cannot reference a Singleton inside itself
- // in that case, containingType is still invalid
- if (int icID = containingType.lookupInlineComponentIdByName(typeStr); icID != -1) {
- *type_return = containingType.lookupInlineComponentById(icID);
- } else {
- auto icType = createICType();
- int placeholderId = containingType.generatePlaceHolderICId();
- const_cast<QQmlImportInstance*>(this)->containingType.associateInlineComponent(typeStr, placeholderId, CompositeMetaTypeIds {}, icType);
- *type_return = QQmlType(icType);
- }
- } else {
- *type_return = createICType();
- }
+ *type_return = QQmlMetaType::fetchOrCreateInlineComponentTypeForUrl(QUrl(url));
}
return ret;
}
QQmlDirComponents::ConstIterator it = qmlDirComponents.find(typeStr), end = qmlDirComponents.end();
if (it != end) {
QString componentUrl;
- bool isCompositeSingleton = false;
+ QQmlMetaType::CompositeTypeLookupMode lookupMode = QQmlMetaType::NonSingleton;
QQmlDirComponents::ConstIterator candidate = end;
for ( ; it != end && it.key() == typeStr; ++it) {
const QQmlDirParser::Component &c = *it;
@@ -657,7 +624,7 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
// This is our best candidate so far
candidate = it;
- isCompositeSingleton = c.singleton;
+ lookupMode = c.singleton ? QQmlMetaType::Singleton : QQmlMetaType::NonSingleton;
}
}
}
@@ -665,7 +632,7 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
if (candidate != end) {
if (!base) // ensure we have a componentUrl
componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName);
- QQmlType returnType = QQmlMetaType::typeForUrl(componentUrl, type, isCompositeSingleton,
+ QQmlType returnType = QQmlMetaType::typeForUrl(componentUrl, type, lookupMode,
nullptr, candidate->version);
if (version_return)
*version_return = candidate->version;
@@ -714,7 +681,10 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
*typeRecursionDetected = recursion;
if (recursionRestriction == QQmlImport::AllowRecursion || !recursion) {
QQmlType returnType = QQmlMetaType::typeForUrl(
- qmlUrl, type, registrationType == QQmlType::CompositeSingletonType, errors);
+ qmlUrl, type, registrationType == QQmlType::CompositeSingletonType
+ ? QQmlMetaType::Singleton
+ : QQmlMetaType::NonSingleton,
+ errors);
if (type_return)
*type_return = returnType;
return returnType.isValid();
@@ -726,16 +696,16 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
}
bool QQmlImports::resolveType(
- const QHashedStringRef &type, QTypeRevision *version_return, QQmlType *type_return,
- QList<QQmlError> *errors, QQmlType::RegistrationType registrationType,
- bool *typeRecursionDetected) const
+ QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QTypeRevision *version_return,
+ QQmlType *type_return, QList<QQmlError> *errors,
+ QQmlType::RegistrationType registrationType, bool *typeRecursionDetected) const
{
const QVector<QHashedStringRef> splitName = type.split(Dot);
auto resolveTypeInNamespace = [&](
QHashedStringRef unqualifiedtype, QQmlImportNamespace *nameSpace,
QList<QQmlError> *errors) -> bool {
if (nameSpace->resolveType(
- m_typeLoader, unqualifiedtype, version_return, type_return, &m_base, errors,
+ typeLoader, unqualifiedtype, version_return, type_return, &m_base, errors,
registrationType, typeRecursionDetected))
return true;
if (nameSpace->imports.size() == 1
@@ -746,7 +716,7 @@ bool QQmlImports::resolveType(
*type_return = QQmlMetaType::typeForUrl(
resolveLocalUrl(nameSpace->imports.at(0)->url,
unqualifiedtype.toString() + QLatin1String(".qml")),
- type, false, errors);
+ type, QQmlMetaType::NonSingleton, errors);
return type_return->isValid();
}
return false;
@@ -765,23 +735,8 @@ bool QQmlImports::resolveType(
} else {
if (resolveTypeInNamespace(splitName.at(0), &m_unqualifiedset, nullptr)) {
// either simple type + inline component
- auto const icName = splitName.at(1).toString();
- auto objectIndex = type_return->lookupInlineComponentIdByName(icName);
- if (objectIndex != -1) {
- *type_return = type_return->lookupInlineComponentById(objectIndex);
- } else {
- auto icTypePriv = new QQmlTypePrivate(QQmlType::RegistrationType::InlineComponentType);
- icTypePriv->setContainingType(type_return);
- icTypePriv->extraData.id->url = type_return->sourceUrl();
- int placeholderId = type_return->generatePlaceHolderICId();
- icTypePriv->extraData.id->url.setFragment(QString::number(placeholderId));
- auto icType = QQmlType(icTypePriv);
- icTypePriv->release();
- type_return->associateInlineComponent(icName, placeholderId, CompositeMetaTypeIds {}, icType);
- *type_return = icType;
- }
- Q_ASSERT(type_return->containingType().isValid());
- type_return->setPendingResolutionName(icName);
+ *type_return = QQmlMetaType::inlineComponentType(
+ *type_return, splitName.at(1).toString());
return true;
} else {
// or a failure
@@ -802,22 +757,8 @@ bool QQmlImports::resolveType(
error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(splitName.at(0).toString()));
} else {
if (resolveTypeInNamespace(splitName.at(1), s, nullptr)) {
- auto const icName = splitName.at(2).toString();
- auto objectIndex = type_return->lookupInlineComponentIdByName(icName);
- if (objectIndex != -1)
- *type_return = type_return->lookupInlineComponentById(objectIndex);
- else {
- auto icTypePriv = new QQmlTypePrivate(QQmlType::RegistrationType::InlineComponentType);
- icTypePriv->setContainingType(type_return);
- icTypePriv->extraData.id->url = type_return->sourceUrl();
- int placeholderId = type_return->generatePlaceHolderICId();
- icTypePriv->extraData.id->url.setFragment(QString::number(placeholderId));
- auto icType = QQmlType(icTypePriv);
- icTypePriv->release();
- type_return->associateInlineComponent(icName, placeholderId, CompositeMetaTypeIds {}, icType);
- *type_return = icType;
- }
- type_return->setPendingResolutionName(icName);
+ *type_return = QQmlMetaType::inlineComponentType(
+ *type_return, splitName.at(2).toString());
return true;
} else {
error.setDescription(QQmlImportDatabase::tr("- %1 is not a type").arg(splitName.at(1).toString()));
@@ -950,7 +891,7 @@ QQmlImportNamespace *QQmlImports::findQualifiedNamespace(const QHashedStringRef
Import an extension defined by a qmldir file.
*/
QTypeRevision QQmlImports::importExtension(
- const QString &uri, QTypeRevision version, QQmlImportDatabase *database,
+ QQmlTypeLoader *typeLoader, const QString &uri, QTypeRevision version,
const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
{
Q_ASSERT(qmldir->hasContent());
@@ -970,35 +911,49 @@ QTypeRevision QQmlImports::importExtension(
return QTypeRevision();
}
- if (qmldir->plugins().isEmpty())
+ if (qmldir->plugins().isEmpty()) {
+ // If the qmldir does not register a plugin, we might still have declaratively
+ // registered types (if we are dealing with an application instead of a library)
+ if (!QQmlMetaType::typeModule(uri, version))
+ QQmlMetaType::qmlRegisterModuleTypes(uri);
return validVersion(version);
+ }
- QQmlPluginImporter importer(uri, version, database, qmldir, m_typeLoader, errors);
+ QQmlPluginImporter importer(
+ uri, version, typeLoader->importDatabase(), qmldir, typeLoader, errors);
return importer.importPlugins();
}
-bool QQmlImports::getQmldirContent(const QString &qmldirIdentifier, const QString &uri,
- QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
+QString QQmlImports::redirectQmldirContent(
+ QQmlTypeLoader *typeLoader, QQmlTypeLoaderQmldirContent *qmldir)
+{
+ const QString preferredPath = qmldir->preferredPath();
+ const QString url = preferredPath.startsWith(u':')
+ ? QStringLiteral("qrc") + preferredPath
+ : QUrl::fromLocalFile(preferredPath).toString();
+
+ QQmlTypeLoaderQmldirContent redirected
+ = typeLoader->qmldirContent(url + QLatin1String("qmldir"));
+
+ // Ignore errors: If the qmldir doesn't exist, stick to the old one.
+ if (redirected.hasContent() && !redirected.hasError())
+ *qmldir = std::move(redirected);
+ return url;
+}
+
+bool QQmlImports::getQmldirContent(
+ QQmlTypeLoader *typeLoader, const QString &qmldirIdentifier, const QString &uri,
+ QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
{
Q_ASSERT(errors);
Q_ASSERT(qmldir);
- *qmldir = m_typeLoader->qmldirContent(qmldirIdentifier);
- if ((*qmldir).hasContent()) {
- // Ensure that parsing was successful
- if ((*qmldir).hasError()) {
- QUrl url = QUrl::fromLocalFile(qmldirIdentifier);
- const QList<QQmlError> qmldirErrors = (*qmldir).errors(uri);
- for (int i = 0; i < qmldirErrors.size(); ++i) {
- QQmlError error = qmldirErrors.at(i);
- error.setUrl(url);
- errors->append(error);
- }
- return false;
- }
- }
+ *qmldir = typeLoader->qmldirContent(qmldirIdentifier);
+ if (!qmldir->hasContent() || !qmldir->hasError())
+ return true;
- return true;
+ errors->append(qmldir->errors(uri, QUrl::fromLocalFile(qmldirIdentifier)));
+ return false;
}
QString QQmlImports::resolvedUri(const QString &dir_arg, QQmlImportDatabase *database)
@@ -1036,26 +991,6 @@ QString QQmlImports::resolvedUri(const QString &dir_arg, QQmlImportDatabase *dat
return stableRelativePath;
}
-/* removes all file selector occurrences in path
- firstPlus is the position of the initial '+' in the path
- which we always have as we check for '+' to decide whether
- we need to do some work at all
-*/
-static QString pathWithoutFileSelectors(QString path, // we want a copy of path
- qsizetype firstPlus)
-{
- do {
- Q_ASSERT(path.at(firstPlus) == u'+');
- const auto eos = path.size();
- qsizetype terminatingSlashPos = firstPlus + 1;
- while (terminatingSlashPos != eos && path.at(terminatingSlashPos) != u'/')
- ++terminatingSlashPos;
- path.remove(firstPlus, terminatingSlashPos - firstPlus + 1);
- firstPlus = path.indexOf(u'+', firstPlus);
- } while (firstPlus != -1);
- return path;
-}
-
/*!
\internal
@@ -1102,47 +1037,17 @@ QTypeRevision QQmlImports::matchingQmldirVersion(
typedef QQmlDirComponents::const_iterator ConstIterator;
const QQmlDirComponents &components = qmldir.components();
- QMultiHash<QString, ConstIterator> baseFileName2ConflictingComponents;
-
ConstIterator cend = components.constEnd();
for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
if (cit2->typeName == cit->typeName && cit2->version == cit->version) {
- // ugly heuristic to deal with file selectors
- const auto comp2PotentialFileSelectorPos = cit2->fileName.indexOf(u'+');
- const bool comp2MightHaveFileSelector = comp2PotentialFileSelectorPos != -1;
- /* If we detect conflicting paths, we check if they agree when we remove anything looking like a
- file selector.
- We need to create copies of the filenames, otherwise QString::replace would modify the
- existing file-names
- */
- QString compFileName1 = cit->fileName;
- QString compFileName2 = cit2->fileName;
- if (auto fileSelectorPos1 = compFileName1.indexOf(u'+'); fileSelectorPos1 != -1) {
- // existing entry was file selector entry, fix it up
- // it could also be the case that _both_ are using file selectors
- QString baseName = comp2MightHaveFileSelector ? pathWithoutFileSelectors(compFileName2,
- comp2PotentialFileSelectorPos)
- : compFileName2;
- if (pathWithoutFileSelectors(compFileName1, fileSelectorPos1) == baseName) {
- baseFileName2ConflictingComponents.insert(baseName, cit);
- baseFileName2ConflictingComponents.insert(baseName, cit2);
- continue;
- }
- // fall through to error case
- } else if (comp2MightHaveFileSelector) {
- // new entry contains file selector (and we now that cit did not)
- if (pathWithoutFileSelectors(compFileName2, comp2PotentialFileSelectorPos) == compFileName1) {
- baseFileName2ConflictingComponents.insert(compFileName1, cit2);
- continue;
- }
- // fall through to error case
- }
// This entry clashes with a predecessor
QQmlError error;
- error.setDescription(QQmlImportDatabase::tr("\"%1\" version %2.%3 is defined more than once in module \"%4\"")
- .arg(cit->typeName).arg(cit->version.majorVersion())
- .arg(cit->version.minorVersion()).arg(uri));
+ error.setDescription(
+ QQmlImportDatabase::tr(
+ "\"%1\" version %2.%3 is defined more than once in module \"%4\"")
+ .arg(cit->typeName).arg(cit->version.majorVersion())
+ .arg(cit->version.minorVersion()).arg(uri));
errors->prepend(error);
return QTypeRevision();
}
@@ -1151,14 +1056,6 @@ QTypeRevision QQmlImports::matchingQmldirVersion(
addVersion(cit->version);
}
- // ensure that all components point to the actual base URL, and let the file selectors resolve them correctly during URL resolution
- for (auto keyIt = baseFileName2ConflictingComponents.keyBegin(); keyIt != baseFileName2ConflictingComponents.keyEnd(); ++keyIt) {
- const QString& baseFileName = *keyIt;
- const auto conflictingComponents = baseFileName2ConflictingComponents.values(baseFileName);
- for (ConstIterator component: conflictingComponents)
- component->fileName = baseFileName;
- }
-
typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
const QQmlDirScripts &scripts = qmldir.scripts();
@@ -1219,7 +1116,7 @@ QQmlImportNamespace *QQmlImports::importNamespace(const QString &prefix)
return nameSpace;
}
-QQmlImportInstance *QQmlImports::addImportToNamespace(
+static QQmlImportInstance *addImportToNamespace(
QQmlImportNamespace *nameSpace, const QString &uri, const QString &url, QTypeRevision version,
QV4::CompiledData::Import::ImportType type, QList<QQmlError> *errors, quint16 precedence)
{
@@ -1249,11 +1146,11 @@ QQmlImportInstance *QQmlImports::addImportToNamespace(
}
QTypeRevision QQmlImports::addLibraryImport(
- QQmlImportDatabase *database, const QString &uri, const QString &prefix,
+ QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix,
QTypeRevision version, const QString &qmldirIdentifier, const QString &qmldirUrl,
ImportFlags flags, quint16 precedence, QList<QQmlError> *errors)
{
- Q_ASSERT(database);
+ Q_ASSERT(typeLoader);
Q_ASSERT(errors);
qCDebug(lcQmlImport)
@@ -1273,15 +1170,19 @@ QTypeRevision QQmlImports::addLibraryImport(
QQmlTypeLoaderQmldirContent qmldir;
if (!qmldirIdentifier.isEmpty()) {
- if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors))
+ if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors))
return QTypeRevision();
if (qmldir.hasContent()) {
- version = importExtension(uri, version, database, &qmldir, errors);
+ version = importExtension(typeLoader, uri, version, &qmldir, errors);
if (!version.isValid())
return QTypeRevision();
- if (!inserted->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors))
+ const QString resolvedUrl = qmldir.hasRedirection()
+ ? redirectQmldirContent(typeLoader, &qmldir)
+ : qmldirUrl;
+
+ if (!inserted->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors))
return QTypeRevision();
}
}
@@ -1333,11 +1234,11 @@ QTypeRevision QQmlImports::addLibraryImport(
In case of failure, the \a errors array will filled appropriately.
*/
QTypeRevision QQmlImports::addFileImport(
- QQmlImportDatabase *database, const QString &uri, const QString &prefix,
- QTypeRevision version, ImportFlags flags, quint16 precedence,
- QString *localQmldir, QList<QQmlError> *errors)
+ QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix,
+ QTypeRevision version, ImportFlags flags, quint16 precedence, QString *localQmldir,
+ QList<QQmlError> *errors)
{
- Q_ASSERT(database);
+ Q_ASSERT(typeLoader);
Q_ASSERT(errors);
qCDebug(lcQmlImport)
@@ -1367,7 +1268,7 @@ QTypeRevision QQmlImports::addFileImport(
QString qmldirUrl = resolveLocalUrl(m_base, importUri + (importUri.endsWith(Slash)
? String_qmldir
: Slash_qmldir));
- qmldirUrl = m_typeLoader->engine()->interceptUrl(
+ qmldirUrl = typeLoader->engine()->interceptUrl(
QUrl(qmldirUrl), QQmlAbstractUrlInterceptor::QmldirFile).toString();
QString qmldirIdentifier;
@@ -1377,7 +1278,7 @@ QTypeRevision QQmlImports::addFileImport(
Q_ASSERT(!localFileOrQrc.isEmpty());
const QString dir = localFileOrQrc.left(localFileOrQrc.lastIndexOf(Slash) + 1);
- if (!m_typeLoader->directoryExists(dir)) {
+ if (!typeLoader->directoryExists(dir)) {
if (precedence < QQmlImportInstance::Implicit) {
QQmlError error;
error.setDescription(QQmlImportDatabase::tr("\"%1\": no such directory").arg(uri));
@@ -1389,12 +1290,12 @@ QTypeRevision QQmlImports::addFileImport(
// Transforms the (possible relative) uri into our best guess relative to the
// import paths.
- importUri = resolvedUri(dir, database);
+ importUri = resolvedUri(dir, typeLoader->importDatabase());
if (importUri.endsWith(Slash))
importUri.chop(1);
- if (!m_typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
- qmldirIdentifier = localFileOrQrc;
+ if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
+ qmldirIdentifier = std::move(localFileOrQrc);
if (localQmldir)
*localQmldir = qmldirIdentifier;
}
@@ -1414,6 +1315,15 @@ QTypeRevision QQmlImports::addFileImport(
// The url for the path containing files for this import
QString url = resolveLocalUrl(m_base, uri);
+ if (url.isEmpty()) {
+ QQmlError error;
+ error.setDescription(
+ QQmlImportDatabase::tr("Cannot resolve URL for import \"%1\"").arg(uri));
+ error.setUrl(m_baseUrl);
+ errors->prepend(error);
+ return QTypeRevision();
+ }
+
if (!url.endsWith(Slash) && !url.endsWith(Backslash))
url += Slash;
@@ -1433,7 +1343,7 @@ QTypeRevision QQmlImports::addFileImport(
if (!(flags & QQmlImports::ImportIncomplete) && !qmldirIdentifier.isEmpty()) {
QQmlTypeLoaderQmldirContent qmldir;
- if (!getQmldirContent(qmldirIdentifier, importUri, &qmldir, errors))
+ if (!getQmldirContent(typeLoader, qmldirIdentifier, importUri, &qmldir, errors))
return QTypeRevision();
if (qmldir.hasContent()) {
@@ -1447,10 +1357,13 @@ QTypeRevision QQmlImports::addFileImport(
errors, precedence);
Q_ASSERT(inserted);
- version = importExtension(importUri, version, database, &qmldir, errors);
+ version = importExtension(typeLoader, importUri, version, &qmldir, errors);
if (!version.isValid())
return QTypeRevision();
+ if (qmldir.hasRedirection())
+ url = redirectQmldirContent(typeLoader, &qmldir);
+
if (!inserted->setQmldirContent(url, qmldir, nameSpace, errors))
return QTypeRevision();
@@ -1466,10 +1379,10 @@ QTypeRevision QQmlImports::addFileImport(
}
QTypeRevision QQmlImports::updateQmldirContent(
- QQmlImportDatabase *database, const QString &uri, const QString &prefix,
+ QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix,
const QString &qmldirIdentifier, const QString &qmldirUrl, QList<QQmlError> *errors)
{
- Q_ASSERT(database);
+ Q_ASSERT(typeLoader);
Q_ASSERT(errors);
qDebug(lcQmlImport)
@@ -1481,16 +1394,20 @@ QTypeRevision QQmlImports::updateQmldirContent(
if (QQmlImportInstance *import = nameSpace->findImport(uri)) {
QQmlTypeLoaderQmldirContent qmldir;
- if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors))
+ if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors))
return QTypeRevision();
if (qmldir.hasContent()) {
QTypeRevision version = importExtension(
- uri, import->version, database, &qmldir, errors);
+ typeLoader, uri, import->version, &qmldir, errors);
if (!version.isValid())
return QTypeRevision();
- if (import->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors)) {
+ const QString resolvedUrl = qmldir.hasRedirection()
+ ? redirectQmldirContent(typeLoader, &qmldir)
+ : qmldirUrl;
+
+ if (import->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors)) {
if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) {
// The implicit import qmldir can be empty, and plugins have no extra versions
if (uri != QLatin1String(".") && !QQmlMetaType::matchingModuleVersion(uri, version).isValid()) {
@@ -1518,7 +1435,7 @@ QTypeRevision QQmlImports::updateQmldirContent(
}
/*!
- \fn QQmlImports::addImplicitImport(QQmlImportDatabase *importDb, QString *localQmldir, QList<QQmlError> *errors)
+ \fn QQmlImports::addImplicitImport(QQmlTypeLoader *typeLoader, QString *localQmldir, QList<QQmlError> *errors)
\internal
Adds an implicit "." file import. This is equivalent to calling addFileImport(), but error
@@ -1531,13 +1448,12 @@ QTypeRevision QQmlImports::updateQmldirContent(
/*!
\internal
*/
-bool QQmlImports::addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl, QQmlType containingType)
+bool QQmlImports::addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl)
{
importInstance->url = importUrl.toString();
importInstance->uri = name;
importInstance->isInlineComponent = true;
importInstance->version = QTypeRevision::zero();
- importInstance->containingType = containingType;
m_unqualifiedset.imports.push_back(importInstance);
m_unqualifiedset.setNeedsSorting(true);
return true;
@@ -1600,8 +1516,9 @@ QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
// 6. $QML_IMPORT_PATH
// 7. QLibraryInfo::QmlImportsPath
- QString installImportsPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath);
- addImportPath(installImportsPath);
+ const auto paths = QLibraryInfo::paths(QLibraryInfo::QmlImportsPath);
+ for (const auto &installImportsPath: paths)
+ addImportPath(installImportsPath);
auto addEnvImportPath = [this](const char *var) {
if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty(var))) {
diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h
index 6a9d207a6b..ef9b4b3422 100644
--- a/src/qml/qml/qqmlimport_p.h
+++ b/src/qml/qml/qqmlimport_p.h
@@ -15,7 +15,6 @@
#include <private/qqmldirparser_p.h>
#include <private/qqmltype_p.h>
#include <private/qstringhash_p.h>
-#include <private/qv4compileddata_p.h>
#include <private/qfieldlist_p.h>
//
@@ -38,6 +37,7 @@ class QQmlImportNamespace;
class QQmlImportDatabase;
class QQmlTypeLoader;
class QQmlTypeLoaderQmldirContent;
+class QTypeRevision;
const QLoggingCategory &lcQmlImport();
@@ -55,7 +55,6 @@ struct QQmlImportInstance
QString uri; // e.g. QtQuick
QString url; // the base path of the import
- QQmlType containingType; // points to the containing type for inline components
QTypeRevision version; // the version imported
bool isLibrary; // true means that this is not a file import
@@ -115,7 +114,7 @@ public:
}
};
-class Q_QML_PRIVATE_EXPORT QQmlImports : public QQmlRefCount
+class Q_QML_EXPORT QQmlImports final : public QQmlRefCounted<QQmlImports>
{
Q_DISABLE_COPY_MOVE(QQmlImports)
public:
@@ -127,7 +126,7 @@ public:
};
Q_DECLARE_FLAGS(ImportFlags, ImportFlag)
- QQmlImports(QQmlTypeLoader *loader) : m_typeLoader(loader) {}
+ QQmlImports() = default;
~QQmlImports()
{
while (QQmlImportNamespace *ns = m_qualifiedSets.takeFirst())
@@ -138,13 +137,14 @@ public:
QUrl baseUrl() const { return m_baseUrl; }
bool resolveType(
- const QHashedStringRef &type, QQmlType *type_return, QTypeRevision *version_return,
- QQmlImportNamespace **ns_return, QList<QQmlError> *errors = nullptr,
+ QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QQmlType *type_return,
+ QTypeRevision *version_return, QQmlImportNamespace **ns_return,
+ QList<QQmlError> *errors = nullptr,
QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType,
bool *typeRecursionDetected = nullptr) const;
QTypeRevision addImplicitImport(
- QQmlImportDatabase *importDb, QString *localQmldir, QList<QQmlError> *errors)
+ QQmlTypeLoader *typeLoader, QString *localQmldir, QList<QQmlError> *errors)
{
Q_ASSERT(errors);
qCDebug(lcQmlImport) << "addImplicitImport:" << qPrintable(baseUrl().toString());
@@ -152,26 +152,25 @@ public:
const ImportFlags flags =
ImportFlags(!isLocal(baseUrl()) ? ImportIncomplete : ImportNoFlag);
return addFileImport(
- importDb, QLatin1String("."), QString(), QTypeRevision(),
- flags, QQmlImportInstance::Implicit, localQmldir, errors);
+ typeLoader, QLatin1String("."), QString(), QTypeRevision(), flags,
+ QQmlImportInstance::Implicit, localQmldir, errors);
}
bool addInlineComponentImport(
- QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl,
- QQmlType containingType);
+ QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl);
QTypeRevision addFileImport(
- QQmlImportDatabase *importDb, const QString &uri, const QString &prefix,
+ QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix,
QTypeRevision version, ImportFlags flags, quint16 precedence, QString *localQmldir,
QList<QQmlError> *errors);
QTypeRevision addLibraryImport(
- QQmlImportDatabase *importDb, const QString &uri, const QString &prefix,
+ QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix,
QTypeRevision version, const QString &qmldirIdentifier, const QString &qmldirUrl,
ImportFlags flags, quint16 precedence, QList<QQmlError> *errors);
QTypeRevision updateQmldirContent(
- QQmlImportDatabase *importDb, const QString &uri, const QString &prefix,
+ QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix,
const QString &qmldirIdentifier, const QString &qmldirUrl, QList<QQmlError> *errors);
void populateCache(QQmlTypeNameCache *cache) const;
@@ -213,14 +212,17 @@ public:
static void setDesignerSupportRequired(bool b);
+ static QTypeRevision validVersion(QTypeRevision version = QTypeRevision());
+
private:
friend class QQmlImportDatabase;
QQmlImportNamespace *importNamespace(const QString &prefix);
bool resolveType(
- const QHashedStringRef &type, QTypeRevision *version_return, QQmlType *type_return,
- QList<QQmlError> *errors, QQmlType::RegistrationType registrationType,
+ QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QTypeRevision *version_return,
+ QQmlType *type_return, QList<QQmlError> *errors,
+ QQmlType::RegistrationType registrationType,
bool *typeRecursionDetected = nullptr) const;
QQmlImportNamespace *findQualifiedNamespace(const QHashedStringRef &) const;
@@ -230,20 +232,17 @@ private:
QTypeRevision version, QList<QQmlError> *errors);
QTypeRevision importExtension(
- const QString &uri, QTypeRevision version, QQmlImportDatabase *database,
+ QQmlTypeLoader *typeLoader, const QString &uri, QTypeRevision version,
const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors);
+ QString redirectQmldirContent(QQmlTypeLoader *typeLoader, QQmlTypeLoaderQmldirContent *qmldir);
+
bool getQmldirContent(
- const QString &qmldirIdentifier, const QString &uri, QQmlTypeLoaderQmldirContent *qmldir,
- QList<QQmlError> *errors);
+ QQmlTypeLoader *typeLoader, const QString &qmldirIdentifier, const QString &uri,
+ QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors);
QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database);
- QQmlImportInstance *addImportToNamespace(
- QQmlImportNamespace *nameSpace, const QString &uri, const QString &url,
- QTypeRevision version, QV4::CompiledData::Import::ImportType type,
- QList<QQmlError> *errors, quint16 precedence);
-
QUrl m_baseUrl;
QString m_base;
@@ -255,13 +254,11 @@ private:
// storage of data related to imports with a namespace
QFieldList<QQmlImportNamespace, &QQmlImportNamespace::nextNamespace> m_qualifiedSets;
-
- QQmlTypeLoader *m_typeLoader = nullptr;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlImports::ImportFlags)
-class Q_QML_PRIVATE_EXPORT QQmlImportDatabase
+class Q_QML_EXPORT QQmlImportDatabase
{
Q_DECLARE_TR_FUNCTIONS(QQmlImportDatabase)
public:
diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp
index 4ae1f20d6c..5c18230450 100644
--- a/src/qml/qml/qqmlincubator.cpp
+++ b/src/qml/qml/qqmlincubator.cpp
@@ -177,9 +177,15 @@ protected:
};
\endcode
-Although the previous example would work, it is not optimal. Real world incubation
-controllers should try and maximize the amount of idle time they consume - rather
-than a static amount like 5 milliseconds - while not disturbing the application.
+Although the example works, it is heavily simplified. Real world incubation controllers
+try and maximize the amount of idle time they consume while not disturbing the
+application. Using a static amount of 5 milliseconds like above may both leave idle
+time on the table in some frames and disturb the application in others.
+
+\l{QQuickWindow}, \l{QQuickView}, and \l{QQuickWidget} all pre-create an incubation
+controller that spaces out incubation over multiple frames using a more intelligent
+algorithm. You rarely have to write your own.
+
*/
/*!
@@ -457,25 +463,37 @@ synchronously which, depending on the complexity of the object, can cause notic
stutters in the application.
The use of QQmlIncubator gives more control over the creation of a QML object,
-including allowing it to be created asynchronously using application idle time. The following
+including allowing it to be created asynchronously using application idle time. The following
example shows a simple use of QQmlIncubator.
\code
+// Initialize the incubator
QQmlIncubator incubator;
component->create(incubator);
+\endcode
-while (!incubator.isReady()) {
- QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
-}
+Let the incubator run for a while (normally by returning control to the event loop),
+then poll it. There are a number of ways to get back to the incubator later. You may
+want to connect to one of the signals sent by \l{QQuickWindow}, or you may want to run
+a \l{QTimer} especially for that. You may also need the object for some specific
+purpose and poll the incubator when that purpose arises.
-QObject *object = incubator.object();
+\code
+// Poll the incubator
+if (incubator.isReady()) {
+ QObject *object = incubator.object();
+ // Use created object
+}
\endcode
-Asynchronous incubators are controlled by a QQmlIncubationController that is
-set on the QQmlEngine, which lets the engine know when the application is idle and
+Asynchronous incubators are controlled by a \l{QQmlIncubationController} that is
+set on the \l{QQmlEngine}, which lets the engine know when the application is idle and
incubating objects should be processed. If an incubation controller is not set on the
-QQmlEngine, QQmlIncubator creates objects synchronously regardless of the
-specified IncubationMode.
+\l{QQmlEngine}, \l{QQmlIncubator} creates objects synchronously regardless of the
+specified IncubationMode. By default, no incubation controller is set. However,
+\l{QQuickView}, \l{QQuickWindow} and \l{QQuickWidget} all set incubation controllers
+on their respective \l{QQmlEngine}s. These incubation controllers space out incubations
+across multiple frames while the view is being rendered.
QQmlIncubator supports three incubation modes:
\list
@@ -742,13 +760,23 @@ void QQmlIncubator::statusChanged(Status status)
}
/*!
-Called after the \a object is first created, but before property bindings are
-evaluated and, if applicable, QQmlParserStatus::componentComplete() is
-called. This is equivalent to the point between QQmlComponent::beginCreate()
+Called after the \a object is first created, but before complex property
+bindings are evaluated and, if applicable, QQmlParserStatus::componentComplete()
+is called. This is equivalent to the point between QQmlComponent::beginCreate()
and QQmlComponent::completeCreate(), and can be used to assign initial values
to the object's properties.
The default implementation does nothing.
+
+\note Simple bindings such as numeric literals are evaluated before
+setInitialState() is called. The categorization of bindings into simple and
+complex ones is intentionally unspecified and may change between versions of
+Qt and depending on whether and how you are using \l{qmlcachegen}. You should
+not rely on any particular binding to be evaluated either before or after
+setInitialState() is called. For example, a constant expression like
+\e{MyType.EnumValue} may be recognized as such at compile time or deferred
+to be executed as binding. The same holds for constant expressions like
+\e{-(5)} or \e{"a" + " constant string"}.
*/
void QQmlIncubator::setInitialState(QObject *object)
{
diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h
index d786cd9f52..f35679a8a1 100644
--- a/src/qml/qml/qqmlincubator_p.h
+++ b/src/qml/qml/qqmlincubator_p.h
@@ -12,6 +12,8 @@
#include <private/qqmlengine_p.h>
#include <private/qqmlguardedcontextdata_p.h>
+#include <QtCore/qpointer.h>
+
//
// W A R N I N G
// -------------
@@ -28,7 +30,7 @@ QT_BEGIN_NAMESPACE
class RequiredProperties;
class QQmlIncubator;
-class Q_QML_PRIVATE_EXPORT QQmlIncubatorPrivate : public QQmlEnginePrivate::Incubator, public QSharedData
+class Q_QML_EXPORT QQmlIncubatorPrivate : public QQmlEnginePrivate::Incubator, public QSharedData
{
public:
QQmlIncubatorPrivate(QQmlIncubator *q, QQmlIncubator::IncubationMode m);
diff --git a/src/qml/qml/qqmlirloader.cpp b/src/qml/qml/qqmlirloader.cpp
index 5137c4c1ab..e1019b804f 100644
--- a/src/qml/qml/qqmlirloader.cpp
+++ b/src/qml/qml/qqmlirloader.cpp
@@ -57,7 +57,7 @@ void QQmlIRLoader::load()
const auto createValueTypePragma = [&](
Pragma::PragmaType type,
- Pragma::ValueTypeBehaviorValue value) {
+ Pragma::ValueTypeBehaviorValues value) {
createPragma(type)->valueTypeBehavior = value;
};
@@ -74,14 +74,21 @@ void QQmlIRLoader::load()
if (unit->flags & QV4::CompiledData::Unit::ComponentsBound)
createComponentPragma(Pragma::ComponentBehavior, Pragma::Bound);
- if (unit->flags & QV4::CompiledData::Unit::FunctionSignaturesEnforced)
- createFunctionSignaturePragma(Pragma::FunctionSignatureBehavior, Pragma::Enforced);
+ if (unit->flags & QV4::CompiledData::Unit::FunctionSignaturesIgnored)
+ createFunctionSignaturePragma(Pragma::FunctionSignatureBehavior, Pragma::Ignored);
if (unit->flags & QV4::CompiledData::Unit::NativeMethodsAcceptThisObject)
createNativeMethodPragma(Pragma::NativeMethodBehavior, Pragma::AcceptThisObject);
+ Pragma::ValueTypeBehaviorValues valueTypeBehavior = {};
if (unit->flags & QV4::CompiledData::Unit::ValueTypesCopied)
- createValueTypePragma(Pragma::ValueTypeBehavior, Pragma::Copy);
+ valueTypeBehavior |= Pragma::Copy;
+ if (unit->flags & QV4::CompiledData::Unit::ValueTypesAddressable)
+ valueTypeBehavior |= Pragma::Addressable;
+ if (unit->flags & QV4::CompiledData::Unit::ValueTypesAssertable)
+ valueTypeBehavior |= Pragma::Assertable;
+ if (valueTypeBehavior)
+ createValueTypePragma(Pragma::ValueTypeBehavior, valueTypeBehavior);
for (uint i = 0; i < qmlUnit->nObjects; ++i) {
const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i);
diff --git a/src/qml/qml/qqmlirloader_p.h b/src/qml/qml/qqmlirloader_p.h
index f4c1213223..8a14d6833e 100644
--- a/src/qml/qml/qqmlirloader_p.h
+++ b/src/qml/qml/qqmlirloader_p.h
@@ -26,7 +26,7 @@ struct Document;
struct Object;
}
-struct Q_QML_PRIVATE_EXPORT QQmlIRLoader {
+struct Q_QML_EXPORT QQmlIRLoader {
QQmlIRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output);
void load();
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index 304c5da29a..d7cf38984b 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -350,19 +350,35 @@ void QQmlPropertyCapture::captureProperty(
captureNonBindableProperty(o, propertyData->notifyIndex(), propertyData->coreIndex(), doNotify);
}
+bool QQmlJavaScriptExpression::needsPropertyChangeTrigger(QObject *target, int propertyIndex)
+{
+ TriggerList **prev = &qpropertyChangeTriggers;
+ TriggerList *current = qpropertyChangeTriggers;
+ while (current) {
+ if (!current->target) {
+ *prev = current->next;
+ QRecyclePool<TriggerList>::Delete(current);
+ current = *prev;
+ } else if (current->target == target && current->propertyIndex == propertyIndex) {
+ return false; // already installed
+ } else {
+ prev = &current->next;
+ current = current->next;
+ }
+ }
+
+ return true;
+}
+
void QQmlPropertyCapture::captureTranslation()
{
// use a unique invalid index to avoid needlessly querying the metaobject for
// the correct index of of the translationLanguage property
int const invalidIndex = -2;
- for (auto trigger = expression->qpropertyChangeTriggers; trigger;
- trigger = trigger->next) {
- if (trigger->target == engine && trigger->propertyIndex == invalidIndex)
- return; // already installed
+ if (expression->needsPropertyChangeTrigger(engine, invalidIndex)) {
+ auto trigger = expression->allocatePropertyChangeTrigger(engine, invalidIndex);
+ trigger->setSource(QQmlEnginePrivate::get(engine)->translationLanguage);
}
- auto trigger = expression->allocatePropertyChangeTrigger(engine, invalidIndex);
-
- trigger->setSource(QQmlEnginePrivate::get(engine)->translationLanguage);
}
void QQmlPropertyCapture::captureBindableProperty(
@@ -372,16 +388,14 @@ void QQmlPropertyCapture::captureBindableProperty(
// the automatic capturing process already takes care of everything
if (!expression->mustCaptureBindableProperty())
return;
- for (auto trigger = expression->qpropertyChangeTriggers; trigger;
- trigger = trigger->next) {
- if (trigger->target == o && trigger->propertyIndex == c)
- return; // already installed
+
+ if (expression->needsPropertyChangeTrigger(o, c)) {
+ auto trigger = expression->allocatePropertyChangeTrigger(o, c);
+ QUntypedBindable bindable;
+ void *argv[] = { &bindable };
+ metaObjectForBindable->metacall(o, QMetaObject::BindableProperty, c, argv);
+ bindable.observe(trigger);
}
- auto trigger = expression->allocatePropertyChangeTrigger(o, c);
- QUntypedBindable bindable;
- void *argv[] = { &bindable };
- metaObjectForBindable->metacall(o, QMetaObject::BindableProperty, c, argv);
- bindable.observe(trigger);
}
void QQmlPropertyCapture::captureNonBindableProperty(QObject *o, int n, int c, bool doNotify)
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index e8e4bb4990..80e2033627 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -62,7 +62,7 @@ private:
QQmlDelayedError **prevError;
};
-class Q_QML_PRIVATE_EXPORT QQmlJavaScriptExpression
+class Q_QML_EXPORT QQmlJavaScriptExpression
{
Q_DISABLE_COPY_MOVE(QQmlJavaScriptExpression)
public:
@@ -132,6 +132,8 @@ public:
QQmlEngine *engine() const { return m_context ? m_context->engine() : nullptr; }
bool hasUnresolvedNames() const { return m_context && m_context->hasUnresolvedNames(); }
+
+ bool needsPropertyChangeTrigger(QObject *target, int propertyIndex);
QPropertyChangeTrigger *allocatePropertyChangeTrigger(QObject *target, int propertyIndex);
protected:
@@ -183,7 +185,7 @@ protected:
TriggerList *qpropertyChangeTriggers = nullptr;
};
-class Q_QML_PRIVATE_EXPORT QQmlPropertyCapture
+class Q_QML_EXPORT QQmlPropertyCapture
{
public:
QQmlPropertyCapture(QQmlEngine *engine, QQmlJavaScriptExpression *e, QQmlJavaScriptExpression::DeleteWatcher *w)
diff --git a/src/qml/qml/qqmllist.cpp b/src/qml/qml/qqmllist.cpp
index 5fd077c7c1..a166041070 100644
--- a/src/qml/qml/qqmllist.cpp
+++ b/src/qml/qml/qqmllist.cpp
@@ -447,7 +447,7 @@ Q_PROPERTY(QQmlListProperty<Fruit> fruit READ fruit)
QML list properties are type-safe - in this case \c {Fruit} is a QObject type that
\c {Apple}, \c {Orange} and \c {Banana} all derive from.
-\sa {Extending QML - Object and List Property Types Example}
+\sa {Chapter 5: Using List Property Types}
*/
/*!
diff --git a/src/qml/qml/qqmllist.h b/src/qml/qml/qqmllist.h
index c54ebf0677..6f2c077764 100644
--- a/src/qml/qml/qqmllist.h
+++ b/src/qml/qml/qqmllist.h
@@ -5,9 +5,11 @@
#define QQMLLIST_H
#include <QtQml/qtqmlglobal.h>
+
+#include <QtCore/qcontainerinfo.h>
#include <QtCore/qlist.h>
+#include <QtCore/qmetatype.h>
#include <QtCore/qvariant.h>
-#include <QtCore/QMetaType>
QT_BEGIN_NAMESPACE
@@ -21,6 +23,8 @@ struct QMetaObject;
template<typename T>
class QQmlListProperty {
public:
+ using value_type = T*;
+
using AppendFunction = void (*)(QQmlListProperty<T> *, T *);
using CountFunction = qsizetype (*)(QQmlListProperty<T> *);
using AtFunction = T *(*)(QQmlListProperty<T> *, qsizetype);
@@ -84,24 +88,45 @@ public:
ReplaceFunction replace = nullptr;
RemoveLastFunction removeLast = nullptr;
+ template<typename List>
+ List toList()
+ {
+ if constexpr (std::is_same_v<List, QList<T *>>) {
+ if (append == qlist_append)
+ return *static_cast<QList<T *> *>(data);
+ }
+
+ const qsizetype size = count(this);
+
+ List result;
+ if constexpr (QContainerInfo::has_reserve_v<List>)
+ result.reserve(size);
+
+ static_assert(QContainerInfo::has_push_back_v<List>);
+ for (qsizetype i = 0; i < size; ++i)
+ result.push_back(at(this, i));
+
+ return result;
+ }
+
private:
static void qlist_append(QQmlListProperty *p, T *v) {
- reinterpret_cast<QList<T *> *>(p->data)->append(v);
+ static_cast<QList<T *> *>(p->data)->append(v);
}
static qsizetype qlist_count(QQmlListProperty *p) {
- return reinterpret_cast<QList<T *> *>(p->data)->size();
+ return static_cast<QList<T *> *>(p->data)->size();
}
static T *qlist_at(QQmlListProperty *p, qsizetype idx) {
- return reinterpret_cast<QList<T *> *>(p->data)->at(idx);
+ return static_cast<QList<T *> *>(p->data)->at(idx);
}
static void qlist_clear(QQmlListProperty *p) {
- return reinterpret_cast<QList<T *> *>(p->data)->clear();
+ return static_cast<QList<T *> *>(p->data)->clear();
}
static void qlist_replace(QQmlListProperty *p, qsizetype idx, T *v) {
- return reinterpret_cast<QList<T *> *>(p->data)->replace(idx, v);
+ return static_cast<QList<T *> *>(p->data)->replace(idx, v);
}
static void qlist_removeLast(QQmlListProperty *p) {
- return reinterpret_cast<QList<T *> *>(p->data)->removeLast();
+ return static_cast<QList<T *> *>(p->data)->removeLast();
}
static void qslow_replace(QQmlListProperty<T> *list, qsizetype idx, T *v)
diff --git a/src/qml/qml/qqmllist_p.h b/src/qml/qml/qqmllist_p.h
index 2a323fcd59..642b3c3db1 100644
--- a/src/qml/qml/qqmllist_p.h
+++ b/src/qml/qml/qqmllist_p.h
@@ -20,6 +20,8 @@
#include "qqmlmetatype_p.h"
#include <QtQml/private/qbipointer_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQmlListReferencePrivate
diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp
index ba6058ff3d..8d5a585b62 100644
--- a/src/qml/qml/qqmllistwrapper.cpp
+++ b/src/qml/qml/qqmllistwrapper.cpp
@@ -3,6 +3,8 @@
#include "qqmllistwrapper_p.h"
+#include <QtQml/qqmlinfo.h>
+
#include <private/qqmllist_p.h>
#include <private/qv4arrayiterator_p.h>
@@ -20,50 +22,143 @@ using namespace Qt::StringLiterals;
DEFINE_OBJECT_VTABLE(QmlListWrapper);
-void Heap::QmlListWrapper::init()
+static void setArrayData(Heap::QmlListWrapper *d)
+{
+ QV4::Scope scope(d->internalClass->engine);
+ QV4::ScopedObject o(scope, d);
+ o->arrayCreate();
+}
+
+struct ListWrapperObject
+{
+ QV4::Scope scope;
+ QV4::ScopedObject object;
+
+ ListWrapperObject(QQmlListProperty<QObject> *p)
+ : scope(static_cast<Heap::QmlListWrapper *>(p->data)->internalClass->engine)
+ , object(scope, static_cast<Heap::QmlListWrapper *>(p->data))
+ {
+ Q_ASSERT(object);
+ Q_ASSERT(object->arrayData());
+ }
+
+ Heap::ArrayData *arrayData() const
+ {
+ return object->arrayData();
+ }
+};
+
+static void appendWrapped(QQmlListProperty<QObject> *p, QObject *o)
+{
+ ListWrapperObject object(p);
+ Heap::ArrayData *arrayData = object.arrayData();
+
+ const uint length = arrayData->length();
+ if (Q_UNLIKELY(length == std::numeric_limits<uint>::max())) {
+ object.scope.engine->throwRangeError(QLatin1String("Too many elements."));
+ return;
+ }
+
+ ArrayData::realloc(object.object, Heap::ArrayData::Simple, length + 1, false);
+ arrayData->vtable()->put(
+ object.object, length, QV4::QObjectWrapper::wrap(object.scope.engine, o));
+}
+
+static qsizetype countWrapped(QQmlListProperty<QObject> *p)
+{
+ ListWrapperObject object(p);
+ return object.arrayData()->length();
+}
+
+static QObject *atWrapped(QQmlListProperty<QObject> *p, qsizetype i)
+{
+ ListWrapperObject object(p);
+ QV4::Scoped<QObjectWrapper> result(object.scope, object.arrayData()->get(i));
+ return result ? result->object() : nullptr;
+}
+
+static void clearWrapped(QQmlListProperty<QObject> *p)
+{
+ ListWrapperObject object(p);
+ object.arrayData()->vtable()->truncate(object.object, 0);
+}
+
+static void replaceWrapped(QQmlListProperty<QObject> *p, qsizetype i, QObject *o)
+{
+ ListWrapperObject object(p);
+ object.arrayData()->vtable()->put(
+ object.object, i, QV4::QObjectWrapper::wrap(object.scope.engine, o));
+}
+
+static void removeLastWrapped(QQmlListProperty<QObject> *p)
+{
+ ListWrapperObject object(p);
+ Heap::ArrayData *arrayData = object.arrayData();
+ const uint length = arrayData->length();
+ if (length > 0)
+ arrayData->vtable()->truncate(object.object, length - 1);
+}
+
+void Heap::QmlListWrapper::init(QMetaType propertyType)
+{
+ Object::init();
+ m_object.init();
+ m_propertyType = propertyType.iface();
+ setArrayData(this);
+ *property() = QQmlListProperty<QObject>(
+ nullptr, this,
+ appendWrapped, countWrapped, atWrapped,
+ clearWrapped, replaceWrapped, removeLastWrapped);
+}
+
+void Heap::QmlListWrapper::init(QObject *object, int propertyId, QMetaType propertyType)
{
Object::init();
- object.init();
- QV4::Scope scope(internalClass->engine);
- QV4::ScopedObject o(scope, this);
- o->setArrayType(Heap::ArrayData::Custom);
+ m_object.init(object);
+ m_propertyType = propertyType.iface();
+ void *args[] = { property(), nullptr };
+ QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyId, args);
+}
+
+void Heap::QmlListWrapper::init(
+ QObject *object, const QQmlListProperty<QObject> &list, QMetaType propertyType)
+{
+ Object::init();
+ m_object.init(object);
+ m_propertyType = propertyType.iface();
+ *property() = list;
}
void Heap::QmlListWrapper::destroy()
{
- object.destroy();
+ m_object.destroy();
Object::destroy();
}
-ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, QObject *object, int propId, QMetaType propType)
+ReturnedValue QmlListWrapper::create(
+ ExecutionEngine *engine, QObject *object, int propId, QMetaType propType)
{
if (!object || propId == -1)
return Encode::null();
-
- Scope scope(engine);
-
- Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocate<QmlListWrapper>());
- r->d()->object = object;
- r->d()->propertyType = propType.iface();
- void *args[] = { &r->d()->property(), nullptr };
- QMetaObject::metacall(object, QMetaObject::ReadProperty, propId, args);
- return r.asReturnedValue();
+ return engine->memoryManager->allocate<QmlListWrapper>(object, propId, propType)
+ ->asReturnedValue();
}
-ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, const QQmlListProperty<QObject> &prop, QMetaType propType)
+ReturnedValue QmlListWrapper::create(
+ ExecutionEngine *engine, const QQmlListProperty<QObject> &prop, QMetaType propType)
{
- Scope scope(engine);
+ return engine->memoryManager->allocate<QmlListWrapper>(prop.object, prop, propType)
+ ->asReturnedValue();
+}
- Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocate<QmlListWrapper>());
- r->d()->object = prop.object;
- r->d()->property() = prop;
- r->d()->propertyType = propType.iface();
- return r.asReturnedValue();
+ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, QMetaType propType)
+{
+ return engine->memoryManager->allocate<QmlListWrapper>(propType)->asReturnedValue();
}
QVariant QmlListWrapper::toVariant() const
{
- if (!d()->object)
+ if (!d()->object())
return QVariant();
return QVariant::fromValue(toListReference());
@@ -71,11 +166,10 @@ QVariant QmlListWrapper::toVariant() const
QQmlListReference QmlListWrapper::toListReference() const
{
- Heap::QmlListWrapper *wrapper = d();
- return QQmlListReferencePrivate::init(wrapper->property(), QMetaType(wrapper->propertyType));
+ const Heap::QmlListWrapper *wrapper = d();
+ return QQmlListReferencePrivate::init(*wrapper->property(), wrapper->propertyType());
}
-
ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QmlListWrapper>());
@@ -83,12 +177,14 @@ ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const
QV4::ExecutionEngine *v4 = w->engine();
if (id.isArrayIndex()) {
- uint index = id.asArrayIndex();
- quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
- if (index < count && w->d()->property().at) {
+ const uint index = id.asArrayIndex();
+ const quint32 count = w->d()->property()->count
+ ? w->d()->property()->count(w->d()->property())
+ : 0;
+ if (index < count && w->d()->property()->at) {
if (hasProperty)
*hasProperty = true;
- return QV4::QObjectWrapper::wrap(v4, w->d()->property().at(&w->d()->property(), index));
+ return QV4::QObjectWrapper::wrap(v4, w->d()->property()->at(w->d()->property(), index));
}
if (hasProperty)
@@ -99,6 +195,14 @@ ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const
return Object::virtualGet(m, id, receiver, hasProperty);
}
+qint64 QmlListWrapper::virtualGetLength(const Managed *m)
+{
+ Q_ASSERT(m->as<QmlListWrapper>());
+ QQmlListProperty<QObject> *property = static_cast<const QmlListWrapper *>(m)->d()->property();
+ Q_ASSERT(property);
+ return property->count ? property->count(property) : 0;
+}
+
bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
Q_ASSERT(m->as<QmlListWrapper>());
@@ -106,7 +210,7 @@ bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value,
const auto *w = static_cast<const QmlListWrapper *>(m);
QV4::ExecutionEngine *v4 = w->engine();
- QQmlListProperty<QObject> *prop = &(w->d()->property());
+ QQmlListProperty<QObject> *prop = w->d()->property();
if (id.isArrayIndex()) {
if (!prop->count || !prop->replace)
@@ -146,14 +250,16 @@ PropertyKey QmlListWrapperOwnPropertyKeyIterator::next(const Object *o, Property
{
const QmlListWrapper *w = static_cast<const QmlListWrapper *>(o);
- quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
+ quint32 count = w->d()->property()->count ? w->d()->property()->count(w->d()->property()) : 0;
if (arrayIndex < count) {
uint index = arrayIndex;
++arrayIndex;
if (attrs)
*attrs = QV4::Attr_Data;
- if (pd)
- pd->value = QV4::QObjectWrapper::wrap(w->engine(), w->d()->property().at(&w->d()->property(), index));
+ if (pd) {
+ pd->value = QV4::QObjectWrapper::wrap(
+ w->engine(), w->d()->property()->at(w->d()->property(), index));
+ }
return PropertyKey::fromArrayIndex(index);
} else if (memberIndex == 0) {
++memberIndex;
@@ -194,7 +300,7 @@ ReturnedValue PropertyListPrototype::method_pop(const FunctionObject *b, const V
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->count)
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
@@ -224,7 +330,7 @@ ReturnedValue PropertyListPrototype::method_push(const FunctionObject *b, const
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->append)
return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
if (!property->count)
@@ -247,7 +353,11 @@ ReturnedValue PropertyListPrototype::method_push(const FunctionObject *b, const
property->append(property, argv[i].as<QV4::QObjectWrapper>()->object());
}
- return Encode(uint(length + argc));
+ const auto actualLength = property->count(property);
+ if (actualLength != length + argc)
+ qmlWarning(property->object) << "List didn't append all objects";
+
+ return Encode(uint(actualLength));
}
ReturnedValue PropertyListPrototype::method_shift(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -260,7 +370,7 @@ ReturnedValue PropertyListPrototype::method_shift(const FunctionObject *b, const
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->count)
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
@@ -294,7 +404,7 @@ ReturnedValue PropertyListPrototype::method_splice(const FunctionObject *b, cons
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->count)
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
@@ -386,7 +496,7 @@ ReturnedValue PropertyListPrototype::method_unshift(const FunctionObject *b, con
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->count)
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
@@ -408,12 +518,16 @@ ReturnedValue PropertyListPrototype::method_unshift(const FunctionObject *b, con
for (int i = 0; i < argc; ++i)
property->append(property, nullptr);
+ if (property->count(property) != argc + len)
+ return scope.engine->throwTypeError(u"List doesn't append null objects"_s);
for (qsizetype k = len; k > 0; --k)
property->replace(property, k + argc - 1, property->at(property, k - 1));
- for (int i = 0; i < argc; ++i)
- property->replace(property, i, argv[i].as<QObjectWrapper>()->object());
+ for (int i = 0; i < argc; ++i) {
+ const auto *wrapper = argv[i].as<QObjectWrapper>();
+ property->replace(property, i, wrapper ? wrapper->object() : nullptr);
+ }
return Encode(uint(len + argc));
}
@@ -446,7 +560,7 @@ ReturnedValue firstOrLastIndexOf(const FunctionObject *b, const Value *thisObjec
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->count)
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
@@ -532,7 +646,7 @@ ReturnedValue PropertyListPrototype::method_sort(const FunctionObject *b, const
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->count)
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
@@ -568,7 +682,7 @@ ReturnedValue PropertyListPrototype::method_get_length(const FunctionObject *b,
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->count)
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
@@ -590,7 +704,7 @@ ReturnedValue PropertyListPrototype::method_set_length(const FunctionObject *b,
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
bool ok = false;
const uint newLength = argc ? argv[0].asArrayLength(&ok) : 0;
@@ -620,11 +734,18 @@ ReturnedValue PropertyListPrototype::method_set_length(const FunctionObject *b,
}
if (!property->append)
- return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
+ return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
for (uint i = count; i < newLength; ++i)
property->append(property, nullptr);
+ count = property->count(property);
+ if (!qIsAtMostUintLimit(count))
+ return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
+
+ if (uint(count) != newLength)
+ return scope.engine->throwTypeError(u"List doesn't append null objects"_s);
+
return true;
}
diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h
index 21240d6b98..55100e9a07 100644
--- a/src/qml/qml/qqmllistwrapper_p.h
+++ b/src/qml/qml/qqmllistwrapper_p.h
@@ -29,20 +29,33 @@ namespace QV4 {
namespace Heap {
-struct QmlListWrapper : Object {
- void init();
+struct QmlListWrapper : Object
+{
+ void init(QMetaType propertyType);
+ void init(QObject *object, int propertyId, QMetaType propertyType);
+ void init(QObject *object, const QQmlListProperty<QObject> &list, QMetaType propertyType);
void destroy();
- QV4QPointer<QObject> object;
- QQmlListProperty<QObject> &property() {
- return *reinterpret_cast<QQmlListProperty<QObject>*>(propertyData);
+ QObject *object() const { return m_object.data(); }
+ QMetaType propertyType() const { return QMetaType(m_propertyType); }
+
+ const QQmlListProperty<QObject> *property() const
+ {
+ return reinterpret_cast<const QQmlListProperty<QObject>*>(m_propertyData);
}
- // interface instead of QMetaType to keep class a POD
- const QtPrivate::QMetaTypeInterface *propertyType;
+ QQmlListProperty<QObject> *property()
+ {
+ return reinterpret_cast<QQmlListProperty<QObject>*>(m_propertyData);
+ }
private:
- void *propertyData[sizeof(QQmlListProperty<QObject>)/sizeof(void*)];
+ void *m_propertyData[sizeof(QQmlListProperty<QObject>)/sizeof(void*)];
+
+ QV4QPointer<QObject> m_object;
+
+ // interface instead of QMetaType to keep class a POD
+ const QtPrivate::QMetaTypeInterface *m_propertyType;
};
}
@@ -56,11 +69,13 @@ struct Q_QML_EXPORT QmlListWrapper : Object
static ReturnedValue create(ExecutionEngine *engine, QObject *object, int propId, QMetaType propType);
static ReturnedValue create(ExecutionEngine *engine, const QQmlListProperty<QObject> &prop, QMetaType propType);
+ static ReturnedValue create(ExecutionEngine *engine, QMetaType propType);
QVariant toVariant() const;
QQmlListReference toListReference() const;
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
+ static qint64 virtualGetLength(const Managed *m);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
};
diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp
index a7eeb3b4d7..3249f5a6eb 100644
--- a/src/qml/qml/qqmllocale.cpp
+++ b/src/qml/qml/qqmllocale.cpp
@@ -13,13 +13,12 @@
#include <private/qv4dateobject_p.h>
#include <private/qv4numberobject_p.h>
#include <private/qv4stringobject_p.h>
+#include <private/qqmlvaluetypewrapper_p.h>
QT_BEGIN_NAMESPACE
using namespace QV4;
-DEFINE_OBJECT_VTABLE(QQmlLocaleData);
-
#define THROW_ERROR(string) \
do { \
return scope.engine->throwError(QString::fromUtf8(string)); \
@@ -27,13 +26,18 @@ DEFINE_OBJECT_VTABLE(QQmlLocaleData);
#define GET_LOCALE_DATA_RESOURCE(OBJECT) \
- QV4::Scoped<QQmlLocaleData> r(scope, OBJECT.as<QQmlLocaleData>()); \
+ QLocale *r = [&]() { \
+ QV4::Scoped<QQmlValueTypeWrapper> r(scope, OBJECT.as<QQmlValueTypeWrapper>()); \
+ return r ? r->cast<QLocale>() : nullptr; \
+ }(); \
if (!r) \
THROW_ERROR("Not a valid Locale object")
static bool isLocaleObject(const QV4::Value &val)
{
- return val.as<QQmlLocaleData>();
+ if (const QV4::QQmlValueTypeWrapper *wrapper = val.as<QQmlValueTypeWrapper>())
+ return wrapper->type() == QMetaType::fromType<QLocale>();
+ return false;
}
//--------------
@@ -78,16 +82,16 @@ ReturnedValue QQmlDateExtension::method_toLocaleString(const QV4::FunctionObject
if (argc == 2) {
if (String *s = argv[1].stringValue()) {
QString format = s->toQString();
- formattedDt = r->d()->locale->toString(dt, format);
+ formattedDt = r->toString(dt, format);
} else if (argv[1].isNumber()) {
quint32 intFormat = argv[1].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- formattedDt = r->d()->locale->toString(dt, format);
+ formattedDt = r->toString(dt, format);
} else {
THROW_ERROR("Locale: Date.toLocaleString(): Invalid datetime format");
}
} else {
- formattedDt = r->d()->locale->toString(dt, enumFormat);
+ formattedDt = r->toString(dt, enumFormat);
}
RETURN_RESULT(scope.engine->newString(formattedDt));
@@ -122,16 +126,16 @@ ReturnedValue QQmlDateExtension::method_toLocaleTimeString(const QV4::FunctionOb
if (argc == 2) {
if (String *s = argv[1].stringValue()) {
QString format = s->toQString();
- formattedTime = r->d()->locale->toString(time, format);
+ formattedTime = r->toString(time, format);
} else if (argv[1].isNumber()) {
quint32 intFormat = argv[1].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- formattedTime = r->d()->locale->toString(time, format);
+ formattedTime = r->toString(time, format);
} else {
THROW_ERROR("Locale: Date.toLocaleTimeString(): Invalid time format");
}
} else {
- formattedTime = r->d()->locale->toString(time, enumFormat);
+ formattedTime = r->toString(time, enumFormat);
}
RETURN_RESULT(scope.engine->newString(formattedTime));
@@ -166,16 +170,16 @@ ReturnedValue QQmlDateExtension::method_toLocaleDateString(const QV4::FunctionOb
if (argc == 2) {
if (String *s = argv[1].stringValue()) {
QString format = s->toQString();
- formattedDate = r->d()->locale->toString(date, format);
+ formattedDate = r->toString(date, format);
} else if (argv[1].isNumber()) {
quint32 intFormat = argv[1].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- formattedDate = r->d()->locale->toString(date, format);
+ formattedDate = r->toString(date, format);
} else {
THROW_ERROR("Locale: Date.loLocaleDateString(): Invalid date format");
}
} else {
- formattedDate = r->d()->locale->toString(date, enumFormat);
+ formattedDate = r->toString(date, enumFormat);
}
RETURN_RESULT(scope.engine->newString(formattedDate));
@@ -205,16 +209,16 @@ ReturnedValue QQmlDateExtension::method_fromLocaleString(const QV4::FunctionObje
if (argc == 3) {
if (String *s = argv[2].stringValue()) {
QString format = s->toQString();
- dt = r->d()->locale->toDateTime(dateString, format);
+ dt = r->toDateTime(dateString, format);
} else if (argv[2].isNumber()) {
quint32 intFormat = argv[2].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- dt = r->d()->locale->toDateTime(dateString, format);
+ dt = r->toDateTime(dateString, format);
} else {
THROW_ERROR("Locale: Date.fromLocaleString(): Invalid datetime format");
}
} else {
- dt = r->d()->locale->toDateTime(dateString, enumFormat);
+ dt = r->toDateTime(dateString, enumFormat);
}
RETURN_RESULT(engine->newDateObject(dt));
@@ -247,16 +251,16 @@ ReturnedValue QQmlDateExtension::method_fromLocaleTimeString(const QV4::Function
if (argc == 3) {
if (String *s = argv[2].stringValue()) {
QString format = s->toQString();
- tm = r->d()->locale->toTime(dateString, format);
+ tm = r->toTime(dateString, format);
} else if (argv[2].isNumber()) {
quint32 intFormat = argv[2].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- tm = r->d()->locale->toTime(dateString, format);
+ tm = r->toTime(dateString, format);
} else {
THROW_ERROR("Locale: Date.fromLocaleTimeString(): Invalid datetime format");
}
} else {
- tm = r->d()->locale->toTime(dateString, enumFormat);
+ tm = r->toTime(dateString, enumFormat);
}
QDateTime dt;
@@ -293,16 +297,16 @@ ReturnedValue QQmlDateExtension::method_fromLocaleDateString(const QV4::Function
if (argc == 3) {
if (String *s = argv[2].stringValue()) {
QString format = s->toQString();
- dt = r->d()->locale->toDate(dateString, format);
+ dt = r->toDate(dateString, format);
} else if (argv[2].isNumber()) {
quint32 intFormat = argv[2].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- dt = r->d()->locale->toDate(dateString, format);
+ dt = r->toDate(dateString, format);
} else {
THROW_ERROR("Locale: Date.fromLocaleDateString(): Invalid datetime format");
}
} else {
- dt = r->d()->locale->toDate(dateString, enumFormat);
+ dt = r->toDate(dateString, enumFormat);
}
RETURN_RESULT(engine->newDateObject(dt.startOfDay(QTimeZone::UTC)));
@@ -363,7 +367,7 @@ QV4::ReturnedValue QQmlNumberExtension::method_toLocaleString(const QV4::Functio
prec = argv[2].toInt32();
}
- RETURN_RESULT(scope.engine->newString(r->d()->locale->toString(number, (char)format, prec)));
+ RETURN_RESULT(scope.engine->newString(r->toString(number, (char)format, prec)));
}
ReturnedValue QQmlNumberExtension::method_toLocaleCurrencyString(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
@@ -392,7 +396,7 @@ ReturnedValue QQmlNumberExtension::method_toLocaleCurrencyString(const QV4::Func
symbol = argv[1].toQStringNoThrow();
}
- RETURN_RESULT(scope.engine->newString(r->d()->locale->toCurrencyString(number, symbol)));
+ RETURN_RESULT(scope.engine->newString(r->toCurrencyString(number, symbol)));
}
ReturnedValue QQmlNumberExtension::method_fromLocaleString(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *argv, int argc)
@@ -409,7 +413,7 @@ ReturnedValue QQmlNumberExtension::method_fromLocaleString(const QV4::FunctionOb
THROW_ERROR("Locale: Number.fromLocaleString(): Invalid arguments");
GET_LOCALE_DATA_RESOURCE(argv[0]);
- locale = *r->d()->locale;
+ locale = *r;
numberIdx = 1;
}
@@ -430,382 +434,161 @@ ReturnedValue QQmlNumberExtension::method_fromLocaleString(const QV4::FunctionOb
//--------------
// Locale object
-ReturnedValue QQmlLocaleData::method_get_firstDayOfWeek(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+void QQmlLocaleValueType::formattedDataSize(QQmlV4FunctionPtr args) const
{
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
- int fdow = int(locale->firstDayOfWeek());
- if (fdow == 7)
- fdow = 0; // Qt::Sunday = 7, but Sunday is 0 in JS Date
- RETURN_RESULT(fdow);
-}
+ QV4::Scope scope(args->v4engine());
+ const auto doThrow = [&](const QString &message) {
+ args->setReturnValue(scope.engine->throwError(message));
+ };
-ReturnedValue QQmlLocaleData::method_get_numberOptions(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) {
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
- int numberOptions = int(locale->numberOptions());
- RETURN_RESULT(numberOptions);
-}
+ const int argc = args->length();
-ReturnedValue QQmlLocaleData::method_set_numberOptions(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) {
- QV4::Scope scope(b);
- QLocale *locale = getThisLocale(scope, thisObject);
- int const numberOptions = argc ? int(argv[0].toNumber()) : QLocale::DefaultNumberOptions;
- locale->setNumberOptions(QLocale::NumberOptions {numberOptions});
- return Encode::undefined();
-}
+ if (argc < 1 || argc > 3) {
+ doThrow(QString::fromLatin1(
+ "Locale: formattedDataSize(): Expected 1-3 arguments, but received %1")
+ .arg(argc));
+ return;
+ }
-ReturnedValue QQmlLocaleData::method_get_formattedDataSize(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
-{
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
+ QV4::ScopedValue arg0(scope, (*args)[0]);
+ bool mismatched0 = false;
+ if (!arg0->isNumber()) {
+ // ### Qt7: Throw an exception here, so that we don't have to handle mismatched0 below.
+ qWarning() << "Locale: formattedDataSize(): Invalid argument ('bytes' should be a number)";
+ if (argc == 1) {
+ args->setReturnValue(
+ scope.engine->newString(locale.formattedDataSize(qint64(arg0->toInteger())))
+ ->asReturnedValue());
+ return;
+ }
- if (argc < 1 || argc > 3) {
- THROW_ERROR(QString::fromLatin1(
- "Locale: formattedDataSize(): Expected 1-3 arguments, but received %1").arg(argc).toLatin1());
+ mismatched0 = true;
}
- const qint64 bytes = static_cast<qint64>(argv[0].toInteger());
- if (argc == 1)
- RETURN_RESULT(scope.engine->newString(locale->formattedDataSize(bytes)));
+ // Anything can be coerced to a number, for better or worse ...
+ Q_ASSERT(argc >= 2);
- int precision = 0;
- if (argc >= 2) {
- if (!argv[1].isInteger())
- THROW_ERROR("Locale: formattedDataSize(): Invalid argument ('precision' must be an int)");
+ QV4::ScopedValue arg1(scope, (*args)[1]);
+ if (!arg1->isInteger()) {
+ doThrow(QLatin1String(
+ "Locale: formattedDataSize(): Invalid argument ('precision' must be an int)"));
+ return;
+ }
+
+ if (mismatched0) {
+ if (argc == 2) {
+ const QString result = locale.formattedDataSize(
+ qint64(arg0->toInteger()), arg1->integerValue());
+ args->setReturnValue(scope.engine->newString(result)->asReturnedValue());
+ return;
+ }
- precision = argv[1].toInt32();
- if (argc == 2)
- RETURN_RESULT(scope.engine->newString(locale->formattedDataSize(bytes, precision)));
+ QV4::ScopedValue arg2(scope, (*args)[2]);
+ if (arg2->isNumber()) {
+ const QString result = locale.formattedDataSize(
+ qint64(arg0->toInteger()), arg1->integerValue(),
+ QLocale::DataSizeFormats(arg2->integerValue()));
+ args->setReturnValue(scope.engine->newString(result)->asReturnedValue());
+ return;
+ }
}
- // argc >= 3
- if (!argv[2].isNumber())
- THROW_ERROR("Locale: formattedDataSize(): Invalid argument ('format' must be DataSizeFormat)");
+ Q_ASSERT(argc == 3);
+ Q_ASSERT(!QV4::ScopedValue(scope, (*args)[2])->isNumber());
- const quint32 intFormat = argv[2].toUInt32();
- const auto format = QLocale::DataSizeFormats(intFormat);
- RETURN_RESULT(scope.engine->newString(locale->formattedDataSize(bytes, precision, format)));
+ doThrow(QLatin1String(
+ "Locale: formattedDataSize(): Invalid argument ('format' must be DataSizeFormat)"));
}
-ReturnedValue QQmlLocaleData::method_get_measurementSystem(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+static QQmlLocale::DayOfWeek qtDayToQmlDay(Qt::DayOfWeek day)
{
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
- return QV4::Encode(locale->measurementSystem());
+ return day == Qt::Sunday ? QQmlLocale::DayOfWeek::Sunday : QQmlLocale::DayOfWeek(day);
}
-ReturnedValue QQmlLocaleData::method_get_textDirection(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+QQmlLocale::DayOfWeek QQmlLocaleValueType::firstDayOfWeek() const
{
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
-
- return QV4::Encode(locale->textDirection());
+ return qtDayToQmlDay(locale.firstDayOfWeek());
}
-ReturnedValue QQmlLocaleData::method_get_weekDays(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+QList<QQmlLocale::DayOfWeek> QQmlLocaleValueType::weekDays() const
{
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
-
- QList<Qt::DayOfWeek> days = locale->weekdays();
-
- QV4::ScopedArrayObject result(scope, scope.engine->newArrayObject());
- result->arrayReserve(days.size());
- for (int i = 0; i < days.size(); ++i) {
- int day = days.at(i);
- if (day == 7) // JS Date days in range 0(Sunday) to 6(Saturday)
- day = 0;
- result->arrayPut(i, QV4::Value::fromInt32(day));
- }
- result->setArrayLengthUnchecked(days.size());
-
- return result.asReturnedValue();
+ const QList<Qt::DayOfWeek> days = locale.weekdays();
+ QList<QQmlLocale::DayOfWeek> result;
+ result.reserve(days.size());
+ for (Qt::DayOfWeek day : days)
+ result.append(qtDayToQmlDay(day));
+ return result;
}
-ReturnedValue QQmlLocaleData::method_get_uiLanguages(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+void QQmlLocaleValueType::toString(QQmlV4FunctionPtr args) const
{
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
+ Scope scope(args->v4engine());
+ const auto doThrow = [&](const QString &message) {
+ args->setReturnValue(scope.engine->throwError(message));
+ };
- QStringList langs = locale->uiLanguages();
- QV4::ScopedArrayObject result(scope, scope.engine->newArrayObject());
- result->arrayReserve(langs.size());
- QV4::ScopedValue v(scope);
- for (int i = 0; i < langs.size(); ++i)
- result->arrayPut(i, (v = scope.engine->newString(langs.at(i))));
+ const int argc = args->length();
- result->setArrayLengthUnchecked(langs.size());
-
- return result.asReturnedValue();
-}
+ // toString()
+ Q_ASSERT(argc > 0);
-ReturnedValue QQmlLocaleData::method_currencySymbol(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
-{
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
-
- if (argc > 1)
- THROW_ERROR("Locale: currencySymbol(): Invalid arguments");
-
- QLocale::CurrencySymbolFormat format = QLocale::CurrencySymbol;
- if (argc == 1) {
- quint32 intFormat = argv[0].toNumber();
- format = QLocale::CurrencySymbolFormat(intFormat);
+ if (argc > 3) {
+ doThrow(QString::fromLatin1("Locale: toString(): Expected 1-3 arguments, but received %1")
+ .arg(argc));
+ return;
}
- RETURN_RESULT(scope.engine->newString(locale->currencySymbol(format)));
-}
-
-#define LOCALE_FORMAT(FUNC) \
-ReturnedValue QQmlLocaleData::method_ ##FUNC (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) { \
- QV4::Scope scope(b); \
- const QLocale *locale = getThisLocale(scope, thisObject); \
- if (!locale) \
- return Encode::undefined(); \
- if (argc > 1) \
- THROW_ERROR("Locale: " #FUNC "(): Invalid arguments"); \
- QLocale::FormatType format = QLocale::LongFormat;\
- if (argc == 1) { \
- quint32 intFormat = argv[0].toUInt32(); \
- format = QLocale::FormatType(intFormat); \
- } \
- RETURN_RESULT(scope.engine->newString(locale-> FUNC (format))); \
-}
+ QV4::ScopedValue arg0(scope, (*args)[0]);
+ if (arg0->isNumber()) {
-LOCALE_FORMAT(dateTimeFormat)
-LOCALE_FORMAT(timeFormat)
-LOCALE_FORMAT(dateFormat)
-
-// +1 added to idx because JS is 0-based, whereas QLocale months begin at 1.
-#define LOCALE_FORMATTED_MONTHNAME(VARIABLE) \
-ReturnedValue QQmlLocaleData::method_ ## VARIABLE (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) {\
- Scope scope(b); \
- const QLocale *locale = getThisLocale(scope, thisObject); \
- if (!locale) \
- return Encode::undefined(); \
- if (argc < 1 || argc > 2) \
- THROW_ERROR("Locale: " #VARIABLE "(): Invalid arguments"); \
- QLocale::FormatType enumFormat = QLocale::LongFormat; \
- int idx = argv[0].toInt32() + 1; \
- if (idx < 1 || idx > 12) \
- THROW_ERROR("Locale: Invalid month"); \
- QString name; \
- if (argc == 2) { \
- if (argv[1].isNumber()) { \
- quint32 intFormat = argv[1].toUInt32(); \
- QLocale::FormatType format = QLocale::FormatType(intFormat); \
- name = locale-> VARIABLE(idx, format); \
- } else { \
- THROW_ERROR("Locale: Invalid datetime format"); \
- } \
- } else { \
- name = locale-> VARIABLE(idx, enumFormat); \
- } \
- RETURN_RESULT(scope.engine->newString(name)); \
-}
+ // toString(int)
+ // toString(double)
+ Q_ASSERT(argc != 1);
-// 0 -> 7 as Qt::Sunday is 7, but Sunday is 0 in JS Date
-#define LOCALE_FORMATTED_DAYNAME(VARIABLE) \
-ReturnedValue QQmlLocaleData::method_ ## VARIABLE (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) {\
- Scope scope(b); \
- const QLocale *locale = getThisLocale(scope, thisObject); \
- if (!locale) \
- return Encode::undefined(); \
- if (argc < 1 || argc > 2) \
- THROW_ERROR("Locale: " #VARIABLE "(): Invalid arguments"); \
- QLocale::FormatType enumFormat = QLocale::LongFormat; \
- int idx = argv[0].toInt32(); \
- if (idx < 0 || idx > 7) \
- THROW_ERROR("Locale: Invalid day"); \
- if (idx == 0) idx = 7; \
- QString name; \
- if (argc == 2) { \
- if (argv[1].isNumber()) { \
- quint32 intFormat = argv[1].toUInt32(); \
- QLocale::FormatType format = QLocale::FormatType(intFormat); \
- name = locale-> VARIABLE(idx, format); \
- } else { \
- THROW_ERROR("Locale: Invalid datetime format"); \
- } \
- } else { \
- name = locale-> VARIABLE(idx, enumFormat); \
- } \
- RETURN_RESULT(scope.engine->newString(name)); \
-}
+ QV4::ScopedValue arg1(scope, (*args)[1]);
+ if (!arg1->isString()) {
+ doThrow(QLatin1String("Locale: the second argument to the toString overload "
+ "whose first argument is a double should be a char"));
+ return;
+ }
-LOCALE_FORMATTED_MONTHNAME(monthName)
-LOCALE_FORMATTED_MONTHNAME(standaloneMonthName)
-LOCALE_FORMATTED_DAYNAME(dayName)
-LOCALE_FORMATTED_DAYNAME(standaloneDayName)
+ // toString(double, const QString &)
+ // toString(double, const QString &, int)
+ Q_ASSERT(argc == 3);
+ Q_ASSERT(!QV4::ScopedValue(scope, (*args)[2])->isInteger());
-ReturnedValue QQmlLocaleData::method_toString(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
-{
- Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
-
- if (argc == 0) {
- // As a special (undocumented) case, when called with no arguments,
- // just forward to QDebug. This makes it consistent with other types
- // in JS that can be converted to a string via toString().
- RETURN_RESULT(scope.engine->newString(QDebug::toString(*locale)));
+ doThrow(QLatin1String("Locale: the third argument to the toString overload "
+ "whose first argument is a double should be an int"));
+ return;
}
- if (argc > 3) {
- return scope.engine->throwError(QString::fromLatin1(
- "Locale: toString(): Expected 1-3 arguments, but received %1").arg(argc));
- }
-
- if (argv[0].isNumber()) {
- if (argv[0].isInteger()) {
- // toString(int)
- RETURN_RESULT(scope.engine->newString(locale->toString(argv[0].toInt32())));
- } else {
- // toString(double[, char][, int])
- const double number = argv[0].toNumber();
- if (argc == 1)
- RETURN_RESULT(scope.engine->newString(locale->toString(number)));
-
- if (!argv[1].isString()) {
- THROW_ERROR("Locale: the second argument to the toString overload "
- "whose first argument is a double should be a char");
- }
- const char format = argv[1].toQString().at(0).toLatin1();
-
- switch (argc) {
- case 2:
- RETURN_RESULT(scope.engine->newString(locale->toString(number, format)));
- case 3:
- if (!argv[2].isInteger()) {
- THROW_ERROR("Locale: the third argument to the toString overload "
- "whose first argument is a double should be an int");
- }
-
- const int precision = argv[2].toInt32();
- RETURN_RESULT(scope.engine->newString(locale->toString(number, format, precision)));
- }
- }
- } else if (const DateObject *dateObject = argv[0].as<DateObject>()) {
- // toString(Date, string) or toString(Date[, FormatType])
+ if (arg0->as<DateObject>()) {
if (argc > 2) {
- return scope.engine->throwError(QString::fromLatin1(
- "Locale: the toString() overload that takes a Date as its first "
- "argument expects 1 or 2 arguments, but received %1").arg(argc));
- }
-
- if (argc == 2 && argv[1].isString()) {
- RETURN_RESULT(scope.engine->newString(locale->toString(
- dateObject->toQDateTime(), argv[1].toQString())));
- }
-
- if (argc == 2 && !argv[1].isNumber()) {
- THROW_ERROR("Locale: the second argument to the toString overloads whose "
- "first argument is a Date should be a string or FormatType");
+ doThrow(QString::fromLatin1(
+ "Locale: the toString() overload that takes a Date as its first "
+ "argument expects 1 or 2 arguments, but received %1").arg(argc));
+ return;
}
- const QLocale::FormatType format = argc == 2
- ? QLocale::FormatType(argv[1].toNumber()) : QLocale::LongFormat;
- RETURN_RESULT(scope.engine->newString(locale->toString(dateObject->toQDateTime(), format)));
- }
+ // toString(QDateTime)
+ Q_ASSERT(argc == 2);
+ QV4::ScopedValue arg1(scope, (*args)[1]);
- THROW_ERROR("Locale: toString() expects either an int, double, or Date as its first argument");
-}
+ // toString(QDateTime, QString)
+ Q_ASSERT(!arg1->isString());
-#define LOCALE_STRING_PROPERTY(VARIABLE) \
-ReturnedValue QQmlLocaleData::method_get_ ## VARIABLE (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) \
-{ \
- Scope scope(b); \
- const QLocale *locale = getThisLocale(scope, thisObject); \
- if (!locale) \
- return Encode::undefined(); \
- RETURN_RESULT(scope.engine->newString(locale-> VARIABLE()));\
-}
-
-LOCALE_STRING_PROPERTY(name)
-LOCALE_STRING_PROPERTY(nativeLanguageName)
-QT_IGNORE_DEPRECATIONS(LOCALE_STRING_PROPERTY(nativeCountryName))
-LOCALE_STRING_PROPERTY(nativeTerritoryName)
-LOCALE_STRING_PROPERTY(decimalPoint)
-LOCALE_STRING_PROPERTY(groupSeparator)
-LOCALE_STRING_PROPERTY(percent)
-LOCALE_STRING_PROPERTY(zeroDigit)
-LOCALE_STRING_PROPERTY(negativeSign)
-LOCALE_STRING_PROPERTY(positiveSign)
-LOCALE_STRING_PROPERTY(exponential)
-LOCALE_STRING_PROPERTY(amText)
-LOCALE_STRING_PROPERTY(pmText)
-
-class QV4LocaleDataDeletable : public QV4::ExecutionEngine::Deletable
-{
-public:
- QV4LocaleDataDeletable(QV4::ExecutionEngine *engine);
- ~QV4LocaleDataDeletable();
+ // toString(QDateTime, QLocale::FormatType)
+ Q_ASSERT(!arg1->isNumber());
- QV4::PersistentValue prototype;
-};
+ doThrow(QLatin1String("Locale: the second argument to the toString overloads whose "
+ "first argument is a Date should be a string or FormatType"));
+ return;
+ }
-QV4LocaleDataDeletable::QV4LocaleDataDeletable(QV4::ExecutionEngine *engine)
-{
- QV4::Scope scope(engine);
- QV4::Scoped<QV4::Object> o(scope, engine->newObject());
-
- o->defineDefaultProperty(QStringLiteral("dateFormat"), QQmlLocaleData::method_dateFormat, 0);
- o->defineDefaultProperty(QStringLiteral("standaloneDayName"), QQmlLocaleData::method_standaloneDayName, 0);
- o->defineDefaultProperty(QStringLiteral("standaloneMonthName"), QQmlLocaleData::method_standaloneMonthName, 0);
- o->defineDefaultProperty(QStringLiteral("dayName"), QQmlLocaleData::method_dayName, 0);
- o->defineDefaultProperty(QStringLiteral("timeFormat"), QQmlLocaleData::method_timeFormat, 0);
- o->defineDefaultProperty(QStringLiteral("monthName"), QQmlLocaleData::method_monthName, 0);
- o->defineDefaultProperty(QStringLiteral("toString"), QQmlLocaleData::method_toString, 0);
- o->defineDefaultProperty(QStringLiteral("currencySymbol"), QQmlLocaleData::method_currencySymbol, 0);
- o->defineDefaultProperty(QStringLiteral("dateTimeFormat"), QQmlLocaleData::method_dateTimeFormat, 0);
- o->defineDefaultProperty(QStringLiteral("formattedDataSize"), QQmlLocaleData::method_get_formattedDataSize, 0);
- o->defineAccessorProperty(QStringLiteral("name"), QQmlLocaleData::method_get_name, nullptr);
- o->defineAccessorProperty(QStringLiteral("positiveSign"), QQmlLocaleData::method_get_positiveSign, nullptr);
- o->defineAccessorProperty(QStringLiteral("uiLanguages"), QQmlLocaleData::method_get_uiLanguages, nullptr);
- o->defineAccessorProperty(QStringLiteral("firstDayOfWeek"), QQmlLocaleData::method_get_firstDayOfWeek, nullptr);
- o->defineAccessorProperty(QStringLiteral("pmText"), QQmlLocaleData::method_get_pmText, nullptr);
- o->defineAccessorProperty(QStringLiteral("percent"), QQmlLocaleData::method_get_percent, nullptr);
- o->defineAccessorProperty(QStringLiteral("textDirection"), QQmlLocaleData::method_get_textDirection, nullptr);
- o->defineAccessorProperty(QStringLiteral("weekDays"), QQmlLocaleData::method_get_weekDays, nullptr);
- o->defineAccessorProperty(QStringLiteral("negativeSign"), QQmlLocaleData::method_get_negativeSign, nullptr);
- o->defineAccessorProperty(QStringLiteral("groupSeparator"), QQmlLocaleData::method_get_groupSeparator, nullptr);
- o->defineAccessorProperty(QStringLiteral("decimalPoint"), QQmlLocaleData::method_get_decimalPoint, nullptr);
- o->defineAccessorProperty(QStringLiteral("nativeLanguageName"), QQmlLocaleData::method_get_nativeLanguageName, nullptr);
- o->defineAccessorProperty(QStringLiteral("nativeCountryName"), QQmlLocaleData::method_get_nativeCountryName, nullptr);
- o->defineAccessorProperty(QStringLiteral("nativeTerritoryName"), QQmlLocaleData::method_get_nativeTerritoryName, nullptr);
- o->defineAccessorProperty(QStringLiteral("zeroDigit"), QQmlLocaleData::method_get_zeroDigit, nullptr);
- o->defineAccessorProperty(QStringLiteral("amText"), QQmlLocaleData::method_get_amText, nullptr);
- o->defineAccessorProperty(QStringLiteral("measurementSystem"), QQmlLocaleData::method_get_measurementSystem, nullptr);
- o->defineAccessorProperty(QStringLiteral("exponential"), QQmlLocaleData::method_get_exponential, nullptr);
- o->defineAccessorProperty(QStringLiteral("numberOptions"), QQmlLocaleData::method_get_numberOptions, QQmlLocaleData::method_set_numberOptions);
-
- prototype.set(engine, o);
+ doThrow(QLatin1String("Locale: toString() expects either an int, double, "
+ "or Date as its first argument"));
}
-QV4LocaleDataDeletable::~QV4LocaleDataDeletable()
-{
-}
-
-V4_DEFINE_EXTENSION(QV4LocaleDataDeletable, localeV4Data);
-
/*!
\qmltype Locale
//! \instantiates QQmlLocale
@@ -862,19 +645,17 @@ V4_DEFINE_EXTENSION(QV4LocaleDataDeletable, localeV4Data);
can use the following enumeration values to specify the formatting of
the string representation for a Date object.
- \list
- \li Locale.LongFormat The long version of day and month names; for
- example, returning "January" as a month name.
- \li Locale.ShortFormat The short version of day and month names; for
- example, returning "Jan" as a month name.
- \li Locale.NarrowFormat A special version of day and month names for
- use when space is limited; for example, returning "J" as a month
- name. Note that the narrow format might contain the same text for
- different months and days or it can even be an empty string if the
- locale doesn't support narrow names, so you should avoid using it
- for date formatting. Also, for the system locale this format is
- the same as ShortFormat.
- \endlist
+ \value Locale.LongFormat The long version of day and month names; for
+ example, returning "January" as a month name.
+ \value Locale.ShortFormat The short version of day and month names; for
+ example, returning "Jan" as a month name.
+ \value Locale.NarrowFormat A special version of day and month names for
+ use when space is limited; for example, returning "J" as a month
+ name. Note that the narrow format might contain the same text for
+ different months and days or it can even be an empty string if the
+ locale doesn't support narrow names, so you should avoid using it
+ for date formatting. Also, for the system locale this format is
+ the same as ShortFormat.
Additionally the double-to-string and string-to-double conversion functions are
@@ -902,21 +683,16 @@ V4_DEFINE_EXTENSION(QV4LocaleDataDeletable, localeV4Data);
QV4::ReturnedValue QQmlLocale::locale(ExecutionEngine *engine, const QString &localeName)
{
- QLocale qlocale;
- if (!localeName.isEmpty())
- qlocale = QLocale(localeName);
- return wrap(engine, qlocale);
-}
+ if (localeName.isEmpty()) {
+ return QQmlValueTypeWrapper::create(
+ engine, nullptr, &QQmlLocaleValueType::staticMetaObject,
+ QMetaType::fromType<QLocale>());
+ }
-QV4::ReturnedValue QQmlLocale::wrap(ExecutionEngine *v4, const QLocale &locale)
-{
- QV4::Scope scope(v4);
- QV4LocaleDataDeletable *d = localeV4Data(scope.engine);
- QV4::Scoped<QQmlLocaleData> wrapper(scope, v4->memoryManager->allocate<QQmlLocaleData>());
- *wrapper->d()->locale = locale;
- QV4::ScopedObject p(scope, d->prototype.value());
- wrapper->setPrototypeOf(p);
- return wrapper.asReturnedValue();
+ QLocale qlocale(localeName);
+ return QQmlValueTypeWrapper::create(
+ engine, &qlocale, &QQmlLocaleValueType::staticMetaObject,
+ QMetaType::fromType<QLocale>());
}
void QQmlLocale::registerStringLocaleCompare(QV4::ExecutionEngine *engine)
@@ -941,10 +717,10 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con
/*!
\qmlproperty string QtQml::Locale::name
- Holds the language and country of this locale as a
- string of the form "language_country", where
+ Holds the language and territory of this locale as a
+ string of the form "language_territory", where
language is a lowercase, two-letter ISO 639 language code,
- and country is an uppercase, two- or three-letter ISO 3166 country code.
+ and territory is an uppercase, two- or three-letter ISO 3166 territory code.
*/
/*!
@@ -1096,15 +872,13 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con
Holds the first day of the week according to the current locale.
- \list
- \li Locale.Sunday = 0
- \li Locale.Monday = 1
- \li Locale.Tuesday = 2
- \li Locale.Wednesday = 3
- \li Locale.Thursday = 4
- \li Locale.Friday = 5
- \li Locale.Saturday = 6
- \endlist
+ \value Locale.Sunday 0
+ \value Locale.Monday 1
+ \value Locale.Tuesday 2
+ \value Locale.Wednesday 3
+ \value Locale.Thursday 4
+ \value Locale.Friday 5
+ \value Locale.Saturday 6
\note that these values match the JS Date API which is different
from the Qt C++ API where Qt::Sunday = 7.
@@ -1180,10 +954,9 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con
\qmlproperty enumeration QtQml::Locale::textDirection
Holds the text direction of the language:
- \list
- \li Qt.LeftToRight
- \li Qt.RightToLeft
- \endlist
+
+ \value Qt.LeftToRight Text normally begins at the left side.
+ \value Qt.RightToLeft Text normally begins at the right side.
*/
/*!
@@ -1202,11 +975,11 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con
\qmlmethod string QtQml::Locale::currencySymbol(format)
Returns the currency symbol for the specified \a format:
- \list
- \li Locale.CurrencyIsoCode a ISO-4217 code of the currency.
- \li Locale.CurrencySymbol a currency symbol.
- \li Locale.CurrencyDisplayName a user readable name of the currency.
- \endlist
+
+ \value Locale.CurrencyIsoCode a ISO-4217 code of the currency.
+ \value Locale.CurrencySymbol a currency symbol.
+ \value Locale.CurrencyDisplayName a user readable name of the currency.
+
\sa Number::toLocaleCurrencyString()
*/
@@ -1216,7 +989,7 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con
Holds a native name of the language for the locale. For example
"Schwiizertüütsch" for Swiss-German locale.
- \sa nativeCountryName
+ \sa nativeTerritoryName
*/
/*!
@@ -1243,16 +1016,13 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con
This property defines which units are used for measurement.
- \list
- \li Locale.MetricSystem This value indicates metric units, such as meters,
- centimeters and millimeters.
- \li Locale.ImperialUSSystem This value indicates imperial units, such as
- inches and miles as they are used in the United States.
- \li Locale.ImperialUKSystem This value indicates imperial units, such as
- inches and miles as they are used in the United Kingdom.
- \li Locale.ImperialSystem Provided for compatibility. The same as
- Locale.ImperialUSSystem.
- \endlist
+ \value Locale.MetricSystem This value indicates metric units, such as meters,
+ centimeters and millimeters.
+ \value Locale.ImperialUSSystem This value indicates imperial units, such as
+ inches and miles as they are used in the United States.
+ \value Locale.ImperialUKSystem This value indicates imperial units, such as
+ inches and miles as they are used in the United Kingdom.
+ \value Locale.ImperialSystem Provided for compatibility. The same as Locale.ImperialUSSystem.
*/
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h
index 7a497db523..82c65ccf82 100644
--- a/src/qml/qml/qqmllocale_p.h
+++ b/src/qml/qml/qqmllocale_p.h
@@ -54,13 +54,14 @@ private:
static QV4::ReturnedValue method_toLocaleCurrencyString(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
};
-
-namespace QQmlLocale
+// This needs to be a struct so that we can derive from QLocale and inherit its enums. Then we can
+// use it as extension in QQmlLocaleEnums and expose all the enums in one go, without duplicating
+// any in different qmltypes files.
+struct Q_QML_EXPORT QQmlLocale : public QLocale
{
- Q_NAMESPACE_EXPORT(Q_QML_PRIVATE_EXPORT)
- QML_NAMED_ELEMENT(Locale)
- QML_ADDED_IN_VERSION(2, 2)
- QML_NAMESPACE_EXTENDED(QLocale)
+ Q_GADGET
+ QML_ANONYMOUS
+public:
// Qt defines Sunday as 7, but JS Date assigns Sunday 0
enum DayOfWeek {
@@ -72,81 +73,176 @@ namespace QQmlLocale
Friday = Qt::Friday,
Saturday = Qt::Saturday
};
- Q_ENUM_NS(DayOfWeek)
+ Q_ENUM(DayOfWeek)
- Q_QML_PRIVATE_EXPORT QV4::ReturnedValue locale(QV4::ExecutionEngine *engine, const QString &localeName);
- Q_QML_PRIVATE_EXPORT QV4::ReturnedValue wrap(QV4::ExecutionEngine *engine, const QLocale &locale);
- Q_QML_PRIVATE_EXPORT void registerStringLocaleCompare(QV4::ExecutionEngine *engine);
- Q_QML_PRIVATE_EXPORT QV4::ReturnedValue method_localeCompare(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue locale(QV4::ExecutionEngine *engine, const QString &localeName);
+ static void registerStringLocaleCompare(QV4::ExecutionEngine *engine);
+ static QV4::ReturnedValue method_localeCompare(
+ const QV4::FunctionObject *, const QV4::Value *thisObject,
+ const QV4::Value *argv, int argc);
};
-namespace QV4 {
+struct DayOfWeekList
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QList<QQmlLocale::DayOfWeek>)
+ QML_SEQUENTIAL_CONTAINER(QQmlLocale::DayOfWeek)
+};
-namespace Heap {
+class QQmlLocaleValueType
+{
+ QLocale locale;
+
+ Q_PROPERTY(QQmlLocale::DayOfWeek firstDayOfWeek READ firstDayOfWeek CONSTANT)
+ Q_PROPERTY(QLocale::MeasurementSystem measurementSystem READ measurementSystem CONSTANT)
+ Q_PROPERTY(Qt::LayoutDirection textDirection READ textDirection CONSTANT)
+ Q_PROPERTY(QList<QQmlLocale::DayOfWeek> weekDays READ weekDays CONSTANT)
+ Q_PROPERTY(QStringList uiLanguages READ uiLanguages CONSTANT)
+
+ Q_PROPERTY(QString name READ name CONSTANT)
+ Q_PROPERTY(QString nativeLanguageName READ nativeLanguageName CONSTANT)
+#if QT_DEPRECATED_SINCE(6, 6)
+ Q_PROPERTY(QString nativeCountryName READ nativeCountryName CONSTANT)
+#endif
+ Q_PROPERTY(QString nativeTerritoryName READ nativeTerritoryName CONSTANT)
+ Q_PROPERTY(QString decimalPoint READ decimalPoint CONSTANT)
+ Q_PROPERTY(QString groupSeparator READ groupSeparator CONSTANT)
+ Q_PROPERTY(QString percent READ percent CONSTANT)
+ Q_PROPERTY(QString zeroDigit READ zeroDigit CONSTANT)
+ Q_PROPERTY(QString negativeSign READ negativeSign CONSTANT)
+ Q_PROPERTY(QString positiveSign READ positiveSign CONSTANT)
+ Q_PROPERTY(QString exponential READ exponential CONSTANT)
+ Q_PROPERTY(QString amText READ amText CONSTANT)
+ Q_PROPERTY(QString pmText READ pmText CONSTANT)
+
+ Q_PROPERTY(QLocale::NumberOptions numberOptions READ numberOptions WRITE setNumberOptions)
+
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QLocale)
+ QML_EXTENDED(QQmlLocaleValueType)
+ QML_CONSTRUCTIBLE_VALUE
+
+public:
+ Q_INVOKABLE QQmlLocaleValueType(const QString &name) : locale(name) {}
-struct QQmlLocaleData : Object {
- inline void init() { locale = new QLocale; }
- void destroy() {
- delete locale;
- Object::destroy();
+ Q_INVOKABLE QString currencySymbol(
+ QLocale::CurrencySymbolFormat format = QLocale::CurrencySymbol) const
+ {
+ return locale.currencySymbol(format);
}
- QLocale *locale;
-};
-}
+ Q_INVOKABLE QString dateTimeFormat(QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ return locale.dateTimeFormat(format);
+ }
-struct QQmlLocaleData : public QV4::Object
-{
- V4_OBJECT2(QQmlLocaleData, Object)
- V4_NEEDS_DESTROY
-
- static QLocale *getThisLocale(QV4::Scope &scope, const QV4::Value *thisObject) {
- const QV4::Object *o = thisObject->as<Object>();
- const QQmlLocaleData *data = o ? o->as<QQmlLocaleData>() : nullptr;
- if (!data) {
- scope.engine->throwTypeError();
- return nullptr;
- }
- return data->d()->locale;
+ Q_INVOKABLE QString timeFormat(QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ return locale.timeFormat(format);
}
- static QV4::ReturnedValue method_currencySymbol(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_dateTimeFormat(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_timeFormat(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_dateFormat(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_monthName(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_standaloneMonthName(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_dayName(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_standaloneDayName(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_toString(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
-
- static QV4::ReturnedValue method_get_firstDayOfWeek(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_measurementSystem(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_textDirection(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_weekDays(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_uiLanguages(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
-
- static QV4::ReturnedValue method_get_name(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_nativeLanguageName(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_nativeCountryName(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_nativeTerritoryName(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_decimalPoint(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_groupSeparator(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_percent(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_zeroDigit(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_negativeSign(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_positiveSign(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_exponential(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_amText(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_pmText(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
-
- static QV4::ReturnedValue method_get_numberOptions(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_set_numberOptions(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
-
- static QV4::ReturnedValue method_get_formattedDataSize(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
-};
+ Q_INVOKABLE QString dateFormat(QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ return locale.dateFormat(format);
+ }
+
+ Q_INVOKABLE QString monthName(int index, QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ // +1 added to idx because JS is 0-based, whereas QLocale months begin at 1.
+ return locale.monthName(index + 1, format);
+ }
+
+ Q_INVOKABLE QString standaloneMonthName(
+ int index, QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ // +1 added to idx because JS is 0-based, whereas QLocale months begin at 1.
+ return locale.standaloneMonthName(index + 1, format);
+ }
-}
+ Q_INVOKABLE QString dayName(int index, QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ // 0 -> 7 as Qt::Sunday is 7, but Sunday is 0 in JS Date
+ return locale.dayName(index == 0 ? 7 : index, format);
+ }
+
+ Q_INVOKABLE QString standaloneDayName(
+ int index, QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ // 0 -> 7 as Qt::Sunday is 7, but Sunday is 0 in JS Date
+ return locale.standaloneDayName(index == 0 ? 7 : index, format);
+ }
+
+ Q_INVOKABLE void formattedDataSize(QQmlV4FunctionPtr args) const;
+ Q_INVOKABLE QString formattedDataSize(
+ double bytes, int precision = 2,
+ QLocale::DataSizeFormats format = QLocale::DataSizeIecFormat) const
+ {
+ return locale.formattedDataSize(
+ qint64(QV4::Value::toInteger(bytes)), precision, format);
+ }
+
+ Q_INVOKABLE void toString(QQmlV4FunctionPtr args) const;
+
+ // As a special (undocumented) case, when called with no arguments,
+ // just forward to QDebug. This makes it consistent with other types
+ // in JS that can be converted to a string via toString().
+ Q_INVOKABLE QString toString() const { return QDebug::toString(locale); }
+
+ Q_INVOKABLE QString toString(int i) const { return locale.toString(i); }
+ Q_INVOKABLE QString toString(double f) const
+ {
+ return QJSNumberCoercion::isInteger(f) ? toString(int(f)) : locale.toString(f);
+ }
+ Q_INVOKABLE QString toString(double f, const QString &format, int precision = 6) const
+ {
+ // Lacking a char type, we have to use QString here
+ return format.length() < 1
+ ? QString()
+ : locale.toString(f, format.at(0).toLatin1(), precision);
+ }
+ Q_INVOKABLE QString toString(const QDateTime &dateTime, const QString &format) const
+ {
+ return locale.toString(dateTime, format);
+ }
+ Q_INVOKABLE QString toString(
+ const QDateTime &dateTime, QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ return locale.toString(dateTime, format);
+ }
+
+ QQmlLocale::DayOfWeek firstDayOfWeek() const;
+ QLocale::MeasurementSystem measurementSystem() const { return locale.measurementSystem(); }
+ Qt::LayoutDirection textDirection() const { return locale.textDirection(); }
+ QList<QQmlLocale::DayOfWeek> weekDays() const;
+ QStringList uiLanguages() const { return locale.uiLanguages(); }
+
+ QString name() const { return locale.name(); }
+ QString nativeLanguageName() const { return locale.nativeLanguageName(); }
+#if QT_DEPRECATED_SINCE(6, 6)
+ QString nativeCountryName() const
+ {
+ QT_IGNORE_DEPRECATIONS(return locale.nativeCountryName();)
+ }
+#endif
+ QString nativeTerritoryName() const { return locale.nativeTerritoryName(); }
+ QString decimalPoint() const { return locale.decimalPoint(); }
+ QString groupSeparator() const { return locale.groupSeparator(); }
+ QString percent() const { return locale.percent(); }
+ QString zeroDigit() const { return locale.zeroDigit(); }
+ QString negativeSign() const { return locale.negativeSign(); }
+ QString positiveSign() const { return locale.positiveSign(); }
+ QString exponential() const { return locale.exponential(); }
+ QString amText() const { return locale.amText(); }
+ QString pmText() const { return locale.pmText(); }
+
+ QLocale::NumberOptions numberOptions() const { return locale.numberOptions(); }
+ void setNumberOptions(const QLocale::NumberOptions &numberOptions)
+ {
+ locale.setNumberOptions(numberOptions);
+ }
+};
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlloggingcategory.cpp b/src/qml/qml/qqmlloggingcategory.cpp
deleted file mode 100644
index d735ecd716..0000000000
--- a/src/qml/qml/qqmlloggingcategory.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (C) 2016 Pelagicore AG
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qqmlloggingcategory_p.h"
-
-#include <QtQml/qqmlinfo.h>
-
-#include <memory>
-
-/*!
- \qmltype LoggingCategory
- \ingroup qml-utility-elements
- \inqmlmodule QtQml
- \brief Defines a logging category in QML.
- \since 5.8
-
- A logging category can be passed to console.log() and friends as the first argument.
- If supplied to to the logger the LoggingCategory's name will be used as Logging Category
- otherwise the default logging category will be used.
-
- \qml
- import QtQuick 2.8
-
- Item {
- LoggingCategory {
- id: category
- name: "com.qt.category"
- defaultLogLevel: LoggingCategory.Warning
- }
-
- Component.onCompleted: {
- console.log(category, "message");
- }
- }
- \endqml
-
- \note As the creation of objects is expensive, it is encouraged to put the needed
- LoggingCategory definitions into a singleton and import this where needed.
-
- \sa QLoggingCategory
-*/
-
-/*!
- \qmlproperty string QtQml::LoggingCategory::name
-
- Holds the name of the logging category.
-
- \note This property needs to be set when declaring the LoggingCategory
- and cannot be changed later.
-
- \sa QLoggingCategory::categoryName()
-*/
-
-/*!
- \qmlproperty enumeration QtQml::LoggingCategory::defaultLogLevel
- \since 5.12
-
- Holds the default log level of the logging category. By default it is
- created with the LoggingCategory.Debug log level.
-
- \note This property needs to be set when declaring the LoggingCategory
- and cannot be changed later.
-*/
-
-QQmlLoggingCategory::QQmlLoggingCategory(QObject *parent)
- : QObject(parent)
- , m_initialized(false)
-{
-}
-
-QQmlLoggingCategory::~QQmlLoggingCategory()
-{
-}
-
-QString QQmlLoggingCategory::name() const
-{
- return QString::fromUtf8(m_name);
-}
-
-QQmlLoggingCategory::DefaultLogLevel QQmlLoggingCategory::defaultLogLevel() const
-{
- return m_defaultLogLevel;
-}
-
-QLoggingCategory *QQmlLoggingCategory::category() const
-{
- return m_category.get();
-}
-
-void QQmlLoggingCategory::classBegin()
-{
-}
-
-void QQmlLoggingCategory::componentComplete()
-{
- m_initialized = true;
- if (m_name.isNull()) {
- qmlWarning(this) << QLatin1String("Declaring the name of a LoggingCategory is mandatory and cannot be changed later");
- } else {
- auto category = std::make_unique<QLoggingCategory>(m_name.constData(), QtMsgType(m_defaultLogLevel));
- m_category.swap(category);
- }
-}
-
-void QQmlLoggingCategory::setDefaultLogLevel(DefaultLogLevel defaultLogLevel)
-{
- if (m_defaultLogLevel == defaultLogLevel)
- return;
-
- if (m_initialized) {
- qmlWarning(this) << QLatin1String("The defaultLogLevel of a LoggingCategory cannot be changed after the component is completed");
- return;
- }
-
- m_defaultLogLevel = defaultLogLevel;
-}
-
-void QQmlLoggingCategory::setName(const QString &name)
-{
- const QByteArray newName = name.toUtf8();
-
- if (m_name == newName)
- return;
-
- if (m_initialized) {
- qmlWarning(this) << QLatin1String("The name of a LoggingCategory cannot be changed after the component is completed");
- return;
- }
-
- m_name = newName;
-}
-
-#include "moc_qqmlloggingcategory_p.cpp"
diff --git a/src/qml/qml/qqmlloggingcategory_p.h b/src/qml/qml/qqmlloggingcategory_p.h
deleted file mode 100644
index 4a27e7e1d7..0000000000
--- a/src/qml/qml/qqmlloggingcategory_p.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (C) 2016 Pelagicore AG
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef QQMLLOGGINGCATEGORY_P_H
-#define QQMLLOGGINGCATEGORY_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/qobject.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qloggingcategory.h>
-
-#include <QtQml/qqmlparserstatus.h>
-#include <QtQml/qqml.h>
-#include <QtCore/private/qglobal_p.h>
-
-#include <memory>
-
-QT_BEGIN_NAMESPACE
-
-class QQmlLoggingCategory : public QObject, public QQmlParserStatus
-{
- Q_OBJECT
- Q_INTERFACES(QQmlParserStatus)
-
- Q_PROPERTY(QString name READ name WRITE setName)
- Q_PROPERTY(DefaultLogLevel defaultLogLevel READ defaultLogLevel WRITE setDefaultLogLevel REVISION(2, 12))
- QML_NAMED_ELEMENT(LoggingCategory)
- QML_ADDED_IN_VERSION(2, 8)
-
-public:
- enum DefaultLogLevel {
- Debug = QtDebugMsg,
- Info = QtInfoMsg,
- Warning = QtWarningMsg,
- Critical = QtCriticalMsg,
- Fatal = QtFatalMsg
- };
- Q_ENUM(DefaultLogLevel);
-
- QQmlLoggingCategory(QObject *parent = nullptr);
- virtual ~QQmlLoggingCategory();
-
- DefaultLogLevel defaultLogLevel() const;
- void setDefaultLogLevel(DefaultLogLevel defaultLogLevel);
- QString name() const;
- void setName(const QString &name);
-
- QLoggingCategory *category() const;
-
- void classBegin() override;
- void componentComplete() override;
-
-private:
- QByteArray m_name;
- std::unique_ptr<QLoggingCategory> m_category;
- DefaultLogLevel m_defaultLogLevel = Debug;
- bool m_initialized;
-};
-
-QT_END_NAMESPACE
-
-#endif // QQMLLOGGINGCATEGORY_H
diff --git a/src/qml/qml/qqmlloggingcategorybase_p.h b/src/qml/qml/qqmlloggingcategorybase_p.h
new file mode 100644
index 0000000000..4a3c4cf6aa
--- /dev/null
+++ b/src/qml/qml/qqmlloggingcategorybase_p.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLLOGGINGCATEGORYBASE_P_H
+#define QQMLLOGGINGCATEGORYBASE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQml/qqml.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qloggingcategory.h>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QML_EXPORT QQmlLoggingCategoryBase : public QObject
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+
+public:
+ QQmlLoggingCategoryBase(QObject *parent = nullptr) : QObject(parent) {}
+
+ const QLoggingCategory *category() const { return m_category.get(); }
+ void setCategory(const char *name, QtMsgType type)
+ {
+ m_category = std::make_unique<QLoggingCategory>(name, type);
+ }
+
+private:
+ std::unique_ptr<QLoggingCategory> m_category;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLLOGGINGCATEGORYBASE_P_H
diff --git a/src/qml/qml/qqmlmetamoduleregistration.cpp b/src/qml/qml/qqmlmetamoduleregistration.cpp
deleted file mode 100644
index 280c7a7c8d..0000000000
--- a/src/qml/qml/qqmlmetamoduleregistration.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include <private/qtqmlglobal_p.h>
-#include <qqmlmoduleregistration.h>
-#include <qqml.h>
-
-QT_BEGIN_NAMESPACE
-
-// Provide the type registration for QtQml here, in libQtQml.so.
-// This way we get a completely functional QtQml module and don't have to
-// rely on the plugin to be loaded.
-// In CMakeLists.txt we've specified NO_GENERATE_QMLTYPES to prevent
-// the generation of an extra type registration file.
-Q_QML_PRIVATE_EXPORT void qml_register_types_QtQml()
-{
- // ### Qt7: Handle version 6 like version 2.
- qmlRegisterModule("QtQml", 2, 0);
- qmlRegisterModule("QtQml", 2, 254);
- qmlRegisterModule("QtQml", QT_VERSION_MAJOR, 0);
- qmlRegisterModule("QtQml", QT_VERSION_MAJOR, QT_VERSION_MINOR);
-}
-
-static const QQmlModuleRegistration registration("QtQml", qml_register_types_QtQml);
-
-QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlmetaobject.cpp b/src/qml/qml/qqmlmetaobject.cpp
index e84c5a366e..352db7bd69 100644
--- a/src/qml/qml/qqmlmetaobject.cpp
+++ b/src/qml/qml/qqmlmetaobject.cpp
@@ -8,28 +8,6 @@
QT_BEGIN_NAMESPACE
-// Returns true if \a from is assignable to a property of type \a to
-bool QQmlMetaObject::canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to)
-{
- Q_ASSERT(!from.isNull() && !to.isNull());
-
- auto equal = [] (const QMetaObject *lhs, const QMetaObject *rhs) -> bool {
- return lhs == rhs || (lhs && rhs && lhs->d.stringdata == rhs->d.stringdata);
- };
-
- const QMetaObject *tom = to.metaObject();
- if (tom == &QObject::staticMetaObject) return true;
-
- const QMetaObject *fromm = from.metaObject();
- while (fromm) {
- if (equal(fromm, tom))
- return true;
- fromm = fromm->superClass();
- }
-
- return false;
-}
-
void QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index)
{
int offset;
@@ -70,7 +48,7 @@ QMetaType QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteAr
type = _m->method(data.coreIndex()).returnMetaType();
}
if (type.flags().testFlag(QMetaType::IsEnumeration))
- type = QMetaType::fromType<int>();
+ type = type.underlyingType();
if (type.isValid())
return type;
else if (unknownTypeError)
@@ -78,42 +56,4 @@ QMetaType QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteAr
return QMetaType();
}
-bool QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage,
- QByteArray *unknownTypeError) const
-{
- Q_ASSERT(_m && index >= 0);
-
- QMetaMethod m = _m->method(index);
- return methodParameterTypes(m, argStorage, unknownTypeError);
-}
-
-bool QQmlMetaObject::constructorParameterTypes(int index, ArgTypeStorage *dummy,
- QByteArray *unknownTypeError) const
-{
- QMetaMethod m = _m->constructor(index);
- return methodParameterTypes(m, dummy, unknownTypeError);
-}
-
-bool QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage *argStorage,
- QByteArray *unknownTypeError)
-{
- Q_ASSERT(argStorage);
-
- int argc = m.parameterCount();
- argStorage->resize(argc);
- for (int ii = 0; ii < argc; ++ii) {
- QMetaType type = m.parameterMetaType(ii);
- // we treat enumerations as int
- if (type.flags().testFlag(QMetaType::IsEnumeration))
- type = QMetaType::fromType<int>();
- if (!type.isValid()) {
- if (unknownTypeError)
- *unknownTypeError = m.parameterTypeName(ii);
- return false;
- }
- argStorage->operator[](ii) = type;
- }
- return true;
-}
-
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlmetaobject_p.h b/src/qml/qml/qqmlmetaobject_p.h
index cc3cc6af79..498c27884e 100644
--- a/src/qml/qml/qqmlmetaobject_p.h
+++ b/src/qml/qml/qqmlmetaobject_p.h
@@ -34,7 +34,8 @@ class QQmlPropertyData;
class Q_QML_EXPORT QQmlMetaObject
{
public:
- typedef QVarLengthArray<QMetaType, 9> ArgTypeStorage;
+ template<qsizetype Prealloc>
+ using ArgTypeStorage = QVarLengthArray<QMetaType, Prealloc>;
inline QQmlMetaObject() = default;
inline QQmlMetaObject(const QObject *);
@@ -52,30 +53,120 @@ public:
inline const QMetaObject *metaObject() const;
QMetaType methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const;
+
/*!
\internal
Returns false if one of the types is unknown. Otherwise, fills \a argstorage with the
metatypes of the function.
*/
- bool methodParameterTypes(int index, ArgTypeStorage *argStorage,
- QByteArray *unknownTypeError) const;
+ template<typename ArgTypeStorage>
+ bool methodParameterTypes(
+ int index, ArgTypeStorage *argStorage, QByteArray *unknownTypeError) const
+ {
+ Q_ASSERT(_m && index >= 0);
+
+ QMetaMethod m = _m->method(index);
+ return methodParameterTypes(m, argStorage, unknownTypeError);
+ }
+
/*!
\internal
Returns false if one of the types is unknown. Otherwise, fills \a argstorage with the
metatypes of the function.
*/
- bool constructorParameterTypes(int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const;
+ template<typename ArgTypeStorage>
+ bool constructorParameterTypes(
+ int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const
+ {
+ QMetaMethod m = _m->constructor(index);
+ return methodParameterTypes(m, dummy, unknownTypeError);
+ }
- static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to);
+ static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to)
+ {
+ Q_ASSERT(!from.isNull() && !to.isNull());
+ return from.metaObject()->inherits(to.metaObject());
+ }
// static_metacall (on Gadgets) doesn't call the base implementation and therefore
// we need a helper to find the correct meta object and property/method index.
- static void resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index);
+ static void resolveGadgetMethodOrPropertyIndex(
+ QMetaObject::Call type, const QMetaObject **metaObject, int *index);
+
+ template<typename ArgTypeStorage>
+ static bool methodParameterTypes(
+ const QMetaMethod &method, ArgTypeStorage *argStorage, QByteArray *unknownTypeError)
+ {
+ Q_ASSERT(argStorage);
+
+ const int argc = method.parameterCount();
+ argStorage->resize(argc);
+ for (int ii = 0; ii < argc; ++ii) {
+ if (!parameterType(method, ii, unknownTypeError, [argStorage](int ii, QMetaType &&type) {
+ argStorage->operator[](ii) = std::forward<QMetaType>(type);
+ })) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ template<typename ArgTypeStorage>
+ static bool methodReturnAndParameterTypes(
+ const QMetaMethod &method, ArgTypeStorage *argStorage, QByteArray *unknownTypeError)
+ {
+ Q_ASSERT(argStorage);
+
+ const int argc = method.parameterCount();
+ argStorage->resize(argc + 1);
+
+ QMetaType type = method.returnMetaType();
+ if (type.flags().testFlag(QMetaType::IsEnumeration))
+ type = type.underlyingType();
+
+ if (!type.isValid()) {
+ if (unknownTypeError)
+ *unknownTypeError = "return type";
+ return false;
+ }
+
+ argStorage->operator[](0) = type;
+
+ for (int ii = 0; ii < argc; ++ii) {
+ if (!parameterType(
+ method, ii, unknownTypeError, [argStorage](int ii, QMetaType &&type) {
+ argStorage->operator[](ii + 1) = std::forward<QMetaType>(type);
+ })) {
+ return false;
+ }
+ }
+
+ return true;
+ }
- static bool methodParameterTypes(const QMetaMethod &method, ArgTypeStorage *argStorage,
- QByteArray *unknownTypeError);
protected:
+ template<typename Store>
+ static bool parameterType(
+ const QMetaMethod &method, int ii, QByteArray *unknownTypeError, const Store &store)
+ {
+ QMetaType type = method.parameterMetaType(ii);
+
+ // we treat enumerations as their underlying type
+ if (type.flags().testFlag(QMetaType::IsEnumeration))
+ type = type.underlyingType();
+
+ if (!type.isValid()) {
+ if (unknownTypeError)
+ *unknownTypeError = method.parameterTypeName(ii);
+ return false;
+ }
+
+ store(ii, std::move(type));
+ return true;
+ }
+
+
const QMetaObject *_m = nullptr;
};
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 054ad48617..1175bde3db 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -3,11 +3,12 @@
#include "qqmlmetatype_p.h"
+#include <private/qqmlextensionplugin_p.h>
#include <private/qqmlmetatypedata_p.h>
-#include <private/qqmltypemodule_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
#include <private/qqmltype_p_p.h>
#include <private/qqmltypeloader_p.h>
-#include <private/qqmlextensionplugin_p.h>
+#include <private/qqmltypemodule_p.h>
#include <private/qqmlvaluetype_p.h>
#include <private/qv4executablecompilationunit_p.h>
@@ -20,32 +21,6 @@ Q_LOGGING_CATEGORY(lcTypeRegistration, "qt.qml.typeregistration")
QT_BEGIN_NAMESPACE
-CompositeMetaTypeIds CompositeMetaTypeIds::fromCompositeName(const QByteArray &name)
-{
- auto ids = QQmlMetaType::registerInternalCompositeType(name);
- ids.refCount = new int;
- *ids.refCount = 1;
- return ids;
-}
-
-void CompositeMetaTypeIds::deref()
-{
- Q_ASSERT(refCount);
- --*refCount;
- if (!*refCount) {
- delete refCount;
- QQmlMetaType::unregisterInternalCompositeType(*this);
- refCount = nullptr;
- }
-}
-
-CompositeMetaTypeIds::~CompositeMetaTypeIds()
-{
- if (refCount)
- deref();
-}
-
-
struct LockedData : private QQmlMetaTypeData
{
friend class QQmlMetaTypeDataPtr;
@@ -86,18 +61,19 @@ static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data,
const QQmlPrivate::RegisterInterface &type)
{
auto *d = new QQmlTypePrivate(QQmlType::InterfaceType);
- d->iid = type.iid;
+ d->extraData.interfaceTypeData = type.iid;
d->typeId = type.typeId;
d->listId = type.listId;
- d->isSetup.storeRelease(true);
d->module = QString::fromUtf8(type.uri);
d->version = type.version;
data->registerType(d);
return d;
}
-static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName,
- const QQmlPrivate::RegisterSingletonType &type)
+static QQmlTypePrivate *createQQmlType(
+ QQmlMetaTypeData *data, const QString &elementName,
+ const QQmlPrivate::RegisterSingletonType &type,
+ const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo)
{
auto *d = new QQmlTypePrivate(QQmlType::SingletonType);
data->registerType(d);
@@ -111,14 +87,9 @@ static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &el
d->revision = type.revision;
}
- d->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo;
- d->extraData.sd->singletonInstanceInfo->scriptCallback = type.scriptApi;
- d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.qObjectApi;
- d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName);
- d->extraData.sd->singletonInstanceInfo->instanceMetaObject
- = type.qObjectApi ? type.instanceMetaObject : nullptr;
- d->extraData.sd->extFunc = type.extensionObjectCreate;
- d->extraData.sd->extMetaObject = type.extensionMetaObject;
+ d->extraData.singletonTypeData->singletonInstanceInfo = siinfo;
+ d->extraData.singletonTypeData->extFunc = type.extensionObjectCreate;
+ d->extraData.singletonTypeData->extMetaObject = type.extensionMetaObject;
return d;
}
@@ -134,74 +105,98 @@ static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &el
d->revision = type.revision;
d->typeId = type.typeId;
d->listId = type.listId;
- d->extraData.cd->allocationSize = type.objectSize;
- d->extraData.cd->userdata = type.userdata;
- d->extraData.cd->newFunc = type.create;
- d->extraData.cd->noCreationReason = type.noCreationReason;
- d->extraData.cd->createValueTypeFunc = type.createValueType;
+ d->extraData.cppTypeData->allocationSize = type.objectSize;
+ d->extraData.cppTypeData->userdata = type.userdata;
+ d->extraData.cppTypeData->newFunc = type.create;
+ d->extraData.cppTypeData->noCreationReason = type.noCreationReason;
+ d->extraData.cppTypeData->createValueTypeFunc = type.createValueType;
d->baseMetaObject = type.metaObject;
- d->extraData.cd->attachedPropertiesFunc = type.attachedPropertiesFunction;
- d->extraData.cd->attachedPropertiesType = type.attachedPropertiesMetaObject;
- d->extraData.cd->parserStatusCast = type.parserStatusCast;
- d->extraData.cd->propertyValueSourceCast = type.valueSourceCast;
- d->extraData.cd->propertyValueInterceptorCast = type.valueInterceptorCast;
- d->extraData.cd->finalizerCast = type.has(QQmlPrivate::RegisterType::FinalizerCast)
+ d->extraData.cppTypeData->attachedPropertiesFunc = type.attachedPropertiesFunction;
+ d->extraData.cppTypeData->attachedPropertiesType = type.attachedPropertiesMetaObject;
+ d->extraData.cppTypeData->parserStatusCast = type.parserStatusCast;
+ d->extraData.cppTypeData->propertyValueSourceCast = type.valueSourceCast;
+ d->extraData.cppTypeData->propertyValueInterceptorCast = type.valueInterceptorCast;
+ d->extraData.cppTypeData->finalizerCast = type.has(QQmlPrivate::RegisterType::FinalizerCast)
? type.finalizerCast
: -1;
- d->extraData.cd->extFunc = type.extensionObjectCreate;
- d->extraData.cd->customParser = reinterpret_cast<QQmlCustomParser *>(type.customParser);
- d->extraData.cd->registerEnumClassesUnscoped = true;
- d->extraData.cd->registerEnumsFromRelatedTypes = true;
- d->extraData.cd->constructValueType = type.has(QQmlPrivate::RegisterType::CreationMethod)
+ d->extraData.cppTypeData->extFunc = type.extensionObjectCreate;
+ d->extraData.cppTypeData->customParser = reinterpret_cast<QQmlCustomParser *>(type.customParser);
+ d->extraData.cppTypeData->registerEnumClassesUnscoped = true;
+ d->extraData.cppTypeData->registerEnumsFromRelatedTypes = true;
+ d->extraData.cppTypeData->constructValueType = type.has(QQmlPrivate::RegisterType::CreationMethod)
&& type.creationMethod != QQmlPrivate::ValueTypeCreationMethod::None;
- d->extraData.cd->populateValueType = type.has(QQmlPrivate::RegisterType::CreationMethod)
+ d->extraData.cppTypeData->populateValueType = type.has(QQmlPrivate::RegisterType::CreationMethod)
&& type.creationMethod == QQmlPrivate::ValueTypeCreationMethod::Structured;
if (type.extensionMetaObject)
- d->extraData.cd->extMetaObject = type.extensionMetaObject;
+ d->extraData.cppTypeData->extMetaObject = type.extensionMetaObject;
// Check if the user wants only scoped enum classes
if (d->baseMetaObject) {
auto indexOfUnscoped = d->baseMetaObject->indexOfClassInfo("RegisterEnumClassesUnscoped");
if (indexOfUnscoped != -1
&& qstrcmp(d->baseMetaObject->classInfo(indexOfUnscoped).value(), "false") == 0) {
- d->extraData.cd->registerEnumClassesUnscoped = false;
+ d->extraData.cppTypeData->registerEnumClassesUnscoped = false;
}
auto indexOfRelated = d->baseMetaObject->indexOfClassInfo("RegisterEnumsFromRelatedTypes");
if (indexOfRelated != -1
&& qstrcmp(d->baseMetaObject->classInfo(indexOfRelated).value(), "false") == 0) {
- d->extraData.cd->registerEnumsFromRelatedTypes = false;
+ d->extraData.cppTypeData->registerEnumsFromRelatedTypes = false;
}
}
return d;
}
+static void addQQmlMetaTypeInterfaces(QQmlTypePrivate *priv, const QByteArray &className)
+{
+ Q_ASSERT(!className.isEmpty());
+ QByteArray ptr = className + '*';
+ QByteArray lst = "QQmlListProperty<" + className + '>';
+
+ QMetaType ptr_type(new QQmlMetaTypeInterface(ptr));
+ QMetaType lst_type(new QQmlListMetaTypeInterface(lst, ptr_type.iface()));
+
+ // Retrieve the IDs once, so that the types are added to QMetaType's custom type registry.
+ ptr_type.id();
+ lst_type.id();
+
+ priv->typeId = ptr_type;
+ priv->listId = lst_type;
+}
+
static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName,
const QQmlPrivate::RegisterCompositeType &type)
{
+ // This is a procedurally registered composite type. It's evil. It doesn't get any metatypes
+ // because we never want to find it in the compositeTypes. Otherwise we might mix it up with an
+ // actually compiled version of the same type.
+
auto *d = new QQmlTypePrivate(QQmlType::CompositeType);
data->registerType(d);
d->setName(QString::fromUtf8(type.uri), elementName);
d->version = type.version;
-
- d->extraData.fd->url = QQmlTypeLoader::normalize(type.url);
+ d->extraData.compositeTypeData = QQmlTypeLoader::normalize(type.url);
return d;
}
-static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName,
- const QQmlPrivate::RegisterCompositeSingletonType &type)
+static QQmlTypePrivate *createQQmlType(
+ QQmlMetaTypeData *data, const QString &elementName,
+ const QQmlPrivate::RegisterCompositeSingletonType &type,
+ const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo)
{
+ // This is a procedurally registered composite singleton. It's evil. It doesn't get any
+ // metatypes because we never want to find it in the compositeTypes. Otherwise we might mix it
+ // up with an actually compiled version of the same type.
+
auto *d = new QQmlTypePrivate(QQmlType::CompositeSingletonType);
data->registerType(d);
d->setName(QString::fromUtf8(type.uri), elementName);
d->version = type.version;
- d->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo;
- d->extraData.sd->singletonInstanceInfo->url = QQmlTypeLoader::normalize(type.url);
- d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName);
+ d->extraData.singletonTypeData->singletonInstanceInfo = siinfo;
return d;
}
@@ -262,7 +257,7 @@ void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
}
}
- // Clone Q_ENUMS
+ // Clone enums registered with the metatype system
for (int ii = mo->enumeratorOffset(); ii < mo->enumeratorCount(); ++ii) {
QMetaEnum enumerator = mo->enumerator(ii);
@@ -317,6 +312,20 @@ void QQmlMetaType::clearTypeRegistrations()
data->urlToNonFileImportType.clear();
data->metaObjectToType.clear();
data->undeletableTypes.clear();
+ data->propertyCaches.clear();
+ data->inlineComponentTypes.clear();
+
+ // Avoid deletion recursion (via QQmlTypePrivate dtor) by moving them out of the way first.
+ QQmlMetaTypeData::CompositeTypes emptyComposites;
+ emptyComposites.swap(data->compositeTypes);
+}
+
+void QQmlMetaType::registerTypeAlias(int typeIndex, const QString &name)
+{
+ QQmlMetaTypeDataPtr data;
+ const QQmlType type = data->types.value(typeIndex);
+ const QQmlTypePrivate *priv = type.priv();
+ data->nameToType.insert(name, priv);
}
int QQmlMetaType::registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &function)
@@ -482,7 +491,9 @@ QQmlType QQmlMetaType::registerType(const QQmlPrivate::RegisterType &type)
return QQmlType(priv);
}
-QQmlType QQmlMetaType::registerSingletonType(const QQmlPrivate::RegisterSingletonType &type)
+QQmlType QQmlMetaType::registerSingletonType(
+ const QQmlPrivate::RegisterSingletonType &type,
+ const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo)
{
if (type.structVersion > 1)
qFatal("qmlRegisterType(): Cannot mix incompatible QML versions.");
@@ -495,14 +506,16 @@ QQmlType QQmlMetaType::registerSingletonType(const QQmlPrivate::RegisterSingleto
return QQmlType();
}
- QQmlTypePrivate *priv = createQQmlType(data, typeName, type);
+ QQmlTypePrivate *priv = createQQmlType(data, typeName, type, siinfo);
addTypeToData(priv, data);
return QQmlType(priv);
}
-QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type)
+QQmlType QQmlMetaType::registerCompositeSingletonType(
+ const QQmlPrivate::RegisterCompositeSingletonType &type,
+ const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo)
{
if (type.structVersion > 1)
qFatal("qmlRegisterType(): Cannot mix incompatible QML versions.");
@@ -519,11 +532,11 @@ QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::Registe
return QQmlType();
}
- QQmlTypePrivate *priv = createQQmlType(data, typeName, type);
+ QQmlTypePrivate *priv = createQQmlType(data, typeName, type, siinfo);
addTypeToData(priv, data);
QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType);
- files->insert(QQmlTypeLoader::normalize(type.url), priv);
+ files->insert(siinfo->url, priv);
return QQmlType(priv);
}
@@ -554,26 +567,172 @@ QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterComposit
return QQmlType(priv);
}
-CompositeMetaTypeIds QQmlMetaType::registerInternalCompositeType(const QByteArray &className)
+class QQmlMetaTypeRegistrationFailureRecorder
{
- QByteArray ptr = className + '*';
- QByteArray lst = "QQmlListProperty<" + className + '>';
+ Q_DISABLE_COPY_MOVE(QQmlMetaTypeRegistrationFailureRecorder)
+public:
+ QQmlMetaTypeRegistrationFailureRecorder(QQmlMetaTypeData *data, QStringList *failures)
+ : data(data)
+ {
+ data->setTypeRegistrationFailures(failures);
+ }
- QMetaType ptr_type(new QQmlMetaTypeInterface(ptr));
- QMetaType lst_type(new QQmlListMetaTypeInterface(lst, ptr_type.iface()));
+ ~QQmlMetaTypeRegistrationFailureRecorder()
+ {
+ data->setTypeRegistrationFailures(nullptr);
+ }
- // Retrieve the IDs once, so that the types are added to QMetaType's custom type registry.
- ptr_type.id();
- lst_type.id();
+ QQmlMetaTypeData *data = nullptr;
+};
+
+
+static QQmlType createTypeForUrl(
+ QQmlMetaTypeData *data, const QUrl &url, const QHashedStringRef &qualifiedType,
+ QQmlMetaType::CompositeTypeLookupMode mode, QList<QQmlError> *errors, QTypeRevision version)
+{
+ const int dot = qualifiedType.indexOf(QLatin1Char('.'));
+ const QString typeName = dot < 0
+ ? qualifiedType.toString()
+ : QString(qualifiedType.constData() + dot + 1, qualifiedType.length() - dot - 1);
+
+ QStringList failures;
+ QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures);
+
+ // Register the type. Note that the URI parameters here are empty; for
+ // file type imports, we do not place them in a URI as we don't
+ // necessarily have a good and unique one (picture a library import,
+ // which may be found in multiple plugin locations on disk), but there
+ // are other reasons for this too.
+ //
+ // By not putting them in a URI, we prevent the types from being
+ // registered on a QQmlTypeModule; this is important, as once types are
+ // placed on there, they cannot be easily removed, meaning if the
+ // developer subsequently loads a different import (meaning different
+ // types) with the same URI (using, say, a different plugin path), it is
+ // very undesirable that we continue to associate the types from the
+ // "old" URI with that new module.
+ //
+ // Not having URIs also means that the types cannot be found by name
+ // etc, the only way to look them up is through QQmlImports -- for
+ // better or worse.
+ const QQmlType::RegistrationType registrationType = mode == QQmlMetaType::Singleton
+ ? QQmlType::CompositeSingletonType
+ : QQmlType::CompositeType;
+ if (checkRegistration(registrationType, data, nullptr, typeName, version, {})) {
+
+ // TODO: Ideally we should defer most of this work using some lazy/atomic mechanism
+ // that creates the details on first use. We must not observably modify
+ // QQmlTypePrivate after it has been created since it is supposed to be immutable
+ // and shared across threads.
+
+ auto *priv = new QQmlTypePrivate(registrationType);
+ addQQmlMetaTypeInterfaces(priv, QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(url));
+
+ priv->setName(QString(), typeName);
+ priv->version = version;
+
+ if (mode == QQmlMetaType::Singleton) {
+ QQmlType::SingletonInstanceInfo::Ptr siinfo = QQmlType::SingletonInstanceInfo::create();
+ siinfo->url = url;
+ siinfo->typeName = typeName.toUtf8();
+ priv->extraData.singletonTypeData->singletonInstanceInfo =
+ QQmlType::SingletonInstanceInfo::ConstPtr(
+ siinfo.take(), QQmlType::SingletonInstanceInfo::ConstPtr::Adopt);
+ } else {
+ priv->extraData.compositeTypeData = url;
+ }
+
+ data->registerType(priv);
+ addTypeToData(priv, data);
+ return QQmlType(priv);
+ }
+
+ // This means that the type couldn't be found by URL, but could not be
+ // registered either, meaning we most likely were passed some kind of bad
+ // data.
+ if (errors) {
+ QQmlError error;
+ error.setDescription(failures.join(u'\n'));
+ errors->prepend(error);
+ } else {
+ qWarning("%s", failures.join(u'\n').toLatin1().constData());
+ }
+ return QQmlType();
+}
+
+QQmlType QQmlMetaType::findCompositeType(
+ const QUrl &url, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
+ CompositeTypeLookupMode mode)
+{
+ const QUrl normalized = QQmlTypeLoader::normalize(url);
+ QQmlMetaTypeDataPtr data;
+
+ bool urlExists = true;
+ auto found = data->urlToType.constFind(normalized);
+ if (found == data->urlToType.cend()) {
+ found = data->urlToNonFileImportType.constFind(normalized);
+ if (found == data->urlToNonFileImportType.cend())
+ urlExists = false;
+ }
+
+ if (const QtPrivate::QMetaTypeInterface *iface = urlExists
+ ? found.value()->typeId.iface()
+ : nullptr) {
+ if (compilationUnit.isNull())
+ return QQmlType(*found);
+
+ const auto composite = data->compositeTypes.constFind(iface);
+ if (composite == data->compositeTypes.constEnd() || composite.value() == compilationUnit)
+ return QQmlType(*found);
+ }
+
+ const QQmlType type = createTypeForUrl(
+ data, normalized, QHashedStringRef(), mode, nullptr, QTypeRevision());
+
+ if (!urlExists && type.isValid())
+ data->urlToType.insert(normalized, type.priv());
+
+ return type;
+}
+
+static QQmlType doRegisterInlineComponentType(QQmlMetaTypeData *data, const QUrl &url)
+{
+ QQmlTypePrivate *priv = new QQmlTypePrivate(QQmlType::InlineComponentType);
+ priv->setName(QString(), url.fragment());
+
+ priv->extraData.inlineComponentTypeData = url;
+
+ const QByteArray className
+ = QQmlPropertyCacheCreatorBase::createClassNameForInlineComponent(url, url.fragment());
+
+ addQQmlMetaTypeInterfaces(priv, className);
+ const QQmlType result(priv);
+ priv->release();
+
+ data->inlineComponentTypes.insert(url, result);
- return {ptr_type, lst_type};
+ return result;
}
-void QQmlMetaType::unregisterInternalCompositeType(const CompositeMetaTypeIds &typeIds)
+QQmlType QQmlMetaType::findInlineComponentType(
+ const QUrl &url, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
{
- QMetaType metaType(typeIds.id);
- QMetaType listMetaType(typeIds.listId);
+ QQmlMetaTypeDataPtr data;
+ // If there is an "unclaimed" inline component type, we can "claim" it now. Otherwise
+ // we have to create a new one.
+ const auto it = data->inlineComponentTypes.constFind(url);
+ if (it != data->inlineComponentTypes.constEnd()) {
+ const auto jt = data->compositeTypes.constFind(it->typeId().iface());
+ if (jt == data->compositeTypes.constEnd() || *jt == compilationUnit)
+ return *it;
+ }
+
+ return doRegisterInlineComponentType(data, url);
+}
+
+void QQmlMetaType::unregisterInternalCompositeType(QMetaType metaType, QMetaType listMetaType)
+{
// This may be called from delayed dtors on shutdown when the data is already gone.
QQmlMetaTypeDataPtr data;
if (data.isValid()) {
@@ -581,6 +740,10 @@ void QQmlMetaType::unregisterInternalCompositeType(const CompositeMetaTypeIds &t
delete vt;
if (QQmlValueType *vt = data->metaTypeToValueType.take(listMetaType.id()))
delete vt;
+
+ auto it = data->compositeTypes.constFind(metaType.iface());
+ if (it != data->compositeTypes.constEnd())
+ data->compositeTypes.erase(it);
}
QMetaType::unregisterMetaType(metaType);
@@ -621,7 +784,7 @@ QQmlType QQmlMetaType::registerSequentialContainer(
priv->revision = container.revision;
priv->typeId = container.metaSequence.valueMetaType();
priv->listId = container.typeId;
- *priv->extraData.ld = container.metaSequence;
+ priv->extraData.sequentialContainerTypeData = container.metaSequence;
addTypeToData(priv, data);
@@ -754,25 +917,6 @@ static bool namespaceContainsRegistrations(const QQmlMetaTypeData *data, const Q
return false;
}
-class QQmlMetaTypeRegistrationFailureRecorder
-{
- Q_DISABLE_COPY_MOVE(QQmlMetaTypeRegistrationFailureRecorder)
-public:
- QQmlMetaTypeRegistrationFailureRecorder(QQmlMetaTypeData *data, QStringList *failures)
- : data(data)
- {
- data->setTypeRegistrationFailures(failures);
- }
-
- ~QQmlMetaTypeRegistrationFailureRecorder()
- {
- data->setTypeRegistrationFailures(nullptr);
- }
-
- QQmlMetaTypeData *data = nullptr;
-};
-
-
QQmlMetaType::RegistrationResult QQmlMetaType::registerPluginTypes(
QObject *instance, const QString &basePath, const QString &uri,
const QString &typeNamespace, QTypeRevision version, QList<QQmlError> *errors)
@@ -873,7 +1017,7 @@ QQmlMetaType::RegistrationResult QQmlMetaType::registerPluginTypes(
*/
QQmlType QQmlMetaType::typeForUrl(const QString &urlString,
const QHashedStringRef &qualifiedType,
- bool isCompositeSingleton, QList<QQmlError> *errors,
+ CompositeTypeLookupMode mode, QList<QQmlError> *errors,
QTypeRevision version)
{
// ### unfortunate (costly) conversion
@@ -891,69 +1035,10 @@ QQmlType QQmlMetaType::typeForUrl(const QString &urlString,
return ret;
}
- const int dot = qualifiedType.indexOf(QLatin1Char('.'));
- const QString typeName = dot < 0
- ? qualifiedType.toString()
- : QString(qualifiedType.constData() + dot + 1, qualifiedType.length() - dot - 1);
-
- QStringList failures;
- QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures);
-
- // Register the type. Note that the URI parameters here are empty; for
- // file type imports, we do not place them in a URI as we don't
- // necessarily have a good and unique one (picture a library import,
- // which may be found in multiple plugin locations on disk), but there
- // are other reasons for this too.
- //
- // By not putting them in a URI, we prevent the types from being
- // registered on a QQmlTypeModule; this is important, as once types are
- // placed on there, they cannot be easily removed, meaning if the
- // developer subsequently loads a different import (meaning different
- // types) with the same URI (using, say, a different plugin path), it is
- // very undesirable that we continue to associate the types from the
- // "old" URI with that new module.
- //
- // Not having URIs also means that the types cannot be found by name
- // etc, the only way to look them up is through QQmlImports -- for
- // better or worse.
- const QQmlType::RegistrationType registrationType = isCompositeSingleton
- ? QQmlType::CompositeSingletonType
- : QQmlType::CompositeType;
- if (checkRegistration(registrationType, data, nullptr, typeName, version, {})) {
- auto *priv = new QQmlTypePrivate(registrationType);
- priv->setName(QString(), typeName);
- priv->version = version;
-
- if (isCompositeSingleton) {
- priv->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo;
- priv->extraData.sd->singletonInstanceInfo->url = url;
- priv->extraData.sd->singletonInstanceInfo->typeName = typeName;
- } else {
- priv->extraData.fd->url = url;
- }
-
- data->registerType(priv);
- addTypeToData(priv, data);
- data->urlToType.insert(url, priv);
- return QQmlType(priv);
- }
-
- // This means that the type couldn't be found by URL, but could not be
- // registered either, meaning we most likely were passed some kind of bad
- // data.
- if (errors) {
- QQmlError error;
- error.setDescription(failures.join(u'\n'));
- errors->prepend(error);
- } else {
- qWarning("%s", failures.join(u'\n').toLatin1().constData());
- }
- return QQmlType();
-}
-
-QRecursiveMutex *QQmlMetaType::typeRegistrationLock()
-{
- return metaTypeDataLock();
+ const QQmlType type = createTypeForUrl(
+ data, url, qualifiedType, mode, errors, version);
+ data->urlToType.insert(url, type.priv());
+ return type;
}
/*
@@ -1275,6 +1360,16 @@ QQmlType QQmlMetaType::qmlType(const QUrl &unNormalizedUrl, bool includeNonFileI
return QQmlType();
}
+QQmlType QQmlMetaType::fetchOrCreateInlineComponentTypeForUrl(const QUrl &url)
+{
+ QQmlMetaTypeDataPtr data;
+ const auto it = data->inlineComponentTypes.constFind(url);
+ if (it != data->inlineComponentTypes.constEnd())
+ return *it;
+
+ return doRegisterInlineComponentType(data, url);
+}
+
/*!
Returns a QQmlPropertyCache for \a obj if one is available.
@@ -1346,9 +1441,12 @@ QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCacheForType(QMetaType metaTyp
return composite;
const QQmlTypePrivate *type = data->idToType.value(metaType.id());
- return (type && type->typeId == metaType)
- ? data->propertyCache(QQmlType(type).metaObject(), type->version)
- : QQmlPropertyCache::ConstPtr();
+ if (type && type->typeId == metaType) {
+ if (const QMetaObject *mo = QQmlType(type).metaObject())
+ return data->propertyCache(mo, type->version);
+ }
+
+ return QQmlPropertyCache::ConstPtr();
}
/*!
@@ -1365,8 +1463,15 @@ QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType(QMetaType meta
return composite;
const QQmlTypePrivate *type = data->idToType.value(metaType.id());
- return (type && type->typeId == metaType)
- ? data->propertyCache(type->baseMetaObject, QTypeRevision())
+ if (!type || type->typeId != metaType)
+ return QQmlPropertyCache::ConstPtr();
+
+ const QMetaObject *metaObject = type->isValueType()
+ ? type->metaObjectForValueType()
+ : type->baseMetaObject;
+
+ return metaObject
+ ? data->propertyCache(metaObject, QTypeRevision())
: QQmlPropertyCache::ConstPtr();
}
@@ -1388,8 +1493,11 @@ QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType(
return QQmlPropertyCache::ConstPtr();
const QQmlType type(typePriv);
- if (type.containsRevisionedAttributes())
+ if (type.containsRevisionedAttributes()) {
+ // It can only have (revisioned) properties or methods if it has a metaobject
+ Q_ASSERT(type.metaObject());
return data->propertyCache(type, version);
+ }
if (const QMetaObject *metaObject = type.metaObject())
return data->propertyCache(metaObject, version);
@@ -1402,6 +1510,8 @@ void QQmlMetaType::unregisterType(int typeIndex)
QQmlMetaTypeDataPtr data;
const QQmlType type = data->types.value(typeIndex);
if (const QQmlTypePrivate *d = type.priv()) {
+ if (d->regType == QQmlType::CompositeType || d->regType == QQmlType::CompositeSingletonType)
+ removeFromInlineComponents(data->inlineComponentTypes, d);
removeQQmlTypePrivate(data->idToType, d);
removeQQmlTypePrivate(data->nameToType, d);
removeQQmlTypePrivate(data->urlToType, d);
@@ -1423,16 +1533,41 @@ void QQmlMetaType::registerMetaObjectForType(const QMetaObject *metaobject, QQml
data->metaObjectToType.insert(metaobject, type);
}
-static bool hasActiveInlineComponents(const QQmlTypePrivate *d)
+static bool hasActiveInlineComponents(const QQmlMetaTypeData *data, const QQmlTypePrivate *d)
{
- for (const QQmlType &ic : std::as_const(d->objectIdToICType)) {
- const QQmlTypePrivate *icPriv = ic.priv();
+ for (auto it = data->inlineComponentTypes.begin(), end = data->inlineComponentTypes.end();
+ it != end; ++it) {
+ if (!QQmlMetaType::equalBaseUrls(it.key(), d->sourceUrl()))
+ continue;
+
+ const QQmlTypePrivate *icPriv = it->priv();
if (icPriv && icPriv->count() > 1)
return true;
}
return false;
}
+static int doCountInternalCompositeTypeSelfReferences(
+ QQmlMetaTypeData *data,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
+{
+ int result = 0;
+ auto doCheck = [&](const QtPrivate::QMetaTypeInterface *iface) {
+ if (!iface)
+ return;
+
+ const auto it = data->compositeTypes.constFind(iface);
+ if (it != data->compositeTypes.constEnd() && *it == compilationUnit)
+ ++result;
+ };
+
+ doCheck(compilationUnit->metaType().iface());
+ for (auto &&inlineData: compilationUnit->inlineComponentData)
+ doCheck(inlineData.qmlType.typeId().iface());
+
+ return result;
+}
+
void QQmlMetaType::freeUnusedTypesAndCaches()
{
QQmlMetaTypeDataPtr data;
@@ -1441,15 +1576,33 @@ void QQmlMetaType::freeUnusedTypesAndCaches()
if (!data.isValid())
return;
+ bool droppedAtLeastOneComposite;
+ do {
+ droppedAtLeastOneComposite = false;
+ auto it = data->compositeTypes.begin();
+ while (it != data->compositeTypes.end()) {
+ if ((*it)->count() <= doCountInternalCompositeTypeSelfReferences(data, *it)) {
+ it = data->compositeTypes.erase(it);
+ droppedAtLeastOneComposite = true;
+ } else {
+ ++it;
+ }
+ }
+ } while (droppedAtLeastOneComposite);
+
bool deletedAtLeastOneType;
do {
deletedAtLeastOneType = false;
QList<QQmlType>::Iterator it = data->types.begin();
while (it != data->types.end()) {
const QQmlTypePrivate *d = (*it).priv();
- if (d && d->count() == 1 && !hasActiveInlineComponents(d)) {
+ if (d && d->count() == 1 && !hasActiveInlineComponents(data, d)) {
deletedAtLeastOneType = true;
+ if (d->regType == QQmlType::CompositeType
+ || d->regType == QQmlType::CompositeSingletonType) {
+ removeFromInlineComponents(data->inlineComponentTypes, d);
+ }
removeQQmlTypePrivate(data->idToType, d);
removeQQmlTypePrivate(data->nameToType, d);
removeQQmlTypePrivate(data->urlToType, d);
@@ -1509,7 +1662,7 @@ QList<QQmlType> QQmlMetaType::qmlTypes()
const QQmlMetaTypeDataPtr data;
QList<QQmlType> types;
- for (QQmlTypePrivate *t : data->nameToType)
+ for (const QQmlTypePrivate *t : data->nameToType)
types.append(QQmlType(t));
return types;
@@ -1543,7 +1696,7 @@ QList<QQmlType> QQmlMetaType::qmlSingletonTypes()
static bool isFullyTyped(const QQmlPrivate::CachedQmlUnit *unit)
{
quint32 numTypedFunctions = 0;
- for (const QQmlPrivate::TypedFunction *function = unit->aotCompiledFunctions;
+ for (const QQmlPrivate::AOTCompiledFunction *function = unit->aotCompiledFunctions;
function; ++function) {
if (function->functionPtr)
++numTypedFunctions;
@@ -1562,7 +1715,7 @@ const QQmlPrivate::CachedQmlUnit *QQmlMetaType::findCachedCompilationUnit(
for (const auto lookup : std::as_const(data->lookupCachedQmlUnit)) {
if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) {
QString error;
- if (!QV4::ExecutableCompilationUnit::verifyHeader(unit->qmlData, QDateTime(), &error)) {
+ if (!unit->qmlData->verifyHeader(QDateTime(), &error)) {
qCDebug(DBG_DISK_CACHE) << "Error loading pre-compiled file " << uri << ":" << error;
if (status)
*status = CachedUnitLookupError::VersionMismatch;
@@ -1651,7 +1804,8 @@ QList<QQmlProxyMetaObject::ProxyData> QQmlMetaType::proxyData(const QMetaObject
QList<QQmlProxyMetaObject::ProxyData> metaObjects;
mo = mo->d.superdata;
- const QQmlMetaTypeDataPtr data;
+ if (!mo)
+ return metaObjects;
auto createProxyMetaObject = [&](QQmlTypePrivate *This,
const QMetaObject *superdataBaseMetaObject,
@@ -1674,19 +1828,25 @@ QList<QQmlProxyMetaObject::ProxyData> QQmlMetaType::proxyData(const QMetaObject
registerMetaObjectForType(mmo, This);
};
- while (mo) {
- QQmlTypePrivate *t = data->metaObjectToType.value(mo);
- if (t) {
+ for (const QQmlMetaTypeDataPtr data; mo; mo = mo->d.superdata) {
+ // TODO: There can in fact be multiple QQmlTypePrivate* for a single QMetaObject*.
+ // This algorithm only accounts for the most recently inserted one. That's pretty
+ // random. However, the availability of types depends on what documents you have
+ // loaded before. Just adding all possible extensions would also be pretty random.
+ // The right way to do this would be to take the relations between the QML modules
+ // into account. For this we would need proper module dependency information.
+ if (QQmlTypePrivate *t = data->metaObjectToType.value(mo)) {
if (t->regType == QQmlType::CppType) {
- createProxyMetaObject(t, t->baseMetaObject, t->extraData.cd->extMetaObject,
- t->extraData.cd->extFunc);
+ createProxyMetaObject(
+ t, t->baseMetaObject, t->extraData.cppTypeData->extMetaObject,
+ t->extraData.cppTypeData->extFunc);
} else if (t->regType == QQmlType::SingletonType) {
- createProxyMetaObject(t, t->baseMetaObject, t->extraData.sd->extMetaObject,
- t->extraData.sd->extFunc);
+ createProxyMetaObject(
+ t, t->baseMetaObject, t->extraData.singletonTypeData->extMetaObject,
+ t->extraData.singletonTypeData->extFunc);
}
}
- mo = mo->d.superdata;
- }
+ };
return metaObjects;
}
@@ -1745,8 +1905,12 @@ const QMetaObject *QQmlMetaType::metaObjectForValueType(QMetaType metaType)
// call QObject pointers value types. Explicitly registered types also override
// the implicit use of gadgets.
if (!(metaType.flags() & QMetaType::PointerToQObject)) {
- if (const QMetaObject *mo = metaObjectForValueType(QQmlMetaType::qmlType(metaType)))
- return mo;
+ const QQmlMetaTypeDataPtr data;
+ const QQmlTypePrivate *type = data->idToType.value(metaType.id());
+ if (type && type->regType == QQmlType::CppType && type->typeId == metaType) {
+ if (const QMetaObject *mo = type->metaObjectForValueType())
+ return mo;
+ }
}
// If it _is_ a gadget, we can just use it.
@@ -1775,33 +1939,76 @@ QQmlPropertyCache::ConstPtr QQmlMetaType::findPropertyCacheInCompositeTypes(QMet
return data->findPropertyCacheInCompositeTypes(t);
}
-void QQmlMetaType::registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit)
+void QQmlMetaType::registerInternalCompositeType(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
{
- compilationUnit->isRegistered = true;
-
QQmlMetaTypeDataPtr data;
- // The QQmlCompiledData is not referenced here, but it is removed from this
- // hash in the QQmlCompiledData destructor
- data->compositeTypes.insert(compilationUnit->typeIds.id.iface(), compilationUnit);
+ auto doInsert = [&data, &compilationUnit](const QtPrivate::QMetaTypeInterface *iface) {
+ Q_ASSERT(iface);
+ Q_ASSERT(compilationUnit);
+
+ // We can't assert on anything else here. We may get a completely new type as exposed
+ // by the qmldiskcache test that changes a QML file in place during the execution
+ // of the test.
+ data->compositeTypes.insert(iface, compilationUnit);
+ };
+
+ doInsert(compilationUnit->metaType().iface());
for (auto &&inlineData: compilationUnit->inlineComponentData)
- data->compositeTypes.insert(inlineData.typeIds.id.iface(), compilationUnit);
+ doInsert(inlineData.qmlType.typeId().iface());
}
-void QQmlMetaType::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit)
+void QQmlMetaType::unregisterInternalCompositeType(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
{
- compilationUnit->isRegistered = false;
+ QQmlMetaTypeDataPtr data;
+
+ auto doRemove = [&](const QtPrivate::QMetaTypeInterface *iface) {
+ if (!iface)
+ return;
+
+ const auto it = data->compositeTypes.constFind(iface);
+ if (it != data->compositeTypes.constEnd() && *it == compilationUnit)
+ data->compositeTypes.erase(it);
+ };
+
+ doRemove(compilationUnit->metaType().iface());
+ for (auto &&inlineData: compilationUnit->inlineComponentData)
+ doRemove(inlineData.qmlType.typeId().iface());
+}
+int QQmlMetaType::countInternalCompositeTypeSelfReferences(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
+{
QQmlMetaTypeDataPtr data;
- data->compositeTypes.remove(compilationUnit->typeIds.id.iface());
- for (auto&& icDatum: compilationUnit->inlineComponentData)
- data->compositeTypes.remove(icDatum.typeIds.id.iface());
+ return doCountInternalCompositeTypeSelfReferences(data, compilationUnit);
}
-QV4::ExecutableCompilationUnit *QQmlMetaType::obtainExecutableCompilationUnit(QMetaType type)
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlMetaType::obtainCompilationUnit(
+ QMetaType type)
{
const QQmlMetaTypeDataPtr data;
return data->compositeTypes.value(type.iface());
}
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlMetaType::obtainCompilationUnit(
+ const QUrl &url)
+{
+ const QUrl normalized = QQmlTypeLoader::normalize(url);
+ QQmlMetaTypeDataPtr data;
+
+ auto found = data->urlToType.constFind(normalized);
+ if (found == data->urlToType.constEnd()) {
+ found = data->urlToNonFileImportType.constFind(normalized);
+ if (found == data->urlToNonFileImportType.constEnd())
+ return QQmlRefPointer<QV4::CompiledData::CompilationUnit>();
+ }
+
+ const auto composite = data->compositeTypes.constFind(found.value()->typeId.iface());
+ return composite == data->compositeTypes.constEnd()
+ ? QQmlRefPointer<QV4::CompiledData::CompilationUnit>()
+ : composite.value();
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index c19dd8aff7..f4870d9db1 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -28,88 +28,89 @@ class QRecursiveMutex;
class QQmlError;
class QQmlValueType;
-namespace QV4 { class ExecutableCompilationUnit; }
+namespace QV4 {
+namespace CompiledData {
+struct CompilationUnit;
+}
+}
-struct CompositeMetaTypeIds
+class Q_QML_EXPORT QQmlMetaType
{
-private:
- int *refCount = nullptr;
- void deref();
- void ref()
- {
- Q_ASSERT(refCount);
- ++*refCount;
- }
-public:
- CompositeMetaTypeIds() = default;
- CompositeMetaTypeIds(QMetaType id, QMetaType listId) : id(id), listId(listId) {}
- CompositeMetaTypeIds(const CompositeMetaTypeIds &other)
- : refCount(other.refCount), id(other.id), listId(other.listId)
- {
- if (refCount)
- ref();
- }
- CompositeMetaTypeIds(CompositeMetaTypeIds &&other)
- : refCount(other.refCount), id(other.id), listId(other.listId)
- {
- other.refCount = nullptr;
- }
- CompositeMetaTypeIds &operator=(const CompositeMetaTypeIds &other)
- {
- if (refCount)
- deref();
- refCount = other.refCount;
- id = other.id;
- listId = other.listId;
- if (refCount)
- ref();
- return *this;
- }
- CompositeMetaTypeIds &operator=(CompositeMetaTypeIds &&other)
- {
- if (refCount)
- deref();
- refCount = other.refCount;
- id = other.id;
- listId = other.listId;
- other.refCount = nullptr;
- return *this;
- }
- ~CompositeMetaTypeIds();
- static CompositeMetaTypeIds fromCompositeName(const QByteArray &name);
-public:
- QMetaType id;
- QMetaType listId;
- bool isValid() const { return id.isValid() && listId.isValid(); }
-};
-
-class Q_QML_PRIVATE_EXPORT QQmlMetaType
-{
- friend struct CompositeMetaTypeIds;
friend class QQmlDesignerMetaObject;
- static CompositeMetaTypeIds registerInternalCompositeType(const QByteArray &className);
- static void unregisterInternalCompositeType(const CompositeMetaTypeIds &typeIds);
-
public:
+
enum class RegistrationResult {
Success,
Failure,
NoRegistrationFunction
};
+ static QUrl inlineComponentUrl(const QUrl &baseUrl, const QString &name)
+ {
+ QUrl icUrl = baseUrl;
+ icUrl.setFragment(name);
+ return icUrl;
+ }
+
+ static bool equalBaseUrls(const QUrl &aUrl, const QUrl &bUrl)
+ {
+ // Everything but fragment has to match
+ return aUrl.port() == bUrl.port()
+ && aUrl.scheme() == bUrl.scheme()
+ && aUrl.userName() == bUrl.userName()
+ && aUrl.password() == bUrl.password()
+ && aUrl.host() == bUrl.host()
+ && aUrl.path() == bUrl.path()
+ && aUrl.query() == bUrl.query();
+ }
+
+ enum CompositeTypeLookupMode {
+ NonSingleton,
+ Singleton,
+ };
+
+ static QQmlType findCompositeType(
+ const QUrl &url,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
+ CompositeTypeLookupMode mode = NonSingleton);
+ static QQmlType findInlineComponentType(
+ const QUrl &url,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
+ static QQmlType findInlineComponentType(
+ const QUrl &baseUrl, const QString &name,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
+ {
+ return findInlineComponentType(inlineComponentUrl(baseUrl, name), compilationUnit);
+ }
+
+ static void unregisterInternalCompositeType(QMetaType metaType, QMetaType listMetaType);
static QQmlType registerType(const QQmlPrivate::RegisterType &type);
static QQmlType registerInterface(const QQmlPrivate::RegisterInterface &type);
- static QQmlType registerSingletonType(const QQmlPrivate::RegisterSingletonType &type);
- static QQmlType registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type);
+ static QQmlType registerSingletonType(
+ const QQmlPrivate::RegisterSingletonType &type,
+ const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo);
+ static QQmlType registerCompositeSingletonType(
+ const QQmlPrivate::RegisterCompositeSingletonType &type,
+ const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo);
static QQmlType registerCompositeType(const QQmlPrivate::RegisterCompositeType &type);
static RegistrationResult registerPluginTypes(QObject *instance, const QString &basePath,
const QString &uri, const QString &typeNamespace,
QTypeRevision version, QList<QQmlError> *errors);
+
static QQmlType typeForUrl(const QString &urlString, const QHashedStringRef& typeName,
- bool isCompositeSingleton, QList<QQmlError> *errors,
+ CompositeTypeLookupMode mode, QList<QQmlError> *errors,
QTypeRevision version = QTypeRevision());
+ static QQmlType fetchOrCreateInlineComponentTypeForUrl(const QUrl &url);
+ static QQmlType inlineComponentType(const QQmlType &outerType, const QString &name)
+ {
+ return outerType.isComposite()
+ ? fetchOrCreateInlineComponentTypeForUrl(
+ inlineComponentUrl(outerType.sourceUrl(), name))
+ : QQmlType();
+ }
+
static void unregisterType(int type);
static void registerMetaObjectForType(const QMetaObject *metaobject, QQmlTypePrivate *type);
@@ -197,8 +198,6 @@ public:
static void prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler);
static void removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler);
- static QRecursiveMutex *typeRegistrationLock();
-
static QString prettyTypeName(const QObject *object);
template <typename QQmlTypeContainer>
@@ -213,6 +212,21 @@ public:
}
}
+ template <typename InlineComponentContainer>
+ static void removeFromInlineComponents(
+ InlineComponentContainer &container, const QQmlTypePrivate *reference)
+ {
+ const QUrl referenceUrl = QQmlType(reference).sourceUrl();
+ for (auto it = container.begin(), end = container.end(); it != end;) {
+ if (equalBaseUrls(it.key(), referenceUrl))
+ it = container.erase(it);
+ else
+ ++it;
+ }
+ }
+
+ static void registerTypeAlias(int typeId, const QString &name);
+
static int registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &autoparent);
static void unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function);
@@ -243,31 +257,18 @@ public:
static bool isValueType(QMetaType type);
static QQmlValueType *valueType(QMetaType metaType);
static const QMetaObject *metaObjectForValueType(QMetaType type);
- static const QMetaObject *metaObjectForValueType(const QQmlType &qmlType)
- {
- // Prefer the extension meta object, if any.
- // Extensions allow registration of non-gadget value types.
- if (const QMetaObject *extensionMetaObject = qmlType.extensionMetaObject()) {
- // This may be a namespace even if the original metaType isn't.
- // You can do such things with QML_FOREIGN declarations.
- if (extensionMetaObject->metaType().flags() & QMetaType::IsGadget)
- return extensionMetaObject;
- }
-
- if (const QMetaObject *qmlTypeMetaObject = qmlType.metaObject()) {
- // This may be a namespace even if the original metaType isn't.
- // You can do such things with QML_FOREIGN declarations.
- if (qmlTypeMetaObject->metaType().flags() & QMetaType::IsGadget)
- return qmlTypeMetaObject;
- }
-
- return nullptr;
- }
static QQmlPropertyCache::ConstPtr findPropertyCacheInCompositeTypes(QMetaType t);
- static void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
- static void unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
- static QV4::ExecutableCompilationUnit *obtainExecutableCompilationUnit(QMetaType type);
+ static void registerInternalCompositeType(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
+ static void unregisterInternalCompositeType(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
+ static int countInternalCompositeTypeSelfReferences(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
+ static QQmlRefPointer<QV4::CompiledData::CompilationUnit> obtainCompilationUnit(
+ QMetaType type);
+ static QQmlRefPointer<QV4::CompiledData::CompilationUnit> obtainCompilationUnit(
+ const QUrl &url);
};
Q_DECLARE_TYPEINFO(QQmlMetaType, Q_RELOCATABLE_TYPE);
@@ -287,7 +288,7 @@ struct QQmlMetaTypeInterface : QtPrivate::QMetaTypeInterface
const QByteArray name;
QQmlMetaTypeInterface(const QByteArray &name)
: QMetaTypeInterface {
- /*.revision=*/ 0,
+ /*.revision=*/ QMetaTypeInterface::CurrentRevision,
/*.alignment=*/ alignof(QObject *),
/*.size=*/ sizeof(QObject *),
/*.flags=*/ QtPrivate::QMetaTypeTypeFlags<QObject *>::Flags,
@@ -322,7 +323,7 @@ struct QQmlListMetaTypeInterface : QtPrivate::QMetaTypeInterface
const QtPrivate::QMetaTypeInterface *valueType;
QQmlListMetaTypeInterface(const QByteArray &name, const QtPrivate::QMetaTypeInterface *valueType)
: QMetaTypeInterface {
- /*.revision=*/ 0,
+ /*.revision=*/ QMetaTypeInterface::CurrentRevision,
/*.alignment=*/ alignof(QQmlListProperty<QObject>),
/*.size=*/ sizeof(QQmlListProperty<QObject>),
/*.flags=*/ QtPrivate::QMetaTypeTypeFlags<QQmlListProperty<QObject>>::Flags,
diff --git a/src/qml/qml/qqmlmetatypedata.cpp b/src/qml/qml/qqmlmetatypedata.cpp
index ab6054349a..bc7e762e53 100644
--- a/src/qml/qml/qqmlmetatypedata.cpp
+++ b/src/qml/qml/qqmlmetatypedata.cpp
@@ -15,8 +15,12 @@ QQmlMetaTypeData::QQmlMetaTypeData()
QQmlMetaTypeData::~QQmlMetaTypeData()
{
- for (auto iter = compositeTypes.cbegin(), end = compositeTypes.cend(); iter != end; ++iter)
- iter.value()->isRegistered = false;
+ {
+ // Unregister all remaining composite types.
+ // Avoid deletion recursion (via QQmlTypePrivate dtor) by moving them out of the way first.
+ CompositeTypes emptyComposites;
+ emptyComposites.swap(compositeTypes);
+ }
propertyCaches.clear();
// Do this before the attached properties disappear.
@@ -137,6 +141,7 @@ QQmlPropertyCache::ConstPtr QQmlMetaTypeData::propertyCache(
quint8 maxMinorVersion = 0;
const QMetaObject *metaObject = type.metaObject();
+ Q_ASSERT(metaObject);
const QTypeRevision combinedVersion = version.hasMajorVersion()
? version
@@ -236,13 +241,11 @@ QQmlPropertyCache::ConstPtr QQmlMetaTypeData::propertyCache(
}
static QQmlPropertyCache::ConstPtr propertyCacheForPotentialInlineComponentType(
- QMetaType t,
- const QHash<const QtPrivate::QMetaTypeInterface *,
- QV4::ExecutableCompilationUnit *>::const_iterator &iter) {
- if (t != (*iter)->typeIds.id) {
+ QMetaType t, const QQmlMetaTypeData::CompositeTypes::const_iterator &iter) {
+ if (t != (*iter)->metaType()) {
// this is an inline component, and what we have in the iterator is currently the parent compilation unit
for (auto &&icDatum: (*iter)->inlineComponentData)
- if (icDatum.typeIds.id == t)
+ if (icDatum.qmlType.typeId() == t)
return (*iter)->propertyCaches.at(icDatum.objectIndex);
}
return (*iter)->rootPropertyCache();
diff --git a/src/qml/qml/qqmlmetatypedata_p.h b/src/qml/qml/qqmlmetatypedata_p.h
index 60b76137d1..8863bd1089 100644
--- a/src/qml/qml/qqmlmetatypedata_p.h
+++ b/src/qml/qml/qqmlmetatypedata_p.h
@@ -36,10 +36,10 @@ struct QQmlMetaTypeData
typedef QHash<int, QQmlTypePrivate *> Ids;
Ids idToType;
- using Names = QMultiHash<QHashedString, QQmlTypePrivate *>;
+ using Names = QMultiHash<QHashedString, const QQmlTypePrivate *>;
Names nameToType;
- typedef QHash<QUrl, QQmlTypePrivate *> Files; //For file imported composite types only
+ typedef QHash<QUrl, const QQmlTypePrivate *> Files; //For file imported composite types only
Files urlToType;
Files urlToNonFileImportType; // For non-file imported composite and composite
// singleton types. This way we can locate any
@@ -49,7 +49,11 @@ struct QQmlMetaTypeData
MetaObjects metaObjectToType;
QVector<QHash<QTypeRevision, QQmlPropertyCache::ConstPtr>> typePropertyCaches;
QHash<int, QQmlValueType *> metaTypeToValueType;
- QHash<const QtPrivate::QMetaTypeInterface *, QV4::ExecutableCompilationUnit *> compositeTypes;
+
+ using CompositeTypes = QHash<const QtPrivate::QMetaTypeInterface *,
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit>>;
+ CompositeTypes compositeTypes;
+ QHash<QUrl, QQmlType> inlineComponentTypes;
struct VersionedUri {
VersionedUri() = default;
diff --git a/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp b/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp
index 05c9347fab..ed4fd34073 100644
--- a/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp
+++ b/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp
@@ -18,9 +18,20 @@ QT_BEGIN_NAMESPACE
with custom QNetworkAccessManager instances with specialized caching,
proxy and cookies support.
+ \list
+ \li The QNetworkDiskCache can be used as a request cache with \l {QNetworkDiskCache}.
+ \li Using \l {QNetworkProxy}, traffic sent by the QNetworkAccessManager can be tunnelled through a proxy.
+ \li Cookies can be saved for future requests by adding a \l {QNetworkCookieJar}.
+ \endlist
+
To implement a factory, subclass QQmlNetworkAccessManagerFactory and
implement the virtual create() method, then assign it to the relevant QML
- engine using QQmlEngine::setNetworkAccessManagerFactory().
+ engine using QQmlEngine::setNetworkAccessManagerFactory(). For instance, the QNetworkAccessManager
+ objects created by the following snippet will cache requests.
+ \snippet code/src_network_access_qnetworkaccessmanager.cpp 0
+
+ The factory can then be passed to the QML engine so it can instantiate the QNetworkAccessManager with the custom behavior.
+ \snippet code/src_network_access_qnetworkaccessmanager.cpp 1
Note the QML engine may create QNetworkAccessManager instances
from multiple threads. Because of this, the implementation of the create()
@@ -44,7 +55,7 @@ QT_BEGIN_NAMESPACE
For more information about signals and threads, see
\l {Threads and QObjects} and \l {Signals and Slots Across Threads}.
- \sa {C++ Extensions: Network Access Manager Factory Example}{Network Access Manager Factory Example}
+ \sa QNetworkDiskCache
*/
/*!
diff --git a/src/qml/qml/qqmlnotifier_p.h b/src/qml/qml/qqmlnotifier_p.h
index 844766a633..0ec7d5fef2 100644
--- a/src/qml/qml/qqmlnotifier_p.h
+++ b/src/qml/qml/qqmlnotifier_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QQmlNotifierEndpoint;
class QQmlData;
-class Q_QML_PRIVATE_EXPORT QQmlNotifier
+class Q_QML_EXPORT QQmlNotifier
{
public:
inline QQmlNotifier();
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index c014c24d43..a9b9140390 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -38,6 +38,19 @@ Q_LOGGING_CATEGORY(lcQmlDefaultMethod, "qt.qml.defaultmethod")
QT_USE_NAMESPACE
+Q_TRACE_PREFIX(qtqml,
+"namespace QV4 {" \
+"struct ExecutionEngine;" \
+"class ExecutableCompilationUnit;" \
+"namespace CompiledData {" \
+"struct Object;" \
+"}}" \
+"class QQmlEngine;"
+)
+
+Q_TRACE_POINT(qtqml, QQmlObjectCreator_createInstance_entry, const QV4::ExecutableCompilationUnit *compilationUnit, const QV4::CompiledData::Object *object, const QUrl &url)
+Q_TRACE_POINT(qtqml, QQmlObjectCreator_createInstance_exit, const QString &typeName)
+
QQmlObjectCreator::QQmlObjectCreator(
QQmlRefPointer<QQmlContextData> parentContext,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
@@ -45,7 +58,7 @@ QQmlObjectCreator::QQmlObjectCreator(
QQmlIncubatorPrivate *incubator)
: phase(Startup)
, compilationUnit(compilationUnit)
- , propertyCaches(&compilationUnit->propertyCaches)
+ , propertyCaches(compilationUnit->propertyCachesPtr())
, sharedState(new QQmlObjectCreatorSharedState, QQmlRefPointer<QQmlObjectCreatorSharedState>::Adopt)
, topLevelCreator(true)
, isContextObject(true)
@@ -57,7 +70,7 @@ QQmlObjectCreator::QQmlObjectCreator(
sharedState->allCreatedBindings.allocate(compilationUnit->totalBindingsCount());
sharedState->allParserStatusCallbacks.allocate(compilationUnit->totalParserStatusCount());
sharedState->allCreatedObjects.allocate(compilationUnit->totalObjectCount());
- sharedState->allJavaScriptObjects = nullptr;
+ sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList();
sharedState->creationContext = creationContext;
sharedState->rootContext.reset();
sharedState->hadTopLevelRequiredProperties = false;
@@ -75,7 +88,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlRefPointer<QQmlContextData> parentConte
QQmlObjectCreatorSharedState *inheritedSharedState, bool isContextObject)
: phase(Startup)
, compilationUnit(compilationUnit)
- , propertyCaches(&compilationUnit->propertyCaches)
+ , propertyCaches(compilationUnit->propertyCachesPtr())
, sharedState(inheritedSharedState)
, topLevelCreator(false)
, isContextObject(isContextObject)
@@ -90,8 +103,10 @@ void QQmlObjectCreator::init(QQmlRefPointer<QQmlContextData> providedParentConte
engine = parentContext->engine();
v4 = engine->handle();
- if (compilationUnit && !compilationUnit->engine)
- compilationUnit->linkToEngine(v4);
+ Q_ASSERT(compilationUnit);
+ Q_ASSERT(compilationUnit->engine == v4);
+ if (!compilationUnit->runtimeStrings)
+ compilationUnit->populate();
qmlUnit = compilationUnit->unitData();
_qobject = nullptr;
@@ -141,7 +156,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
} else {
Q_ASSERT(subComponentIndex >= 0);
if (flags & CreationFlags::InlineComponent) {
- if (compilationUnit->unitData()->flags & QV4::CompiledData::Unit::ComponentsBound
+ if (compilationUnit->componentsAreBound()
&& compilationUnit != parentContext->typeCompilationUnit()) {
recordError({}, tr("Cannot instantiate bound inline component in different file"));
phase = ObjectsCreated;
@@ -151,7 +166,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
isComponentRoot = true;
} else {
Q_ASSERT(flags & CreationFlags::NormalObject);
- if (compilationUnit->unitData()->flags & QV4::CompiledData::Unit::ComponentsBound
+ if (compilationUnit->componentsAreBound()
&& sharedState->creationContext != parentContext) {
recordError({}, tr("Cannot instantiate bound component "
"outside its creation context"));
@@ -174,9 +189,9 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
QV4::Scope scope(v4);
- Q_ASSERT(sharedState->allJavaScriptObjects || topLevelCreator);
+ Q_ASSERT(sharedState->allJavaScriptObjects.canTrack() || topLevelCreator);
if (topLevelCreator)
- sharedState->allJavaScriptObjects = scope.alloc(compilationUnit->totalObjectCount());
+ sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList(scope, compilationUnit->totalObjectCount());
if (!isComponentRoot && sharedState->creationContext) {
// otherwise QQmlEnginePrivate::createInternalContext() handles it
@@ -191,7 +206,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
}
if (topLevelCreator)
- sharedState->allJavaScriptObjects = nullptr;
+ sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList();
phase = CreatingObjectsPhase2;
@@ -220,10 +235,11 @@ void QQmlObjectCreator::beginPopulateDeferred(const QQmlRefPointer<QQmlContextDa
sharedState->rootContext = newContext;
Q_ASSERT(topLevelCreator);
- Q_ASSERT(!sharedState->allJavaScriptObjects);
+ Q_ASSERT(!sharedState->allJavaScriptObjects.canTrack());
+ // FIXME (QTBUG-122956): allocating from the short lived scope does not make any sense
QV4::Scope valueScope(v4);
- sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount());
+ sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList(valueScope, compilationUnit->totalObjectCount());
}
void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex,
@@ -277,6 +293,16 @@ void QQmlObjectCreator::populateDeferredBinding(const QQmlProperty &qmlProperty,
}
}
+void QQmlObjectCreator::populateDeferredInstance(
+ QObject *outerObject, int deferredIndex, int index, QObject *instance,
+ QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty,
+ const QV4::CompiledData::Binding *binding)
+{
+ doPopulateDeferred(outerObject, deferredIndex, [&]() {
+ populateInstance(index, instance, bindingTarget, valueTypeProperty, binding);
+ });
+}
+
void QQmlObjectCreator::finalizePopulateDeferred()
{
phase = ObjectsCreated;
@@ -290,8 +316,11 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
QMetaType propertyType = property->propType();
if (property->isEnum()) {
- if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum)) {
- propertyType = QMetaType::fromType<int>();
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum) ||
+ // TODO: For historical reasons you can assign any number to an enum property alias
+ // This can be fixed with an opt-out mechanism, for example a pragma.
+ (property->isAlias() && binding->isNumberBinding())) {
+ propertyType = property->propType().underlyingType();
} else {
// ### This should be resolved earlier at compile time and the binding value should be changed accordingly.
QVariant value = compilationUnit->bindingValueAsString(binding);
@@ -413,6 +442,49 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
}
break;
+ case QMetaType::SChar: {
+ assertType(QV4::CompiledData::Binding::Type_Number);
+ double d = compilationUnit->bindingValueAsNumber(binding);
+ qint8 value = qint8(d);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
+ break;
+ }
+ case QMetaType::UChar: {
+ assertType(QV4::CompiledData::Binding::Type_Number);
+ double d = compilationUnit->bindingValueAsNumber(binding);
+ quint8 value = quint8(d);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
+ break;
+ }
+ case QMetaType::Short: {
+ assertType(QV4::CompiledData::Binding::Type_Number);
+ double d = compilationUnit->bindingValueAsNumber(binding);
+ qint16 value = qint16(d);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
+ break;
+ }
+ case QMetaType::UShort: {
+ assertType(QV4::CompiledData::Binding::Type_Number);
+ double d = compilationUnit->bindingValueAsNumber(binding);
+ quint16 value = quint16(d);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
+ break;
+ }
+ case QMetaType::LongLong: {
+ assertType(QV4::CompiledData::Binding::Type_Number);
+ double d = compilationUnit->bindingValueAsNumber(binding);
+ qint64 value = qint64(d);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
+ break;
+ }
+ case QMetaType::ULongLong: {
+ assertType(QV4::CompiledData::Binding::Type_Number);
+ double d = compilationUnit->bindingValueAsNumber(binding);
+ quint64 value = quint64(d);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
+ break;
+ }
+ break;
case QMetaType::Float: {
assertType(QV4::CompiledData::Binding::Type_Number);
float value = float(compilationUnit->bindingValueAsNumber(binding));
@@ -426,9 +498,9 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
}
break;
case QMetaType::QColor: {
- QVariant data(propertyType);
- if (QQmlValueTypeProvider::createValueType(
- compilationUnit->bindingValueAsString(binding), propertyType, data.data())) {
+ QVariant data = QQmlValueTypeProvider::createValueType(
+ compilationUnit->bindingValueAsString(binding), propertyType);
+ if (data.isValid()) {
property->writeProperty(_qobject, data.data(), propertyWriteFlags);
}
}
@@ -509,12 +581,9 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
case QMetaType::QVector3D:
case QMetaType::QVector4D:
case QMetaType::QQuaternion: {
- QVariant result(propertyType);
- bool ok = QQmlValueTypeProvider::createValueType(
- compilationUnit->bindingValueAsString(binding),
- result.metaType(), result.data());
- assertOrNull(ok);
- Q_UNUSED(ok);
+ QVariant result = QQmlValueTypeProvider::createValueType(
+ compilationUnit->bindingValueAsString(binding), propertyType);
+ assertOrNull(result.isValid());
property->writeProperty(_qobject, result.data(), propertyWriteFlags);
break;
}
@@ -602,8 +671,8 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
}
- QVariant target(propertyType);
- if (QQmlValueTypeProvider::createValueType(source, propertyType, target.data())) {
+ QVariant target = QQmlValueTypeProvider::createValueType(source, propertyType);
+ if (target.isValid()) {
property->writeProperty(_qobject, target.data(), propertyWriteFlags);
break;
}
@@ -635,10 +704,11 @@ void QQmlObjectCreator::setupBindings(BindingSetupFlags mode)
QQmlListProperty<void> savedList;
qSwap(_currentList, savedList);
- const QV4::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(_compiledObjectIndex);
+ const QV4::CompiledData::BindingPropertyData *propertyData
+ = compilationUnit->bindingPropertyDataPerObjectAt(_compiledObjectIndex);
if (_compiledObject->idNameIndex) {
- const QQmlPropertyData *idProperty = propertyData.last();
+ const QQmlPropertyData *idProperty = propertyData->last();
Q_ASSERT(!idProperty || !idProperty->isValid() || idProperty->name(_qobject) == QLatin1String("id"));
if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType().id() == QMetaType::QString) {
QV4::CompiledData::Binding idBinding;
@@ -685,7 +755,7 @@ void QQmlObjectCreator::setupBindings(BindingSetupFlags mode)
const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable();
for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) {
- const QQmlPropertyData *const property = propertyData.at(i);
+ const QQmlPropertyData *const property = propertyData->at(i);
if (property) {
const QQmlPropertyData *targetProperty = property;
if (targetProperty->isAlias()) {
@@ -766,16 +836,17 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
QV4::ResolvedTypeReference *tr = resolvedType(binding->propertyNameIndex);
Q_ASSERT(tr);
QQmlType attachedType = tr->type();
+ QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
if (!attachedType.isValid()) {
QQmlTypeNameCache::Result res = context->imports()->query(
- stringAt(binding->propertyNameIndex));
+ stringAt(binding->propertyNameIndex), QQmlTypeLoader::get(enginePrivate));
if (res.isValid())
attachedType = res.type;
else
return false;
}
QObject *qmlObject = qmlAttachedPropertiesObject(
- _qobject, attachedType.attachedPropertiesFunction(QQmlEnginePrivate::get(engine)));
+ _qobject, attachedType.attachedPropertiesFunction(enginePrivate));
if (!qmlObject) {
recordError(binding->location,
QStringLiteral("Could not create attached properties object '%1'")
@@ -889,14 +960,13 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
&& !(bindingFlags & QV4::CompiledData::Binding::IsPropertyObserver)
&& !_valueTypeProperty;
- if (_ddata->hasBindingBit(bindingProperty->coreIndex()) && allowedToRemoveBinding) {
- QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex()));
- } else if (bindingProperty->isBindable() && allowedToRemoveBinding) {
- QList<DeferredQPropertyBinding> &pendingBindings = sharedState.data()->allQPropertyBindings;
- auto it = std::remove_if(pendingBindings.begin(), pendingBindings.end(), [&](const DeferredQPropertyBinding &deferred) {
- return deferred.properyIndex == bindingProperty->coreIndex() && deferred.target == _bindingTarget;
- });
- pendingBindings.erase(it, pendingBindings.end());
+ if (allowedToRemoveBinding) {
+ if (bindingProperty->isBindable()) {
+ removePendingBinding(_bindingTarget, bindingProperty->coreIndex());
+ } else {
+ QQmlPropertyPrivate::removeBinding(
+ _bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex()));
+ }
}
if (bindingType == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) {
@@ -942,6 +1012,9 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
qmlBinding = QQmlPropertyBinding::create(bindingProperty, runtimeFunction, _scopeObject, context, currentQmlContext(), _bindingTarget, index);
}
sharedState.data()->allQPropertyBindings.push_back(DeferredQPropertyBinding {_bindingTarget, bindingProperty->coreIndex(), qmlBinding });
+
+ QQmlData *data = QQmlData::get(_bindingTarget, true);
+ data->setBindingBit(_bindingTarget, bindingProperty->coreIndex());
} else {
// When writing bindings to grouped properties implemented as value types,
// such as point.x: { someExpression; }, then the binding is installed on
@@ -1067,9 +1140,9 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
recordError(binding->valueLocation, tr("Cannot assign object type %1 with no default method").arg(QString::fromLatin1(createdSubObject->metaObject()->className())));
return false;
}
- qCWarning(lcQmlDefaultMethod) << "Assigning an object to a signal handler is deprecated."
- "Instead, create the object, give it an id, and call the desired slot from the signal handler."
- ;
+ qCWarning(lcQmlDefaultMethod) << "Assigning an object to a signal handler is deprecated. "
+ "Instead, create the object, give it an id, and call the desired slot "
+ "from the signal handler. The object is:" << createdSubObject;
QMetaMethod signalMethod = _qobject->metaObject()->method(bindingProperty->coreIndex());
if (!QMetaObject::checkConnectArgs(signalMethod, method)) {
@@ -1250,7 +1323,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
sharedState->allCreatedObjects.push(instance);
} else {
- const auto compilationUnit = typeRef->compilationUnit();
+ auto compilationUnit = typeRef->compilationUnit();
Q_ASSERT(compilationUnit);
typeName = compilationUnit->fileName();
// compilation unit is shared between root type and its inline component types
@@ -1261,19 +1334,42 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
}
if (!type.isInlineComponentType()) {
- QQmlObjectCreator subCreator(context, compilationUnit, sharedState.data(),
- isContextObject);
+ QQmlObjectCreator subCreator(
+ context, engine->handle()->executableCompilationUnit(
+ std::move(compilationUnit)),
+ sharedState.data(), isContextObject);
instance = subCreator.create();
if (!instance) {
errors += subCreator.errors;
return nullptr;
}
} else {
- int subObjectId = type.inlineComponentId();
- QScopedValueRollback<int> rollback {compilationUnit->icRoot, subObjectId};
- QQmlObjectCreator subCreator(context, compilationUnit, sharedState.data(),
- isContextObject);
- instance = subCreator.create(subObjectId, nullptr, nullptr, CreationFlags::InlineComponent);
+ QString subObjectName;
+ if (QString *icRootName = compilationUnit->icRootName.get()) {
+ subObjectName = type.elementName();
+ std::swap(*icRootName, subObjectName);
+ } else {
+ compilationUnit->icRootName = std::make_unique<QString>(type.elementName());
+ }
+
+ const auto guard = qScopeGuard([&] {
+ if (subObjectName.isEmpty())
+ compilationUnit->icRootName.reset();
+ else
+ std::swap(*compilationUnit->icRootName, subObjectName);
+ });
+
+ const int inlineComponentId
+ = compilationUnit->inlineComponentId(*compilationUnit->icRootName);
+ QQmlObjectCreator subCreator(
+ context,
+ engine->handle()->executableCompilationUnit(
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit>(
+ compilationUnit)),
+ sharedState.data(),
+ isContextObject);
+ instance = subCreator.create(
+ inlineComponentId, nullptr, nullptr, CreationFlags::InlineComponent);
if (!instance) {
errors += subCreator.errors;
return nullptr;
@@ -1326,7 +1422,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
if (customParser && obj->hasFlag(QV4::CompiledData::Object::HasCustomParserBindings)) {
customParser->engine = QQmlEnginePrivate::get(engine);
- customParser->imports = compilationUnit->typeNameCache.data();
+ customParser->imports = compilationUnit->typeNameCache().data();
QList<const QV4::CompiledData::Binding *> bindings;
const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index);
@@ -1354,9 +1450,8 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
QObject *scopeObject = instance;
qSwap(_scopeObject, scopeObject);
- Q_ASSERT(sharedState->allJavaScriptObjects);
- *sharedState->allJavaScriptObjects = QV4::QObjectWrapper::wrap(v4, instance);
- ++sharedState->allJavaScriptObjects;
+ Q_ASSERT(sharedState->allJavaScriptObjects.canTrack());
+ sharedState->allJavaScriptObjects.trackObject(v4, instance);
QV4::Scope valueScope(v4);
QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc());
@@ -1438,17 +1533,27 @@ bool QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt)
while (!sharedState->allQPropertyBindings.isEmpty()) {
auto& [target, index, qmlBinding] = sharedState->allQPropertyBindings.first();
+
+ QQmlData *data = QQmlData::get(target);
+ if (!data || !data->hasBindingBit(index)) {
+ // The target property has been overwritten since we stashed the binding.
+ sharedState->allQPropertyBindings.pop_front();
+ continue;
+ }
+
QUntypedBindable bindable;
void *argv[] = { &bindable };
// allow interception
target->metaObject()->metacall(target, QMetaObject::BindableProperty, index, argv);
const bool success = bindable.setBinding(qmlBinding);
+ const auto bindingPrivateRefCount = QPropertyBindingPrivate::get(qmlBinding)->refCount();
+
// Only pop_front after setting the binding as the bindings are refcounted.
sharedState->allQPropertyBindings.pop_front();
// If the binding was actually not set, it's deleted now.
- if (success) {
+ if (success && bindingPrivateRefCount > 1) {
if (auto priv = QPropertyBindingPrivate::get(qmlBinding); priv->hasCustomVTable()) {
auto qmlBindingPriv = static_cast<QQmlPropertyBinding *>(priv);
auto jsExpression = qmlBindingPriv->jsExpression();
@@ -1747,3 +1852,15 @@ QQmlObjectCreatorRecursionWatcher::QQmlObjectCreatorRecursionWatcher(QQmlObjectC
, watcher(creator->sharedState.data())
{
}
+
+void ObjectInCreationGCAnchorList::trackObject(QV4::ExecutionEngine *engine, QObject *instance)
+{
+ *allJavaScriptObjects = QV4::QObjectWrapper::wrap(engine, instance);
+ // we have to handle the case where the gc is already running, but the scope is discarded
+ // before the collector runs again. In that case, rescanning won't help us. Thus, mark the
+ // object.
+ QV4::WriteBarrier::markCustom(engine, [this](QV4::MarkStack *ms) {
+ allJavaScriptObjects->heapObject()->mark(ms);
+ });
+ ++allJavaScriptObjects;
+}
diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h
index c951072dd5..8b1c251e2b 100644
--- a/src/qml/qml/qqmlobjectcreator_p.h
+++ b/src/qml/qml/qqmlobjectcreator_p.h
@@ -84,14 +84,28 @@ struct DeferredQPropertyBinding {
QUntypedPropertyBinding binding;
};
-struct QQmlObjectCreatorSharedState : QQmlRefCount
+class ObjectInCreationGCAnchorList {
+public:
+ // this is a non owning view, rule of zero applies
+ ObjectInCreationGCAnchorList() = default;
+ ObjectInCreationGCAnchorList(const QV4::Scope &scope, int totalObjectCount)
+ {
+ allJavaScriptObjects = scope.alloc(totalObjectCount);
+ }
+ void trackObject(QV4::ExecutionEngine *engine, QObject *instance);
+ bool canTrack() const { return allJavaScriptObjects; }
+private:
+ QV4::Value *allJavaScriptObjects = nullptr; // pointer to vector on JS stack to reference JS wrappers during creation phase.
+};
+
+struct QQmlObjectCreatorSharedState final : QQmlRefCounted<QQmlObjectCreatorSharedState>
{
QQmlRefPointer<QQmlContextData> rootContext;
QQmlRefPointer<QQmlContextData> creationContext;
QFiniteStack<QQmlAbstractBinding::Ptr> allCreatedBindings;
QFiniteStack<QQmlParserStatus*> allParserStatusCallbacks;
QFiniteStack<QQmlGuard<QObject> > allCreatedObjects;
- QV4::Value *allJavaScriptObjects; // pointer to vector on JS stack to reference JS wrappers during creation phase.
+ ObjectInCreationGCAnchorList allJavaScriptObjects; // pointer to vector on JS stack to reference JS wrappers during creation phase.
QQmlComponentAttached *componentAttached;
QList<QQmlFinalizerHook *> finalizeHooks;
QQmlVmeProfiler profiler;
@@ -101,7 +115,7 @@ struct QQmlObjectCreatorSharedState : QQmlRefCount
bool hadTopLevelRequiredProperties;
};
-class Q_QML_PRIVATE_EXPORT QQmlObjectCreator
+class Q_QML_EXPORT QQmlObjectCreator
{
Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator)
public:
@@ -120,6 +134,10 @@ public:
void beginPopulateDeferred(const QQmlRefPointer<QQmlContextData> &context);
void populateDeferredBinding(const QQmlProperty &qmlProperty, int deferredIndex,
const QV4::CompiledData::Binding *binding);
+ void populateDeferredInstance(QObject *outerObject, int deferredIndex,
+ int index, QObject *instance, QObject *bindingTarget,
+ const QQmlPropertyData *valueTypeProperty,
+ const QV4::CompiledData::Binding *binding = nullptr);
void finalizePopulateDeferred();
bool finalize(QQmlInstantiationInterrupt &interrupt);
@@ -144,6 +162,14 @@ public:
int index, QObject *parent,
const QQmlRefPointer<QQmlContextData> &context);
+ void removePendingBinding(QObject *target, int propertyIndex)
+ {
+ QList<DeferredQPropertyBinding> &pendingBindings = sharedState.data()->allQPropertyBindings;
+ pendingBindings.removeIf([&](const DeferredQPropertyBinding &deferred) {
+ return deferred.properyIndex == propertyIndex && deferred.target == target;
+ });
+ }
+
private:
QQmlObjectCreator(QQmlRefPointer<QQmlContextData> contextData,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
@@ -231,6 +257,10 @@ private:
void doPopulateDeferred(QObject *instance, int deferredIndex, Functor f)
{
QQmlData *declarativeData = QQmlData::get(instance);
+
+ // We're in the process of creating the object. We sure hope it's still alive.
+ Q_ASSERT(declarativeData && declarativeData->propertyCache);
+
QObject *bindingTarget = instance;
QQmlPropertyCache::ConstPtr cache = declarativeData->propertyCache;
@@ -240,8 +270,9 @@ private:
qt_ptr_swap(_scopeObject, scopeObject);
QV4::Scope valueScope(v4);
- QScopedValueRollback<QV4::Value*> jsObjectGuard(sharedState->allJavaScriptObjects,
- valueScope.alloc(compilationUnit->totalObjectCount()));
+ QScopedValueRollback<ObjectInCreationGCAnchorList> jsObjectGuard(
+ sharedState->allJavaScriptObjects,
+ ObjectInCreationGCAnchorList(valueScope, compilationUnit->totalObjectCount()));
Q_ASSERT(topLevelCreator);
QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc());
diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp
index cf51286677..2d0a78c6cb 100644
--- a/src/qml/qml/qqmlopenmetaobject.cpp
+++ b/src/qml/qml/qqmlopenmetaobject.cpp
@@ -5,8 +5,10 @@
#include <private/qqmlpropertycache_p.h>
#include <private/qqmldata_p.h>
#include <private/qqmlmetatype_p.h>
+
#include <private/qmetaobjectbuilder_p.h>
#include <qdebug.h>
+#include <QtCore/qpointer.h>
#include <QtCore/qset.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qml/qml/qqmlopenmetaobject_p.h b/src/qml/qml/qqmlopenmetaobject_p.h
index 7b09566710..35e595ec15 100644
--- a/src/qml/qml/qqmlopenmetaobject_p.h
+++ b/src/qml/qml/qqmlopenmetaobject_p.h
@@ -28,11 +28,12 @@ QT_BEGIN_NAMESPACE
class QQmlEngine;
class QMetaPropertyBuilder;
class QQmlOpenMetaObjectTypePrivate;
-class Q_QML_PRIVATE_EXPORT QQmlOpenMetaObjectType : public QQmlRefCount
+class Q_QML_EXPORT QQmlOpenMetaObjectType final
+ : public QQmlRefCounted<QQmlOpenMetaObjectType>
{
public:
QQmlOpenMetaObjectType(const QMetaObject *base);
- ~QQmlOpenMetaObjectType() override;
+ ~QQmlOpenMetaObjectType();
void createProperties(const QVector<QByteArray> &names);
int createProperty(const QByteArray &name);
@@ -53,7 +54,7 @@ private:
};
class QQmlOpenMetaObjectPrivate;
-class Q_QML_PRIVATE_EXPORT QQmlOpenMetaObject : public QAbstractDynamicMetaObject
+class Q_QML_EXPORT QQmlOpenMetaObject : public QAbstractDynamicMetaObject
{
public:
QQmlOpenMetaObject(QObject *, const QMetaObject * = nullptr);
diff --git a/src/qml/qml/qqmlplatform.cpp b/src/qml/qml/qqmlplatform.cpp
index dbd54b5f11..dd52ee2090 100644
--- a/src/qml/qml/qqmlplatform.cpp
+++ b/src/qml/qml/qqmlplatform.cpp
@@ -22,13 +22,18 @@ QQmlPlatform::~QQmlPlatform()
QString QQmlPlatform::os()
{
+ // ### Qt7: Consider implementing in terms of QSysInfo
+
#if defined(Q_OS_ANDROID)
return QStringLiteral("android");
#elif defined(Q_OS_IOS)
return QStringLiteral("ios");
#elif defined(Q_OS_TVOS)
return QStringLiteral("tvos");
-#elif defined(Q_OS_MAC)
+#elif defined(Q_OS_VISIONOS)
+ return QStringLiteral("visionos");
+#elif defined(Q_OS_MACOS)
+ // ### Qt7: Replace with "macos"
return QStringLiteral("osx");
#elif defined(Q_OS_WIN)
return QStringLiteral("windows");
diff --git a/src/qml/qml/qqmlplatform_p.h b/src/qml/qml/qqmlplatform_p.h
index 567a2f0f64..9905f6330c 100644
--- a/src/qml/qml/qqmlplatform_p.h
+++ b/src/qml/qml/qqmlplatform_p.h
@@ -21,13 +21,12 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlPlatform : public QObject
+class Q_QML_EXPORT QQmlPlatform : public QObject
{
Q_OBJECT
Q_PROPERTY(QString os READ os CONSTANT)
Q_PROPERTY(QString pluginName READ pluginName CONSTANT)
QML_ANONYMOUS
- QML_ADDED_IN_VERSION(2, 0)
public:
explicit QQmlPlatform(QObject *parent = nullptr);
@@ -42,6 +41,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQmlPlatform)
-
#endif // QQMLPLATFORM_P_H
diff --git a/src/qml/qml/qqmlpluginimporter.cpp b/src/qml/qml/qqmlpluginimporter.cpp
index 8f33e11649..091cd28b73 100644
--- a/src/qml/qml/qqmlpluginimporter.cpp
+++ b/src/qml/qml/qqmlpluginimporter.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmlpluginimporter_p.h"
+#include "qqmlimport_p.h"
#include <private/qqmlextensionplugin_p.h>
#include <private/qqmltypeloader_p.h>
@@ -17,8 +18,6 @@
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcQmlImport)
-
struct QmlPlugin {
std::unique_ptr<QPluginLoader> loader;
};
@@ -59,12 +58,6 @@ private:
Q_GLOBAL_STATIC(PluginMap, qmlPluginsById); // stores the uri and the PluginLoaders
-static QTypeRevision validVersion(QTypeRevision version = QTypeRevision())
-{
- // If the given version is invalid, return a valid but useless version to signal "It's OK".
- return version.isValid() ? version : QTypeRevision::fromMinorVersion(0);
-}
-
static QVector<QStaticPlugin> makePlugins()
{
QVector<QStaticPlugin> plugins;
@@ -217,7 +210,7 @@ QTypeRevision QQmlPluginImporter::importStaticPlugin(QObject *instance, const QS
if (!database->initializedPlugins.contains(pluginId))
finalizePlugin(instance, pluginId);
- return validVersion(importVersion);
+ return QQmlImports::validVersion(importVersion);
}
QTypeRevision QQmlPluginImporter::importDynamicPlugin(
@@ -326,7 +319,7 @@ QTypeRevision QQmlPluginImporter::importDynamicPlugin(
if (!engineInitialized)
finalizePlugin(instance, pluginId);
- return validVersion(importVersion);
+ return QQmlImports::validVersion(importVersion);
}
/*!
@@ -611,7 +604,7 @@ QTypeRevision QQmlPluginImporter::importPlugins() {
database->modulesForWhichPluginsHaveBeenLoaded.insert(moduleId);
}
- return validVersion(importVersion);
+ return QQmlImports::validVersion(importVersion);
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index 7d5f99ca5c..92c9765509 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -15,25 +15,26 @@
// We mean it.
//
-#include <QtQml/qtqmlglobal.h>
-#include <QtQml/qqmlparserstatus.h>
+#include <QtQml/qjsprimitivevalue.h>
+#include <QtQml/qjsvalue.h>
#include <QtQml/qqmllist.h>
+#include <QtQml/qqmlparserstatus.h>
#include <QtQml/qqmlpropertyvaluesource.h>
-#include <QtQml/qjsvalue.h>
+#include <QtQml/qtqmlglobal.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qdebug.h>
#include <QtCore/qglobal.h>
-#include <QtCore/qvariant.h>
-#include <QtCore/qurl.h>
+#include <QtCore/qmetacontainer.h>
+#include <QtCore/qmetaobject.h>
#include <QtCore/qpointer.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qvariant.h>
#include <QtCore/qversionnumber.h>
-#include <QtCore/qmetaobject.h>
-#include <QtCore/qmetacontainer.h>
-#include <QtCore/qdebug.h>
-
#include <functional>
-#include <type_traits>
#include <limits>
+#include <type_traits>
QT_BEGIN_NAMESPACE
@@ -81,6 +82,10 @@ class QQmlEngine;
class QQmlCustomParser;
class QQmlTypeNotAvailable;
+class QQmlV4Function;
+using QQmlV4FunctionPtr = QQmlV4Function *;
+using QQmlV4ExecutionEnginePtr = QV4::ExecutionEngine *;
+
template<class T>
QQmlCustomParser *qmlCreateCustomParser()
{
@@ -114,7 +119,7 @@ namespace QQmlPrivate
#endif
};
- enum class ConstructionMode
+ enum class SingletonConstructionMode
{
None,
Constructor,
@@ -139,18 +144,18 @@ namespace QQmlPrivate
};
template<typename T, typename WrapperT>
- constexpr ConstructionMode constructionMode()
+ constexpr SingletonConstructionMode singletonConstructionMode()
{
if constexpr (!std::is_base_of<QObject, T>::value)
- return ConstructionMode::None;
+ return SingletonConstructionMode::None;
if constexpr (!std::is_same_v<T, WrapperT> && HasSingletonFactory<T, WrapperT>::value)
- return ConstructionMode::FactoryWrapper;
+ return SingletonConstructionMode::FactoryWrapper;
if constexpr (std::is_default_constructible<T>::value)
- return ConstructionMode::Constructor;
+ return SingletonConstructionMode::Constructor;
if constexpr (HasSingletonFactory<T>::value)
- return ConstructionMode::Factory;
+ return SingletonConstructionMode::Factory;
- return ConstructionMode::None;
+ return SingletonConstructionMode::None;
}
template<typename>
@@ -168,16 +173,16 @@ namespace QQmlPrivate
template<typename T>
void createInto(void *memory, void *) { new (memory) QQmlElement<T>; }
- template<typename T, typename WrapperT, ConstructionMode Mode>
+ template<typename T, typename WrapperT, SingletonConstructionMode Mode>
QObject *createSingletonInstance(QQmlEngine *q, QJSEngine *j)
{
Q_UNUSED(q);
Q_UNUSED(j);
- if constexpr (Mode == ConstructionMode::Constructor)
+ if constexpr (Mode == SingletonConstructionMode::Constructor)
return new T;
- else if constexpr (Mode == ConstructionMode::Factory)
+ else if constexpr (Mode == SingletonConstructionMode::Factory)
return T::create(q, j);
- else if constexpr (Mode == ConstructionMode::FactoryWrapper)
+ else if constexpr (Mode == SingletonConstructionMode::FactoryWrapper)
return WrapperT::create(q, j);
else
return nullptr;
@@ -191,39 +196,43 @@ namespace QQmlPrivate
using CreateParentFunction = QObject *(*)(QObject *);
using CreateValueTypeFunction = QVariant (*)(const QJSValue &);
- template<typename T, typename WrapperT = T, ConstructionMode Mode = constructionMode<T, WrapperT>()>
+ template<typename T, typename WrapperT = T,
+ SingletonConstructionMode Mode = singletonConstructionMode<T, WrapperT>()>
struct Constructors;
template<typename T, typename WrapperT>
- struct Constructors<T, WrapperT, ConstructionMode::Constructor>
+ struct Constructors<T, WrapperT, SingletonConstructionMode::Constructor>
{
static constexpr CreateIntoFunction createInto
= QQmlPrivate::createInto<T>;
static constexpr CreateSingletonFunction createSingletonInstance
- = QQmlPrivate::createSingletonInstance<T, WrapperT, ConstructionMode::Constructor>;
+ = QQmlPrivate::createSingletonInstance<
+ T, WrapperT, SingletonConstructionMode::Constructor>;
};
template<typename T, typename WrapperT>
- struct Constructors<T, WrapperT, ConstructionMode::None>
+ struct Constructors<T, WrapperT, SingletonConstructionMode::None>
{
static constexpr CreateIntoFunction createInto = nullptr;
static constexpr CreateSingletonFunction createSingletonInstance = nullptr;
};
template<typename T, typename WrapperT>
- struct Constructors<T, WrapperT, ConstructionMode::Factory>
+ struct Constructors<T, WrapperT, SingletonConstructionMode::Factory>
{
static constexpr CreateIntoFunction createInto = nullptr;
static constexpr CreateSingletonFunction createSingletonInstance
- = QQmlPrivate::createSingletonInstance<T, WrapperT, ConstructionMode::Factory>;
+ = QQmlPrivate::createSingletonInstance<
+ T, WrapperT, SingletonConstructionMode::Factory>;
};
template<typename T, typename WrapperT>
- struct Constructors<T, WrapperT, ConstructionMode::FactoryWrapper>
+ struct Constructors<T, WrapperT, SingletonConstructionMode::FactoryWrapper>
{
static constexpr CreateIntoFunction createInto = nullptr;
static constexpr CreateSingletonFunction createSingletonInstance
- = QQmlPrivate::createSingletonInstance<T, WrapperT, ConstructionMode::FactoryWrapper>;
+ = QQmlPrivate::createSingletonInstance<
+ T, WrapperT, SingletonConstructionMode::FactoryWrapper>;
};
template<typename T,
@@ -361,8 +370,8 @@ namespace QQmlPrivate
struct Properties<Parent, void>
{
using Func = QQmlAttachedPropertiesFunc<QObject>;
- static const QMetaObject *staticMetaObject() { return nullptr; };
- static Func attachedPropertiesFunc() { return nullptr; };
+ static const QMetaObject *staticMetaObject() { return nullptr; }
+ static Func attachedPropertiesFunc() { return nullptr; }
};
using Type = typename std::conditional<
@@ -619,6 +628,7 @@ namespace QQmlPrivate
qintptr extraData;
};
+ QObject *thisObject() const;
QQmlEngine *qmlEngine() const;
QJSValue jsMetaType(int index) const;
@@ -640,7 +650,23 @@ namespace QQmlPrivate
QtMsgType type, const QString &message,
const QLoggingCategory *loggingCategory) const;
- QString objectToString(QObject *object) const;
+ QVariant constructValueType(
+ QMetaType resultMetaType, const QMetaObject *resultMetaObject,
+ int ctorIndex, void *ctorArg) const;
+
+ // Those are explicit arguments to the Date() ctor, not implicit coercions.
+ QDateTime constructDateTime(double timestamp) const;
+ QDateTime constructDateTime(const QString &string) const;
+ QDateTime constructDateTime(const QJSPrimitiveValue &arg) const
+ {
+ return arg.type() == QJSPrimitiveValue::String
+ ? constructDateTime(arg.toString())
+ : constructDateTime(arg.toDouble());
+ }
+
+ QDateTime constructDateTime(
+ double year, double month, double day = 1,
+ double hours = 0, double minutes = 0, double seconds = 0, double msecs = 0) const;
// All of these lookup functions should be used as follows:
//
@@ -679,6 +705,7 @@ namespace QQmlPrivate
void initLoadGlobalLookup(uint index) const;
bool loadScopeObjectPropertyLookup(uint index, void *target) const;
+ bool writeBackScopeObjectPropertyLookup(uint index, void *source) const;
void initLoadScopeObjectPropertyLookup(uint index, QMetaType type) const;
bool loadSingletonLookup(uint index, void *target) const;
@@ -691,12 +718,17 @@ namespace QQmlPrivate
void initLoadTypeLookup(uint index, uint importNamespace) const;
bool getObjectLookup(uint index, QObject *object, void *target) const;
+ bool writeBackObjectLookup(uint index, QObject *object, void *source) const;
void initGetObjectLookup(uint index, QObject *object, QMetaType type) const;
bool getValueLookup(uint index, void *value, void *target) const;
+ bool writeBackValueLookup(uint index, void *value, void *source) const;
void initGetValueLookup(uint index, const QMetaObject *metaObject, QMetaType type) const;
+ bool getEnumLookup(uint index, void *target) const;
+#if QT_QML_REMOVED_SINCE(6, 6)
bool getEnumLookup(uint index, int *target) const;
+#endif
void initGetEnumLookup(uint index, const QMetaObject *metaObject,
const char *enumerator, const char *enumValue) const;
@@ -707,21 +739,21 @@ namespace QQmlPrivate
void initSetValueLookup(uint index, const QMetaObject *metaObject, QMetaType type) const;
};
- struct TypedFunction {
- qintptr extraData;
- QMetaType returnType;
- QList<QMetaType> argumentTypes;
- void (*functionPtr)(const AOTCompiledContext *context, void *resultPtr, void **arguments);
+ struct AOTCompiledFunction {
+ int functionIndex;
+ int numArguments;
+ void (*signature)(QV4::ExecutableCompilationUnit *unit, QMetaType *argTypes);
+ void (*functionPtr)(const AOTCompiledContext *context, void **argv);
};
-#if QT_DEPRECATED_SINCE(6, 5)
- QT_DEPRECATED_VERSION_X(6, 5, "Use TypedFunction instead")
- typedef TypedFunction AOTCompiledFunction;
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_DEPRECATED_VERSION_X(6, 6, "Use AOTCompiledFunction instead")
+ typedef AOTCompiledFunction TypedFunction;
#endif
struct CachedQmlUnit {
const QV4::CompiledData::Unit *qmlData;
- const TypedFunction *aotCompiledFunctions;
+ const AOTCompiledFunction *aotCompiledFunctions;
void *unused2;
};
@@ -810,29 +842,6 @@ namespace QQmlPrivate
return qstrcmp(metaObject->classInfo(index).value(), "true") == 0;
}
- inline const char *classElementName(const QMetaObject *metaObject)
- {
- const char *elementName = classInfo(metaObject, "QML.Element");
- if (qstrcmp(elementName, "auto") == 0) {
- const char *strippedClassName = metaObject->className();
- for (const char *c = strippedClassName; *c != '\0'; c++) {
- if (*c == ':')
- strippedClassName = c + 1;
- }
-
- return strippedClassName;
- }
- if (qstrcmp(elementName, "anonymous") == 0)
- return nullptr;
-
- if (!elementName) {
- qWarning().nospace() << "Missing QML.Element class info \"" << elementName << "\""
- << " for " << metaObject->className();
- }
-
- return elementName;
- }
-
template<class T, class = std::void_t<>>
struct QmlExtended
{
@@ -893,6 +902,20 @@ namespace QQmlPrivate
&& bool(T::QmlIsUncreatable::yes);
};
+ template<class T, class = std::void_t<>>
+ struct QmlAnonymous
+ {
+ static constexpr bool Value = false;
+ };
+
+ template<class T>
+ struct QmlAnonymous<T, std::void_t<typename T::QmlIsAnonymous>>
+ {
+ static constexpr bool Value =
+ QmlTypeHasMarker<T, decltype(&T::qt_qmlMarker_anonymous)>::value
+ && bool(T::QmlIsAnonymous::yes);
+ };
+
template<class T, class = std::void_t<>>
struct QmlSingleton
@@ -929,7 +952,7 @@ namespace QQmlPrivate
};
template<class T>
- struct QmlInterface<T, std::void_t<typename T::QmlIsInterface>>
+ struct QmlInterface<T, std::void_t<typename T::QmlIsInterface, decltype(qobject_interface_iid<T *>())>>
{
static constexpr bool Value = bool(T::QmlIsInterface::yes);
};
@@ -946,39 +969,9 @@ namespace QQmlPrivate
static const QMetaObject *staticMetaObject() { return &T::staticMetaObject; }
};
- /*! \internal
- For singletons: check that there is a static create method. It is needed by the engine to
- initialize a singleton object in case T has no default constructor.
-
- Using a default constructor is preferred over the static create method.
- */
- template<class T, class = std::void_t<>>
- struct HasStaticCreate
- {
- static constexpr bool Value = false;
- };
-
- // std::is_member_pointer<&X::Y> returns false when Y is a static member, and
- // sfinae takes care of the existence Y being a member of X
- template<class T>
- struct HasStaticCreate<
- T,
- typename std::enable_if<!std::is_member_pointer<decltype(&T::create)>::value
- && std::is_invocable_r_v<T *, decltype(&T::create),
- QQmlEngine *, QJSEngine *>>::type>
- {
- static constexpr bool Value = true;
- };
-
template<class T>
struct QmlMetaType
{
- static constexpr bool hasAcceptableSingletonCtors()
- {
- return std::is_default_constructible_v<T> || HasStaticCreate<T>::Value;
- }
-
-
static constexpr bool hasAcceptableCtors()
{
if constexpr (!std::is_default_constructible_v<T>)
@@ -989,7 +982,7 @@ namespace QQmlPrivate
return std::is_copy_constructible_v<T>;
}
- static QMetaType self()
+ static constexpr QMetaType self()
{
if constexpr (std::is_base_of_v<QObject, T>)
return QMetaType::fromType<T*>();
@@ -997,7 +990,7 @@ namespace QQmlPrivate
return QMetaType::fromType<T>();
}
- static QMetaType list()
+ static constexpr QMetaType list()
{
if constexpr (std::is_base_of_v<QObject, T>)
return QMetaType::fromType<QQmlListProperty<T>>();
@@ -1005,13 +998,28 @@ namespace QQmlPrivate
return QMetaType::fromType<QList<T>>();
}
- static QMetaSequence sequence()
+ static constexpr QMetaSequence sequence()
{
if constexpr (std::is_base_of_v<QObject, T>)
return QMetaSequence();
else
return QMetaSequence::fromContainer<QList<T>>();
}
+
+ static constexpr int size()
+ {
+ return sizeof(T);
+ }
+ };
+
+ template<>
+ struct QmlMetaType<void>
+ {
+ static constexpr bool hasAcceptableCtors() { return true; }
+ static constexpr QMetaType self() { return QMetaType(); }
+ static constexpr QMetaType list() { return QMetaType(); }
+ static constexpr QMetaSequence sequence() { return QMetaSequence(); }
+ static constexpr int size() { return 0; }
};
template<typename T, typename E, typename WrapperT = T>
@@ -1052,7 +1060,7 @@ namespace QQmlPrivate
3,
QmlMetaType<T>::self(),
QmlMetaType<T>::list(),
- int(sizeof(T)),
+ QmlMetaType<T>::size(),
Constructors<T>::createInto,
nullptr,
ValueType<T, E>::create,
@@ -1144,8 +1152,16 @@ namespace QQmlPrivate
Q_QML_EXPORT void qmlRegistrationWarning(QmlRegistrationWarning warning, QMetaType type);
+ Q_QML_EXPORT QMetaType compositeMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName);
+ Q_QML_EXPORT QMetaType compositeListMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName);
+
} // namespace QQmlPrivate
QT_END_NAMESPACE
+Q_DECLARE_OPAQUE_POINTER(QQmlV4FunctionPtr)
+Q_DECLARE_OPAQUE_POINTER(QQmlV4ExecutionEnginePtr)
+
#endif // QQMLPRIVATE_H
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index ad08f06b05..7c78fbb984 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -2,40 +2,35 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmlproperty.h"
-#include "qqmlproperty_p.h"
-
-#include "qqmlboundsignal_p.h"
-#include "qqmlcontext.h"
-#include "qqmlboundsignal_p.h"
-#include "qqmlengine.h"
-#include "qqmlengine_p.h"
-#include "qqmldata_p.h"
-#include "qqmlstringconverters_p.h"
-
-#include "qqmlvmemetaobject_p.h"
-#include "qqmlvaluetypeproxybinding_p.h"
+
#include <private/qjsvalue_p.h>
-#include <private/qv4functionobject_p.h>
-#include <private/qv4qobjectwrapper_p.h>
+#include <private/qmetaobject_p.h>
+#include <private/qproperty_p.h>
+#include <private/qqmlboundsignal_p.h>
#include <private/qqmlbuiltinfunctions_p.h>
+#include <private/qqmldata_p.h>
+#include <private/qqmlengine_p.h>
#include <private/qqmlirbuilder_p.h>
-#include <QtQml/private/qqmllist_p.h>
-
-#include <QStringList>
-#include <QVector>
-#include <private/qmetaobject_p.h>
+#include <private/qqmllist_p.h>
+#include <private/qqmlproperty_p.h>
+#include <private/qqmlsignalnames_p.h>
+#include <private/qqmlstringconverters_p.h>
+#include <private/qqmlvaluetypeproxybinding_p.h>
#include <private/qqmlvaluetypewrapper_p.h>
+#include <private/qqmlvmemetaobject_p.h>
+#include <private/qv4functionobject_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlpropertymap.h>
+
#include <QtCore/qdebug.h>
-#include <cmath>
-#include <QtQml/QQmlPropertyMap>
-#include <QtCore/private/qproperty_p.h>
#include <QtCore/qsequentialiterable.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qvector.h>
-Q_DECLARE_METATYPE(QList<int>)
-Q_DECLARE_METATYPE(QList<qreal>)
-Q_DECLARE_METATYPE(QList<bool>)
-Q_DECLARE_METATYPE(QList<QString>)
-Q_DECLARE_METATYPE(QList<QUrl>)
+#include <cmath>
QT_BEGIN_NAMESPACE
@@ -252,10 +247,11 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name,
// Types must begin with an uppercase letter (see checkRegistration()
// in qqmlmetatype.cpp for the enforcement of this).
if (typeNameCache && !pathName.isEmpty() && pathName.at(0).isUpper()) {
- QQmlTypeNameCache::Result r = typeNameCache->query(pathName);
+ QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
+ QQmlTypeLoader *typeLoader = QQmlTypeLoader::get(enginePrivate);
+ QQmlTypeNameCache::Result r = typeNameCache->query(pathName, typeLoader);
if (r.isValid()) {
if (r.type.isValid()) {
- QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate);
if (!func) return; // Not an attachable type
@@ -267,12 +263,11 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name,
// TODO: Do we really _not_ want to query the namespaced types here?
r = typeNameCache->query<QQmlTypeNameCache::QueryNamespaced::No>(
- path.at(ii), r.importNamespace);
+ path.at(ii), r.importNamespace, typeLoader);
if (!r.type.isValid())
return; // Invalid type in namespace
- QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate);
if (!func)
return; // Not an attachable type
@@ -370,10 +365,9 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name,
QQmlData *ddata = QQmlData::get(currentObject, false);
auto findChangeSignal = [&](QStringView signalName) {
- const QString changed = QStringLiteral("Changed");
- if (signalName.endsWith(changed)) {
- const QStringView propName = signalName.first(signalName.size() - changed.size());
- const QQmlPropertyData *d = ddata->propertyCache->property(propName, currentObject, context);
+ if (auto propName = QQmlSignalNames::changedSignalNameToPropertyName(signalName)) {
+ const QQmlPropertyData *d =
+ ddata->propertyCache->property(*propName, currentObject, context);
while (d && d->isFunction())
d = ddata->propertyCache->overrideData(d);
@@ -386,20 +380,11 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name,
return false;
};
- const QString terminalString = terminal.toString();
- if (QmlIR::IRBuilder::isSignalPropertyName(terminalString)) {
- QString signalName = terminalString.mid(2);
- int firstNon_;
- int length = signalName.size();
- for (firstNon_ = 0; firstNon_ < length; ++firstNon_)
- if (signalName.at(firstNon_) != u'_')
- break;
- signalName[firstNon_] = signalName.at(firstNon_).toLower();
-
+ const auto findSignal = [&](const QString &signalName) {
if (ddata && ddata->propertyCache) {
// Try method
- const QQmlPropertyData *d = ddata->propertyCache->property(
- signalName, currentObject, context);
+ const QQmlPropertyData *d
+ = ddata->propertyCache->property(signalName, currentObject, context);
// ### Qt7: This code treats methods as signals. It should use d->isSignal().
// That would be a change in behavior, though. Right now you can construct a
@@ -410,13 +395,30 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name,
if (d) {
object = currentObject;
core = *d;
- return;
+ return true;
}
- if (findChangeSignal(signalName))
- return;
- } else if (findSignalInMetaObject(signalName.toUtf8())) {
+ return findChangeSignal(signalName);
+ }
+
+ return findSignalInMetaObject(signalName.toUtf8());
+ };
+
+ auto signalName = QQmlSignalNames::handlerNameToSignalName(terminal);
+ if (signalName) {
+ if (findSignal(*signalName))
return;
+ } else {
+ signalName = QQmlSignalNames::badHandlerNameToSignalName(terminal);
+ if (signalName) {
+ if (findSignal(*signalName)) {
+ qWarning()
+ << terminal
+ << "is not a properly capitalized signal handler name."
+ << QQmlSignalNames::signalNameToHandlerName(*signalName)
+ << "would be correct.";
+ return;
+ }
}
}
@@ -429,7 +431,7 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name,
if (!property->isFunction()) {
object = currentObject;
core = *property;
- nameCache = terminalString;
+ nameCache = terminal.toString();
return;
}
property = ddata->propertyCache->overrideData(property);
@@ -750,15 +752,7 @@ QString QQmlProperty::name() const
d->nameCache = d->core.name(d->object) + QLatin1Char('.') + QString::fromUtf8(vtName);
} else if (type() & SignalProperty) {
// ### Qt7: Return the original signal name here. Do not prepend "on"
- QString name = QStringLiteral("on") + d->core.name(d->object);
- for (int i = 2, end = name.size(); i != end; ++i) {
- const QChar c = name.at(i);
- if (c != u'_') {
- name[i] = c.toUpper();
- break;
- }
- }
- d->nameCache = name;
+ d->nameCache = QQmlSignalNames::signalNameToHandlerName(d->core.name(d->object));
} else {
d->nameCache = d->core.name(d->object);
}
@@ -1231,14 +1225,9 @@ bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx,
v = QVariant(menum.keyToValue(value.toByteArray(), &ok));
if (!ok)
return false;
- } else if (v.userType() != QMetaType::Int && v.userType() != QMetaType::UInt) {
- int enumMetaTypeId = QMetaType::fromName(
- QByteArray(menum.scope() + QByteArray("::") + menum.name())).id();
- if ((enumMetaTypeId == QMetaType::UnknownType) || (v.userType() != enumMetaTypeId) || !v.constData())
- return false;
- v = QVariant(*reinterpret_cast<const int *>(v.constData()));
}
- v.convert(QMetaType(QMetaType::Int));
+ if (!v.convert(prop.metaType())) // ### TODO: underlyingType might be faster?
+ return false;
}
// the status variable is changed by qt_metacall to indicate what it did
@@ -1384,6 +1373,18 @@ static ConvertAndAssignResult tryConvertAndAssign(
return {false, false};
}
+ if (variantMetaType == QMetaType::fromType<QJSValue>()) {
+ // Handle Qt.binding bindings here to avoid mistaken conversion below
+ const QJSValue &jsValue = get<QJSValue>(value);
+ const QV4::FunctionObject *f
+ = QJSValuePrivate::asManagedType<QV4::FunctionObject>(&jsValue);
+ if (f && f->isBinding()) {
+ QV4::QObjectWrapper::setProperty(
+ f->engine(), object, &property, f->asReturnedValue());
+ return {true, true};
+ }
+ }
+
// common cases:
switch (propertyMetaType.id()) {
case QMetaType::Bool:
@@ -1429,14 +1430,15 @@ static ConvertAndAssignResult tryConvertAndAssign(
}
}
- QVariant converted(propertyMetaType);
- if (QQmlValueTypeProvider::createValueType(value, propertyMetaType, converted.data())
- || QMetaType::convert(value.metaType(), value.constData(),
- propertyMetaType, converted.data())) {
- return {true, property.writeProperty(object, converted.data(), flags)};
+ QVariant converted = QQmlValueTypeProvider::createValueType(value, propertyMetaType);
+ if (!converted.isValid()) {
+ converted = QVariant(propertyMetaType);
+ if (!QMetaType::convert(value.metaType(), value.constData(),
+ propertyMetaType, converted.data())) {
+ return {false, false};
+ }
}
-
- return {false, false};
+ return {true, property.writeProperty(object, converted.data(), flags)};
};
template<typename Op>
@@ -1559,18 +1561,26 @@ bool QQmlPropertyPrivate::write(
if (valueMetaObject.isNull())
return false;
- QQmlListProperty<void> prop;
+ QQmlListProperty<QObject> prop;
property.readProperty(object, &prop);
- if (!prop.clear)
+ if (!prop.clear || !prop.append)
return false;
- prop.clear(&prop);
+ const bool useNonsignalingListOps = prop.clear == &QQmlVMEMetaObject::list_clear
+ && prop.append == &QQmlVMEMetaObject::list_append;
+
+ auto propClear =
+ useNonsignalingListOps ? &QQmlVMEMetaObject::list_clear_nosignal : prop.clear;
+ auto propAppend =
+ useNonsignalingListOps ? &QQmlVMEMetaObject::list_append_nosignal : prop.append;
+
+ propClear(&prop);
const auto doAppend = [&](QObject *o) {
if (o && !QQmlMetaObject::canConvert(o, valueMetaObject))
o = nullptr;
- prop.append(&prop, o);
+ propAppend(&prop, o);
};
if (variantMetaType == QMetaType::fromType<QQmlListReference>()) {
@@ -1581,9 +1591,18 @@ bool QQmlPropertyPrivate::write(
const QList<QObject *> &list = qvariant_cast<QList<QObject *> >(value);
for (qsizetype ii = 0; ii < list.size(); ++ii)
doAppend(list.at(ii));
+ } else if (variantMetaType == QMetaType::fromType<QList<QVariant>>()) {
+ const QList<QVariant> &list
+ = *static_cast<const QList<QVariant> *>(value.constData());
+ for (const QVariant &entry : list)
+ doAppend(QQmlMetaType::toQObject(entry));
} else if (!iterateQObjectContainer(variantMetaType, value.data(), doAppend)) {
doAppend(QQmlMetaType::toQObject(value));
}
+ if (useNonsignalingListOps) {
+ Q_ASSERT(QQmlVMEMetaObject::get(object));
+ QQmlVMEResolvedList(&prop).activateSignal();
+ }
} else if (variantMetaType == propertyMetaType) {
QVariant v = value;
property.writeProperty(object, v.data(), flags);
@@ -1595,16 +1614,6 @@ bool QQmlPropertyPrivate::write(
sequence.addValue(list.data(), value.data());
property.writeProperty(object, list.data(), flags);
}
- } else if (variantMetaType == QMetaType::fromType<QJSValue>()) {
- QJSValue jsValue = qvariant_cast<QJSValue>(value);
- const QV4::FunctionObject *f
- = QJSValuePrivate::asManagedType<QV4::FunctionObject>(&jsValue);
- if (f && f->isBinding()) {
- QV4::QObjectWrapper::setProperty(
- f->engine(), object, &property, f->asReturnedValue());
- return true;
- }
- return false;
} else if (enginePriv && propertyMetaType == QMetaType::fromType<QJSValue>()) {
// We can convert everything into a QJSValue if we have an engine.
QJSValue jsValue = QJSValuePrivate::fromReturnedValue(
@@ -1696,13 +1705,13 @@ bool QQmlPropertyPrivate::write(
if (!ok && QQmlMetaType::isInterface(propertyMetaType)) {
auto valueAsQObject = qvariant_cast<QObject *>(value);
- if (void *interface = valueAsQObject
+ if (void *iface = valueAsQObject
? valueAsQObject->qt_metacast(QQmlMetaType::interfaceIId(propertyMetaType))
: nullptr;
- interface) {
+ iface) {
// this case can occur when object has an interface type
// and the variant contains a type implementing the interface
- return property.writeProperty(object, &interface, flags);
+ return property.writeProperty(object, &iface, flags);
}
}
@@ -1947,9 +1956,8 @@ QMetaMethod QQmlPropertyPrivate::findSignalByName(const QMetaObject *mo, const Q
// If no signal is found, but the signal is of the form "onBlahChanged",
// return the notify signal for the property "Blah"
- if (name.endsWith("Changed")) {
- QByteArray propName = name.mid(0, name.size() - 7);
- int propIdx = mo->indexOfProperty(propName.constData());
+ if (auto propName = QQmlSignalNames::changedSignalNameToPropertyName(name)) {
+ int propIdx = mo->indexOfProperty(propName->constData());
if (propIdx >= 0) {
QMetaProperty prop = mo->property(propIdx);
if (prop.hasNotifySignal())
diff --git a/src/qml/qml/qqmlproperty.h b/src/qml/qml/qqmlproperty.h
index 0878034bab..cbe5eb21ad 100644
--- a/src/qml/qml/qqmlproperty.h
+++ b/src/qml/qml/qqmlproperty.h
@@ -23,7 +23,6 @@ class Q_QML_EXPORT QQmlProperty
{
Q_GADGET
QML_ANONYMOUS
- QML_ADDED_IN_VERSION(2, 15)
Q_PROPERTY(QObject *object READ object CONSTANT FINAL)
Q_PROPERTY(QString name READ name CONSTANT FINAL)
diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h
index 06693f8d0a..fd07547d60 100644
--- a/src/qml/qml/qqmlproperty_p.h
+++ b/src/qml/qml/qqmlproperty_p.h
@@ -26,6 +26,8 @@
#include <QtQml/qqmlengine.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQmlContext;
@@ -35,7 +37,7 @@ class QQmlMetaObject;
class QQmlAbstractBinding;
class QQmlBoundSignalExpression;
-class Q_QML_PRIVATE_EXPORT QQmlPropertyPrivate : public QQmlRefCount
+class Q_QML_EXPORT QQmlPropertyPrivate final : public QQmlRefCounted<QQmlPropertyPrivate>
{
public:
enum class InitFlag {
diff --git a/src/qml/qml/qqmlpropertybinding.cpp b/src/qml/qml/qqmlpropertybinding.cpp
index 5c8325a425..c8a7e6256a 100644
--- a/src/qml/qml/qqmlpropertybinding.cpp
+++ b/src/qml/qml/qqmlpropertybinding.cpp
@@ -16,6 +16,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::Literals::StringLiterals;
+
Q_LOGGING_CATEGORY(lcQQPropertyBinding, "qt.qml.propertybinding");
QUntypedPropertyBinding QQmlPropertyBinding::create(const QQmlPropertyData *pd, QV4::Function *function,
@@ -230,10 +232,17 @@ void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep, void
setIsUndefined(true);
//suspend binding evaluation state for reset and subsequent read
auto state = QtPrivate::suspendCurrentBindingStatus();
- prop.reset();
+ prop.reset(); // May re-allocate the bindingData
QVariant currentValue = QVariant(prop.propertyMetaType(), propertyDataPtr);
QtPrivate::restoreBindingStatus(state);
writeBackCurrentValue(std::move(currentValue));
+
+ // Re-fetch binding data
+ bindingData = storage->bindingData(propertyDataPtr);
+ if (!bindingData)
+ bindingData = bindingDataFromPropertyData(propertyDataPtr, propertyData->propType());
+ bindingDataPointer = QPropertyBindingDataPointer {bindingData};
+
// reattach the binding (without causing a new notification)
if (Q_UNLIKELY(bindingData->d() & QtPrivate::QPropertyBindingData::BindingBit)) {
qCWarning(lcQQPropertyBinding) << "Resetting " << prop.name() << "due to the binding becoming undefined caused a new binding to be installed\n"
@@ -245,7 +254,7 @@ void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep, void
firstObserver = bindingDataPointer.firstObserver();
bindingData->d_ref() = reinterpret_cast<quintptr>(this) | QtPrivate::QPropertyBindingData::BindingBit;
if (firstObserver)
- bindingDataPointer.setObservers(firstObserver.ptr);
+ prependObserver(firstObserver);
} else {
QQmlError qmlError;
auto location = jsExpression()->sourceLocation();
@@ -272,7 +281,7 @@ QString QQmlPropertyBinding::createBindingLoopErrorDescription()
Q_ASSERT(propertyData);
Q_ASSERT(!targetIndex().hasValueTypeIndex());
QQmlProperty prop = QQmlPropertyPrivate::restore(target(), *propertyData, &valueTypeData, nullptr);
- return QStringLiteral(R"(QML %1: Binding loop detected for property "%2")").arg(QQmlMetaType::prettyTypeName(target()) , prop.name());
+ return R"(QML %1: Binding loop detected for property "%2")"_L1.arg(QQmlMetaType::prettyTypeName(target()) , prop.name());
}
void QQmlPropertyBinding::bindingErrorCallback(QPropertyBindingPrivate *that)
diff --git a/src/qml/qml/qqmlpropertybinding_p.h b/src/qml/qml/qqmlpropertybinding_p.h
index 50688fa245..840239285e 100644
--- a/src/qml/qml/qqmlpropertybinding_p.h
+++ b/src/qml/qml/qqmlpropertybinding_p.h
@@ -33,7 +33,7 @@ namespace QV4 {
class QQmlPropertyBinding;
class QQmlScriptString;
-class Q_QML_PRIVATE_EXPORT QQmlPropertyBindingJS : public QQmlJavaScriptExpression
+class Q_QML_EXPORT QQmlPropertyBindingJS : public QQmlJavaScriptExpression
{
bool mustCaptureBindableProperty() const final {return false;}
@@ -47,14 +47,14 @@ class Q_QML_PRIVATE_EXPORT QQmlPropertyBindingJS : public QQmlJavaScriptExpressi
inline QQmlPropertyBinding const *asBinding() const;
};
-class Q_QML_PRIVATE_EXPORT QQmlPropertyBindingJSForBoundFunction : public QQmlPropertyBindingJS
+class Q_QML_EXPORT QQmlPropertyBindingJSForBoundFunction : public QQmlPropertyBindingJS
{
public:
QV4::ReturnedValue evaluate(bool *isUndefined);
QV4::PersistentValue m_boundFunction;
};
-class Q_QML_PRIVATE_EXPORT QQmlPropertyBinding : public QPropertyBindingPrivate
+class Q_QML_EXPORT QQmlPropertyBinding : public QPropertyBindingPrivate
{
friend class QQmlPropertyBindingJS;
@@ -220,10 +220,10 @@ inline const QtPrivate::BindingFunctionVTable *bindingFunctionVTableForQQmlPrope
class QQmlTranslationPropertyBinding
{
public:
- static QUntypedPropertyBinding Q_QML_PRIVATE_EXPORT create(const QQmlPropertyData *pd,
+ static QUntypedPropertyBinding Q_QML_EXPORT create(const QQmlPropertyData *pd,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
const QV4::CompiledData::Binding *binding);
- static QUntypedPropertyBinding Q_QML_PRIVATE_EXPORT
+ static QUntypedPropertyBinding Q_QML_EXPORT
create(const QMetaType &pd,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
const QQmlTranslation &translationData);
@@ -286,19 +286,27 @@ bool QQmlPropertyBinding::evaluate(QMetaType metaType, void *dataPtr)
if (!hasBoundFunction()) {
Q_ASSERT(metaType.sizeOf() > 0);
- // No need to construct here. evaluate() expects uninitialized memory.
- const auto size = [&]() -> qsizetype {
+ using Tuple = std::tuple<qsizetype, bool, bool>;
+ const auto [size, needsConstruction, needsDestruction] = [&]() -> Tuple {
switch (type) {
- case QMetaType::QObjectStar: return sizeof(QObject *);
- case QMetaType::Bool: return sizeof(bool);
- case QMetaType::Int: return (sizeof(int));
- case QMetaType::Double: return (sizeof(double));
- case QMetaType::Float: return (sizeof(float));
- case QMetaType::QString: return (sizeof(QString));
- default: return metaType.sizeOf();
+ case QMetaType::QObjectStar: return Tuple(sizeof(QObject *), false, false);
+ case QMetaType::Bool: return Tuple(sizeof(bool), false, false);
+ case QMetaType::Int: return Tuple(sizeof(int), false, false);
+ case QMetaType::Double: return Tuple(sizeof(double), false, false);
+ case QMetaType::Float: return Tuple(sizeof(float), false, false);
+ case QMetaType::QString: return Tuple(sizeof(QString), true, true);
+ default: {
+ const auto flags = metaType.flags();
+ return Tuple(
+ metaType.sizeOf(),
+ flags & QMetaType::NeedsConstruction,
+ flags & QMetaType::NeedsDestruction);
+ }
}
}();
Q_ALLOCA_VAR(void, result, size);
+ if (needsConstruction)
+ metaType.construct(result);
const bool evaluatedToUndefined = !jsExpression()->evaluate(&result, &metaType, 0);
if (!handleErrorAndUndefined(evaluatedToUndefined))
@@ -326,10 +334,12 @@ bool QQmlPropertyBinding::evaluate(QMetaType metaType, void *dataPtr)
const bool hasChanged = !metaType.equals(result, dataPtr);
if (hasChanged) {
- metaType.destruct(dataPtr);
+ if (needsDestruction)
+ metaType.destruct(dataPtr);
metaType.construct(dataPtr, result);
}
- metaType.destruct(result);
+ if (needsDestruction)
+ metaType.destruct(result);
return hasChanged;
}
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 4f14009cd1..805113ad97 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -10,6 +10,7 @@
#include <private/qmetaobject_p.h>
#include <private/qmetaobjectbuilder_p.h>
#include <private/qqmlpropertycachemethodarguments_p.h>
+#include <private/qqmlsignalnames_p.h>
#include <private/qv4value_p.h>
@@ -53,17 +54,13 @@ QQmlPropertyData::flagsForProperty(const QMetaProperty &p)
const QMetaType metaType = p.metaType();
int propType = metaType.id();
if (p.isEnumType()) {
- flags.type = QQmlPropertyData::Flags::EnumType;
+ flags.setType(QQmlPropertyData::Flags::EnumType);
} else if (metaType.flags() & QMetaType::PointerToQObject) {
- flags.type = QQmlPropertyData::Flags::QObjectDerivedType;
+ flags.setType(QQmlPropertyData::Flags::QObjectDerivedType);
} else if (propType == QMetaType::QVariant) {
- flags.type = QQmlPropertyData::Flags::QVariantType;
- } else if (propType < static_cast<int>(QMetaType::User)) {
- // nothing to do
- } else if (propType == qMetaTypeId<QJSValue>()) {
- flags.type = QQmlPropertyData::Flags::QJSValueType;
+ flags.setType(QQmlPropertyData::Flags::QVariantType);
} else if (metaType.flags() & QMetaType::IsQmlList) {
- flags.type = QQmlPropertyData::Flags::QListType;
+ flags.setType(QQmlPropertyData::Flags::QListType);
}
return flags;
@@ -83,28 +80,43 @@ void QQmlPropertyData::load(const QMetaProperty &p)
void QQmlPropertyData::load(const QMetaMethod &m)
{
setCoreIndex(m.methodIndex());
- setArguments(nullptr);
+ m_flags.setType(Flags::FunctionType);
- setPropType(m.returnMetaType());
+ // We need to set the constructor, signal, constant, arguments, V4Function, cloned flags.
+ // These are specific to methods and change with each method.
+ // The same QQmlPropertyData may be loaded with multiple methods in sequence.
- m_flags.type = Flags::FunctionType;
- if (m.methodType() == QMetaMethod::Signal) {
+ switch (m.methodType()) {
+ case QMetaMethod::Signal:
m_flags.setIsSignal(true);
- } else if (m.methodType() == QMetaMethod::Constructor) {
+ m_flags.setIsConstructor(false);
+ setPropType(m.returnMetaType());
+ break;
+ case QMetaMethod::Constructor:
+ m_flags.setIsSignal(false);
m_flags.setIsConstructor(true);
- setPropType(QMetaType::fromType<QObject *>());
+ break;
+ default:
+ m_flags.setIsSignal(false);
+ m_flags.setIsConstructor(false);
+ setPropType(m.returnMetaType());
+ break;
}
+
m_flags.setIsConstant(m.isConst());
const int paramCount = m.parameterCount();
if (paramCount) {
m_flags.setHasArguments(true);
- if ((paramCount == 1) && (m.parameterMetaType(0) == QMetaType::fromType<QQmlV4Function *>()))
- m_flags.setIsV4Function(true);
+ m_flags.setIsV4Function(
+ paramCount == 1 &&
+ m.parameterMetaType(0) == QMetaType::fromType<QQmlV4FunctionPtr>());
+ } else {
+ m_flags.setHasArguments(false);
+ m_flags.setIsV4Function(false);
}
- if (m.attributes() & QMetaMethod::Cloned)
- m_flags.setIsCloned(true);
+ m_flags.setIsCloned(m.attributes() & QMetaMethod::Cloned);
Q_ASSERT(m.revision() <= std::numeric_limits<quint16>::max());
setRevision(QTypeRevision::fromEncodedVersion(m.revision()));
@@ -249,8 +261,7 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag
int signalHandlerIndex = signalHandlerIndexCache.size();
signalHandlerIndexCache.append(handler);
- QString handlerName = QLatin1String("on") + name;
- handlerName[2] = handlerName.at(2).toUpper();
+ const QString handlerName = QQmlSignalNames::signalNameToHandlerName(name);
setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex);
setNamedProperty(handlerName, signalHandlerIndex + signalOffset(),
@@ -352,6 +363,18 @@ QQmlPropertyCache::copyAndAppend(const QMetaObject *metaObject,
return rv;
}
+static QHashedString signalNameToHandlerName(const QHashedString &methodName)
+{
+ return QQmlSignalNames::signalNameToHandlerName(methodName);
+}
+
+static QHashedString signalNameToHandlerName(const QHashedCStringRef &methodName)
+{
+ return QQmlSignalNames::signalNameToHandlerName(
+ QLatin1StringView{ methodName.constData(), methodName.length() });
+}
+
+
void QQmlPropertyCache::append(const QMetaObject *metaObject,
QTypeRevision typeVersion,
QQmlPropertyData::Flags propertyFlags,
@@ -440,49 +463,29 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
QQmlPropertyData *old = nullptr;
- if (utf8) {
- QHashedString methodName(QString::fromUtf8(rawName, cptr - rawName));
+ const auto doSetNamedProperty = [&](const auto &methodName) {
if (StringCache::mapped_type *it = stringCache.value(methodName)) {
if (handleOverride(methodName, data, (old = it->second)) == InvalidOverride)
- continue;
+ return;
}
- setNamedProperty(methodName, ii, data);
- if (data->isSignal()) {
- QHashedString on(QLatin1String("on") % methodName.at(0).toUpper() % QStringView{methodName}.mid(1));
- setNamedProperty(on, ii, sigdata);
- ++signalHandlerIndex;
- }
- } else {
- QHashedCStringRef methodName(rawName, cptr - rawName);
- if (StringCache::mapped_type *it = stringCache.value(methodName)) {
- if (handleOverride(methodName, data, (old = it->second)) == InvalidOverride)
- continue;
- }
setNamedProperty(methodName, ii, data);
if (data->isSignal()) {
- int length = methodName.length();
-
- QVarLengthArray<char, 128> str(length+3);
- str[0] = 'o';
- str[1] = 'n';
- str[2] = QtMiscUtils::toAsciiUpper(rawName[0]);
- if (length > 1)
- memcpy(&str[3], &rawName[1], length - 1);
- str[length + 2] = '\0';
-
- QHashedString on(QString::fromLatin1(str.data()));
- setNamedProperty(on, ii, data);
+
+ // TODO: Remove this once we can. Signals should not be overridable.
+ if (!utf8)
+ data->m_flags.setIsOverridableSignal(true);
+
+ setNamedProperty(signalNameToHandlerName(methodName), ii, sigdata);
++signalHandlerIndex;
}
- }
+ };
- if (old) {
- // We only overload methods in the same class, exactly like C++
- if (old->isFunction() && old->coreIndex() >= methodOffset)
- data->m_flags.setIsOverload(true);
- }
+ if (utf8)
+ doSetNamedProperty(QHashedString(QString::fromUtf8(rawName, cptr - rawName)));
+ else
+ doSetNamedProperty(QHashedCStringRef(rawName, cptr - rawName));
}
int propCount = metaObject->propertyCount();
@@ -683,28 +686,9 @@ const QQmlPropertyData *QQmlPropertyCache::findProperty(
return nullptr;
}
-QString QQmlPropertyData::name(QObject *object) const
-{
- if (!object)
- return QString();
-
- return name(object->metaObject());
-}
-QString QQmlPropertyData::name(const QMetaObject *metaObject) const
-{
- if (!metaObject || coreIndex() == -1)
- return QString();
- if (isFunction()) {
- QMetaMethod m = metaObject->method(coreIndex());
- return QString::fromUtf8(m.name().constData());
- } else {
- QMetaProperty p = metaObject->property(coreIndex());
- return QString::fromUtf8(p.name());
- }
-}
bool QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor)
{
@@ -1186,7 +1170,7 @@ int countMetaObjectFields(const QMetaObject &mo, StringVisitor stringVisitor)
} // anonymous namespace
-static_assert(QMetaObjectPrivate::OutputRevision == 11, "Check and adjust determineMetaObjectSizes");
+static_assert(QMetaObjectPrivate::OutputRevision == 12, "Check and adjust determineMetaObjectSizes");
bool QQmlPropertyCache::determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount,
int *stringCount)
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index eb88cc43a2..7ff499460d 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -40,9 +40,9 @@ class QQmlVMEMetaObject;
class QQmlMetaObjectPointer
{
public:
- QQmlMetaObjectPointer() = default;
+ Q_NODISCARD_CTOR QQmlMetaObjectPointer() = default;
- QQmlMetaObjectPointer(const QMetaObject *staticMetaObject)
+ Q_NODISCARD_CTOR QQmlMetaObjectPointer(const QMetaObject *staticMetaObject)
: d(quintptr(staticMetaObject))
{
Q_ASSERT((d.loadRelaxed() & Shared) == 0);
@@ -57,7 +57,7 @@ public:
private:
friend class QQmlPropertyCache;
- QQmlMetaObjectPointer(const QQmlMetaObjectPointer &other)
+ Q_NODISCARD_CTOR QQmlMetaObjectPointer(const QQmlMetaObjectPointer &other)
: d(other.d.loadRelaxed())
{
// other has to survive until this ctor is done. So d cannot disappear before.
@@ -105,7 +105,7 @@ private:
Shared = 1
};
- struct SharedHolder : public QQmlRefCount
+ struct SharedHolder final : public QQmlRefCounted<SharedHolder>
{
Q_DISABLE_COPY_MOVE(SharedHolder)
SharedHolder(QMetaObject *shared) : metaObject(shared) {}
@@ -116,7 +116,8 @@ private:
mutable QBasicAtomicInteger<quintptr> d = 0;
};
-class Q_QML_PRIVATE_EXPORT QQmlPropertyCache : public QQmlRefCount
+class Q_QML_EXPORT QQmlPropertyCache final
+ : public QQmlRefCounted<QQmlPropertyCache>
{
public:
using Ptr = QQmlRefPointer<QQmlPropertyCache>;
@@ -135,7 +136,7 @@ public:
const QMetaObject *, QTypeRevision metaObjectRevision = QTypeRevision::zero());
QQmlPropertyCache() = default;
- ~QQmlPropertyCache() override;
+ ~QQmlPropertyCache();
void update(const QMetaObject *);
void invalidate(const QMetaObject *);
@@ -232,7 +233,7 @@ private:
friend class QQmlCompiler;
template <typename T> friend class QQmlPropertyCacheCreator;
template <typename T> friend class QQmlPropertyCacheAliasCreator;
- friend class QQmlComponentAndAliasResolver;
+ template <typename T> friend class QQmlComponentAndAliasResolver;
friend class QQmlMetaObject;
QQmlPropertyCache(const QQmlMetaObjectPointer &metaObject) : _metaObject(metaObject) {}
diff --git a/src/qml/qml/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp
index 4bc903c22e..06b405c7e4 100644
--- a/src/qml/qml/qqmlpropertycachecreator.cpp
+++ b/src/qml/qml/qqmlpropertycachecreator.cpp
@@ -9,70 +9,54 @@ QT_BEGIN_NAMESPACE
QAtomicInt QQmlPropertyCacheCreatorBase::classIndexCounter(0);
-
-QMetaType QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(QV4::CompiledData::BuiltinType type)
+template<typename BaseNameHandler, typename FailHandler>
+auto processUrlForClassName(
+ const QUrl &url, BaseNameHandler &&baseNameHandler, FailHandler &&failHandler)
{
- switch (type) {
- case QV4::CompiledData::BuiltinType::Var: return QMetaType::fromType<QVariant>();
- case QV4::CompiledData::BuiltinType::Int: return QMetaType::fromType<int>();
- case QV4::CompiledData::BuiltinType::Bool: return QMetaType::fromType<bool>();
- case QV4::CompiledData::BuiltinType::Real: return QMetaType::fromType<qreal>();
- case QV4::CompiledData::BuiltinType::String: return QMetaType::fromType<QString>();
- case QV4::CompiledData::BuiltinType::Url: return QMetaType::fromType<QUrl>();
- case QV4::CompiledData::BuiltinType::Time: return QMetaType::fromType<QTime>();
- case QV4::CompiledData::BuiltinType::Date: return QMetaType::fromType<QDate>();
- case QV4::CompiledData::BuiltinType::DateTime: return QMetaType::fromType<QDateTime>();
- case QV4::CompiledData::BuiltinType::Rect: return QMetaType::fromType<QRectF>();
- case QV4::CompiledData::BuiltinType::Point: return QMetaType::fromType<QPointF>();
- case QV4::CompiledData::BuiltinType::Size: return QMetaType::fromType<QSizeF>();
- case QV4::CompiledData::BuiltinType::InvalidBuiltin: break;
- };
- return QMetaType {};
+ const QString path = url.path();
+
+ // Not a reusable type if we don't have an absolute Url
+ const qsizetype lastSlash = path.lastIndexOf(QLatin1Char('/'));
+ if (lastSlash <= -1)
+ return failHandler();
+
+ // ### this might not be correct for .ui.qml files
+ const QStringView baseName = QStringView{path}.mid(lastSlash + 1, path.size() - lastSlash - 5);
+
+ // Not a reusable type if it doesn't start with a upper case letter.
+ return (!baseName.isEmpty() && baseName.at(0).isUpper())
+ ? baseNameHandler(baseName)
+ : failHandler();
}
-QMetaType QQmlPropertyCacheCreatorBase::listTypeForPropertyType(QV4::CompiledData::BuiltinType type)
+bool QQmlPropertyCacheCreatorBase::canCreateClassNameTypeByUrl(const QUrl &url)
{
- switch (type) {
- case QV4::CompiledData::BuiltinType::Var: return QMetaType::fromType<QList<QVariant>>();
- case QV4::CompiledData::BuiltinType::Int: return QMetaType::fromType<QList<int>>();
- case QV4::CompiledData::BuiltinType::Bool: return QMetaType::fromType<QList<bool>>();
- case QV4::CompiledData::BuiltinType::Real: return QMetaType::fromType<QList<qreal>>();
- case QV4::CompiledData::BuiltinType::String: return QMetaType::fromType<QList<QString>>();
- case QV4::CompiledData::BuiltinType::Url: return QMetaType::fromType<QList<QUrl>>();
- case QV4::CompiledData::BuiltinType::Time: return QMetaType::fromType<QList<QTime>>();
- case QV4::CompiledData::BuiltinType::Date: return QMetaType::fromType<QList<QDate>>();
- case QV4::CompiledData::BuiltinType::DateTime: return QMetaType::fromType<QList<QDateTime>>();
- case QV4::CompiledData::BuiltinType::Rect: return QMetaType::fromType<QList<QRectF>>();
- case QV4::CompiledData::BuiltinType::Point: return QMetaType::fromType<QList<QPointF>>();
- case QV4::CompiledData::BuiltinType::Size: return QMetaType::fromType<QList<QSizeF>>();
- case QV4::CompiledData::BuiltinType::InvalidBuiltin: break;
- };
- return QMetaType {};
+ return processUrlForClassName(url, [](QStringView) {
+ return true;
+ }, []() {
+ return false;
+ });
}
QByteArray QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(const QUrl &url)
{
- const QString path = url.path();
- int lastSlash = path.lastIndexOf(QLatin1Char('/'));
- // Not a reusable type if we don't have an absolute Url
- if (lastSlash <= -1)
- return QByteArray();
- // ### this might not be correct for .ui.qml files
- const QStringView nameBase = QStringView{path}.mid(lastSlash + 1, path.size() - lastSlash - 5);
- // Not a reusable type if it doesn't start with a upper case letter.
- if (nameBase.isEmpty() || !nameBase.at(0).isUpper())
- return QByteArray();
- return nameBase.toUtf8() + "_QMLTYPE_" +
- QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
+ return processUrlForClassName(url, [](QStringView nameBase) {
+ return nameBase.toUtf8() + QByteArray("_QMLTYPE_");
+ }, []() {
+ return QByteArray("ANON_QML_TYPE_");
+ }) + QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
}
-QByteArray QQmlPropertyCacheCreatorBase::createClassNameForInlineComponent(const QUrl &baseUrl, int icId)
+QByteArray QQmlPropertyCacheCreatorBase::createClassNameForInlineComponent(
+ const QUrl &baseUrl, const QString &name)
{
- QByteArray baseName = createClassNameTypeByUrl(baseUrl);
- if (baseName.isEmpty())
- baseName = QByteArray("ANON_QML_IC_") + QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
- baseName += "_" + QByteArray::number(icId);
- return baseName;
+ QByteArray baseName = processUrlForClassName(baseUrl, [](QStringView nameBase) {
+ return QByteArray(nameBase.toUtf8() + "_QMLTYPE_");
+ }, []() {
+ return QByteArray("ANON_QML_IC_");
+ });
+ return baseName + name.toUtf8() + '_'
+ + QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
}
QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(
@@ -113,8 +97,19 @@ QQmlPropertyCache::ConstPtr QQmlBindingInstantiationContext::instantiatingProper
if (instantiatingProperty->isQObject()) {
// rawPropertyCacheForType assumes a given unspecified version means "any version".
// There is another overload that takes no version, which we shall not use here.
- return QQmlMetaType::rawPropertyCacheForType(instantiatingProperty->propType(),
+ auto result = QQmlMetaType::rawPropertyCacheForType(instantiatingProperty->propType(),
instantiatingProperty->typeVersion());
+ if (result)
+ return result;
+ /* We might end up here if there's a grouped property, and the type hasn't been registered.
+ Still try to get a property cache, as long as the type of the property is well-behaved
+ (i.e., not dynamic)*/
+ if (auto metaObject = instantiatingProperty->propType().metaObject(); metaObject) {
+ // we'll warn about dynamic meta-object later in the property validator
+ if (!(QMetaObjectPrivate::get(metaObject)->flags & DynamicMetaObject))
+ return QQmlMetaType::propertyCache(metaObject);
+ }
+ // fall through intentional
} else if (const QMetaObject *vtmo = QQmlMetaType::metaObjectForValueType(instantiatingProperty->propType())) {
return QQmlMetaType::propertyCache(vtmo, instantiatingProperty->typeVersion());
}
@@ -131,12 +126,7 @@ void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches(
if (propertyCaches->at(groupPropertyObjectIndex))
continue;
- if (pendingBinding.instantiatingPropertyName.isEmpty()) {
- // Generalized group property.
- auto cache = propertyCaches->at(pendingBinding.referencingObjectIndex);
- propertyCaches->set(groupPropertyObjectIndex, cache);
- continue;
- }
+ Q_ASSERT(!pendingBinding.instantiatingPropertyName.isEmpty());
if (!pendingBinding.referencingObjectPropertyCache) {
pendingBinding.referencingObjectPropertyCache
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
index e58d8ece1b..4d49ca6ed4 100644
--- a/src/qml/qml/qqmlpropertycachecreator_p.h
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -21,8 +21,14 @@
#include <private/qqmltypedata_p.h>
#include <private/inlinecomponentutils_p.h>
#include <private/qqmlsourcecoordinate_p.h>
+#include <private/qqmlsignalnames_p.h>
#include <QScopedValueRollback>
+
+#if QT_CONFIG(regularexpression)
+#include <QtCore/qregularexpression.h>
+#endif
+
#include <vector>
QT_BEGIN_NAMESPACE
@@ -66,12 +72,62 @@ struct QQmlPropertyCacheCreatorBase
public:
static QAtomicInt Q_AUTOTEST_EXPORT classIndexCounter;
- static QMetaType metaTypeForPropertyType(QV4::CompiledData::BuiltinType type);
- static QMetaType listTypeForPropertyType(QV4::CompiledData::BuiltinType type);
+ static QMetaType metaTypeForPropertyType(QV4::CompiledData::CommonType type)
+ {
+ switch (type) {
+ case QV4::CompiledData::CommonType::Void: return QMetaType();
+ case QV4::CompiledData::CommonType::Var: return QMetaType::fromType<QVariant>();
+ case QV4::CompiledData::CommonType::Int: return QMetaType::fromType<int>();
+ case QV4::CompiledData::CommonType::Bool: return QMetaType::fromType<bool>();
+ case QV4::CompiledData::CommonType::Real: return QMetaType::fromType<qreal>();
+ case QV4::CompiledData::CommonType::String: return QMetaType::fromType<QString>();
+ case QV4::CompiledData::CommonType::Url: return QMetaType::fromType<QUrl>();
+ case QV4::CompiledData::CommonType::Time: return QMetaType::fromType<QTime>();
+ case QV4::CompiledData::CommonType::Date: return QMetaType::fromType<QDate>();
+ case QV4::CompiledData::CommonType::DateTime: return QMetaType::fromType<QDateTime>();
+#if QT_CONFIG(regularexpression)
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType::fromType<QRegularExpression>();
+#else
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType();
+#endif
+ case QV4::CompiledData::CommonType::Rect: return QMetaType::fromType<QRectF>();
+ case QV4::CompiledData::CommonType::Point: return QMetaType::fromType<QPointF>();
+ case QV4::CompiledData::CommonType::Size: return QMetaType::fromType<QSizeF>();
+ case QV4::CompiledData::CommonType::Invalid: break;
+ };
+ return QMetaType {};
+ }
+
+ static QMetaType listTypeForPropertyType(QV4::CompiledData::CommonType type)
+ {
+ switch (type) {
+ case QV4::CompiledData::CommonType::Void: return QMetaType();
+ case QV4::CompiledData::CommonType::Var: return QMetaType::fromType<QList<QVariant>>();
+ case QV4::CompiledData::CommonType::Int: return QMetaType::fromType<QList<int>>();
+ case QV4::CompiledData::CommonType::Bool: return QMetaType::fromType<QList<bool>>();
+ case QV4::CompiledData::CommonType::Real: return QMetaType::fromType<QList<qreal>>();
+ case QV4::CompiledData::CommonType::String: return QMetaType::fromType<QList<QString>>();
+ case QV4::CompiledData::CommonType::Url: return QMetaType::fromType<QList<QUrl>>();
+ case QV4::CompiledData::CommonType::Time: return QMetaType::fromType<QList<QTime>>();
+ case QV4::CompiledData::CommonType::Date: return QMetaType::fromType<QList<QDate>>();
+ case QV4::CompiledData::CommonType::DateTime: return QMetaType::fromType<QList<QDateTime>>();
+#if QT_CONFIG(regularexpression)
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType::fromType<QList<QRegularExpression>>();
+#else
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType();
+#endif
+ case QV4::CompiledData::CommonType::Rect: return QMetaType::fromType<QList<QRectF>>();
+ case QV4::CompiledData::CommonType::Point: return QMetaType::fromType<QList<QPointF>>();
+ case QV4::CompiledData::CommonType::Size: return QMetaType::fromType<QList<QSizeF>>();
+ case QV4::CompiledData::CommonType::Invalid: break;
+ };
+ return QMetaType {};
+ }
+ static bool canCreateClassNameTypeByUrl(const QUrl &url);
static QByteArray createClassNameTypeByUrl(const QUrl &url);
- static QByteArray createClassNameForInlineComponent(const QUrl &baseUrl, int icId);
+ static QByteArray createClassNameForInlineComponent(const QUrl &baseUrl, const QString &name);
struct IncrementalResult {
// valid if and only if an error occurred
@@ -115,14 +171,6 @@ public:
*/
QQmlError verifyNoICCycle();
- /*!
- \internal
- Fills the property caches for the CompiledObjects by
- calling buildMetaObjectsIncrementally until it can no
- longer resume.
- */
- QQmlError buildMetaObjects();
-
enum class VMEMetaObjectIsRequired {
Maybe,
Always
@@ -165,7 +213,7 @@ inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlP
, typeClassName(typeClassName)
, currentRoot(-1)
{
- propertyCaches->resize(objectContainer->objectCount());
+ propertyCaches->resetAndResize(objectContainer->objectCount());
using namespace icutils;
@@ -179,7 +227,7 @@ inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlP
}
// create a graph on inline components referencing inline components
- std::vector<Node> nodes;
+ std::vector<icutils::Node> nodes;
nodes.resize(allICs.size());
std::iota(nodes.begin(), nodes.end(), 0);
AdjacencyList adjacencyList;
@@ -233,20 +281,6 @@ QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectsIncrementally()
}
template <typename ObjectContainer>
-inline QQmlError
-QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects()
-{
- QQmlError error = verifyNoICCycle();
- if (error.isValid())
- return error;
- QQmlPropertyCacheCreatorBase::IncrementalResult result;
- do {
- result = buildMetaObjectsIncrementally();
- } while (result.canResume);
- return result.error;
-}
-
-template <typename ObjectContainer>
inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired)
{
auto isAddressable = [](const QUrl &url) {
@@ -277,7 +311,10 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecur
auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
Q_ASSERT(typeRef);
QQmlPropertyCache::ConstPtr baseTypeCache = typeRef->createPropertyCache();
- QQmlError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache);
+ QQmlError error = baseTypeCache
+ ? createMetaObject(context.referencingObjectIndex, obj, baseTypeCache)
+ : qQmlCompileError(binding->location, QQmlPropertyCacheCreatorBase::tr(
+ "Type cannot be used for 'on' assignment"));
if (error.isValid())
return error;
}
@@ -367,7 +404,13 @@ inline QQmlPropertyCache::ConstPtr QQmlPropertyCacheCreator<ObjectContainer>::pr
}
}
- return typeRef->createPropertyCache();
+ if (QQmlPropertyCache::ConstPtr propertyCache = typeRef->createPropertyCache())
+ return propertyCache;
+ *error = qQmlCompileError(
+ obj->location,
+ QQmlPropertyCacheCreatorBase::tr("Type '%1' cannot declare new members.")
+ .arg(stringAt(obj->inheritedTypeNameIndex)));
+ return nullptr;
} else if (const QV4::CompiledData::Binding *binding = context.instantiatingBinding) {
if (binding->isAttachedProperty()) {
auto *typeRef = objectContainer->resolvedType(
@@ -375,8 +418,9 @@ inline QQmlPropertyCache::ConstPtr QQmlPropertyCacheCreator<ObjectContainer>::pr
Q_ASSERT(typeRef);
QQmlType qmltype = typeRef->type();
if (!qmltype.isValid()) {
- imports->resolveType(stringAt(binding->propertyNameIndex),
- &qmltype, nullptr, nullptr, nullptr);
+ imports->resolveType(
+ QQmlTypeLoader::get(enginePrivate), stringAt(binding->propertyNameIndex),
+ &qmltype, nullptr, nullptr);
}
const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate);
@@ -385,23 +429,6 @@ inline QQmlPropertyCache::ConstPtr QQmlPropertyCacheCreator<ObjectContainer>::pr
return nullptr;
}
return QQmlMetaType::propertyCache(attachedMo);
- } else if (binding->isGroupProperty()) {
- const auto *obj = objectContainer->objectAt(binding->value.objectIndex);
- if (!stringAt(obj->inheritedTypeNameIndex).isEmpty())
- return nullptr;
-
- for (int i = 0, end = objectContainer->objectCount(); i != end; ++i) {
- const auto *ext = objectContainer->objectAt(i);
- if (ext->idNameIndex != binding->propertyNameIndex)
- continue;
-
- if (ext->inheritedTypeNameIndex == 0)
- return nullptr;
-
- QQmlBindingInstantiationContext pendingContext(i, &(*binding), QString(), nullptr);
- pendingGroupPropertyBindings->append(pendingContext);
- return nullptr;
- }
}
}
return nullptr;
@@ -472,8 +499,13 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
// For property change signal override detection.
// We prepopulate a set of signal names which already exist in the object,
// and throw an error if there is a signal/method defined as an override.
- QSet<QString> seenSignals;
- seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged");
+ // TODO: Remove AllowOverride once we can. No override should be allowed.
+ enum class AllowOverride { No, Yes };
+ QHash<QString, AllowOverride> seenSignals {
+ { QStringLiteral("destroyed"), AllowOverride::No },
+ { QStringLiteral("parentChanged"), AllowOverride::No },
+ { QStringLiteral("objectNameChanged"), AllowOverride::No }
+ };
const QQmlPropertyCache *parentCache = cache.data();
while ((parentCache = parentCache->parent().data())) {
if (int pSigCount = parentCache->signalCount()) {
@@ -484,7 +516,15 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin();
iter != parentCache->stringCache.end(); ++iter) {
if (currPSig == (*iter).second) {
- seenSignals.insert(iter.key());
+ if (currPSig->isOverridableSignal()) {
+ const qsizetype oldSize = seenSignals.size();
+ AllowOverride &entry = seenSignals[iter.key()];
+ if (seenSignals.size() != oldSize)
+ entry = AllowOverride::Yes;
+ } else {
+ seenSignals[iter.key()] = AllowOverride::No;
+ }
+
break;
}
}
@@ -498,8 +538,9 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
for ( ; p != pend; ++p) {
auto flags = QQmlPropertyData::defaultSignalFlags();
- QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed");
- seenSignals.insert(changedSigName);
+ const QString changedSigName =
+ QQmlSignalNames::propertyNameToChangedSignalName(stringAt(p->nameIndex));
+ seenSignals[changedSigName] = AllowOverride::No;
cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
}
@@ -509,8 +550,9 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
for ( ; a != aend; ++a) {
auto flags = QQmlPropertyData::defaultSignalFlags();
- QString changedSigName = stringAt(a->nameIndex()) + QLatin1String("Changed");
- seenSignals.insert(changedSigName);
+ const QString changedSigName =
+ QQmlSignalNames::propertyNameToChangedSignalName(stringAt(a->nameIndex()));
+ seenSignals[changedSigName] = AllowOverride::No;
cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
}
@@ -562,10 +604,26 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
flags.setHasArguments(true);
QString signalName = stringAt(s->nameIndex);
- if (seenSignals.contains(signalName))
- return qQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal"));
- seenSignals.insert(signalName);
-
+ const auto it = seenSignals.find(signalName);
+ if (it == seenSignals.end()) {
+ seenSignals[signalName] = AllowOverride::No;
+ } else {
+ // TODO: Remove the AllowOverride::Yes branch once we can.
+ QQmlError message = qQmlCompileError(
+ s->location,
+ QQmlPropertyCacheCreatorBase::tr(
+ "Duplicate signal name: "
+ "invalid override of property change signal or superclass signal"));
+ switch (*it) {
+ case AllowOverride::No:
+ return message;
+ case AllowOverride::Yes:
+ message.setUrl(objectContainer->url());
+ enginePrivate->warning(message);
+ *it = AllowOverride::No; // No further overriding allowed.
+ break;
+ }
+ }
cache->appendSignal(signalName, flags, effectiveMethodIndex++,
paramCount?paramTypes.constData():nullptr, names);
}
@@ -578,8 +636,23 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
auto flags = QQmlPropertyData::defaultSlotFlags();
const QString slotName = stringAt(function->nameIndex);
- if (seenSignals.contains(slotName))
- return qQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal"));
+ const auto it = seenSignals.constFind(slotName);
+ if (it != seenSignals.constEnd()) {
+ // TODO: Remove the AllowOverride::Yes branch once we can.
+ QQmlError message = qQmlCompileError(
+ function->location,
+ QQmlPropertyCacheCreatorBase::tr(
+ "Duplicate method name: "
+ "invalid override of property change signal or superclass signal"));
+ switch (*it) {
+ case AllowOverride::No:
+ return message;
+ case AllowOverride::Yes:
+ message.setUrl(objectContainer->url());
+ enginePrivate->warning(message);
+ break;
+ }
+ }
// Note: we don't append slotName to the seenSignals list, since we don't
// protect against overriding change signals or methods with properties.
@@ -614,58 +687,53 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
QTypeRevision propertyTypeVersion = QTypeRevision::zero();
QQmlPropertyData::Flags propertyFlags;
- const QV4::CompiledData::BuiltinType type = p->builtinType();
+ const QV4::CompiledData::CommonType type = p->commonType();
if (p->isList())
- propertyFlags.type = QQmlPropertyData::Flags::QListType;
- else if (type == QV4::CompiledData::BuiltinType::Var)
- propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType;
+ propertyFlags.setType(QQmlPropertyData::Flags::QListType);
+ else if (type == QV4::CompiledData::CommonType::Var)
+ propertyFlags.setType(QQmlPropertyData::Flags::VarPropertyType);
- if (type != QV4::CompiledData::BuiltinType::InvalidBuiltin) {
+ if (type != QV4::CompiledData::CommonType::Invalid) {
propertyType = p->isList()
? listTypeForPropertyType(type)
: metaTypeForPropertyType(type);
} else {
- Q_ASSERT(!p->isBuiltinType());
+ Q_ASSERT(!p->isCommonType());
QQmlType qmltype;
bool selfReference = false;
- if (!imports->resolveType(stringAt(p->builtinTypeOrTypeNameIndex()), &qmltype, nullptr, nullptr,
- nullptr, QQmlType::AnyRegistrationType, &selfReference)) {
+ if (!imports->resolveType(
+ QQmlTypeLoader::get(enginePrivate),
+ stringAt(p->commonTypeOrTypeNameIndex()), &qmltype, nullptr, nullptr,
+ nullptr, QQmlType::AnyRegistrationType, &selfReference)) {
return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type"));
}
// inline components are not necessarily valid yet
- Q_ASSERT(qmltype.isValid() || qmltype.isInlineComponentType());
+ Q_ASSERT(qmltype.isValid());
if (qmltype.isComposite() || qmltype.isInlineComponentType()) {
- CompositeMetaTypeIds typeIds;
+ QQmlType compositeType;
if (qmltype.isInlineComponentType()) {
- auto objectId = qmltype.inlineComponentId();
- auto containingType = qmltype.containingType();
- if (containingType.isValid()) {
- auto icType = containingType.lookupInlineComponentById(objectId);
- typeIds = {icType.typeId(), icType.qListTypeId()};
- } else {
- typeIds = {};
- }
- if (!typeIds.isValid()) // type has not been registered yet, we must be in containing type
- typeIds = objectContainer->typeIdsForComponent(objectId);
- Q_ASSERT(typeIds.isValid());
+ compositeType = qmltype;
+ Q_ASSERT(compositeType.isValid());
} else if (selfReference) {
- typeIds = objectContainer->typeIdsForComponent();
+ compositeType = objectContainer->qmlTypeForComponent();
} else {
- QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ // compositeType may not be the same type as qmlType because multiple engines
+ // may load different types for the same document. Therefore we have to ask
+ // our engine's type loader here.
+ QQmlRefPointer<QQmlTypeData> tdata
+ = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
-
- auto compilationUnit = tdata->compilationUnit();
- typeIds = compilationUnit->typeIdsForComponent();
+ compositeType = tdata->compilationUnit()->qmlTypeForComponent();
}
if (p->isList()) {
- propertyType = typeIds.listId;
+ propertyType = compositeType.qListTypeId();
} else {
- propertyType = typeIds.id;
+ propertyType = compositeType.typeId();
}
} else {
if (p->isList())
@@ -676,11 +744,9 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
}
if (p->isList())
- propertyFlags.type = QQmlPropertyData::Flags::QListType;
+ propertyFlags.setType(QQmlPropertyData::Flags::QListType);
else if (propertyType.flags().testFlag(QMetaType::PointerToQObject))
- propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType;
- else
- propertyFlags.type = QQmlPropertyData::Flags::ValueType;
+ propertyFlags.setType(QQmlPropertyData::Flags::QObjectDerivedType);
}
if (!p->isReadOnly() && !propertyType.flags().testFlag(QMetaType::IsQmlList))
@@ -701,50 +767,56 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
}
template <typename ObjectContainer>
-inline QMetaType QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter(const QV4::CompiledData::ParameterType &param,
- QString *customTypeName)
+inline QMetaType QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter(
+ const QV4::CompiledData::ParameterType &param, QString *customTypeName)
{
- const quint32 typeId = param.typeNameIndexOrBuiltinType();
- if (param.indexIsBuiltinType()) {
+ const quint32 typeId = param.typeNameIndexOrCommonType();
+ if (param.indexIsCommonType()) {
// built-in type
if (param.isList())
- return listTypeForPropertyType(QV4::CompiledData::BuiltinType(typeId));
- return metaTypeForPropertyType(QV4::CompiledData::BuiltinType(typeId));
+ return listTypeForPropertyType(QV4::CompiledData::CommonType(typeId));
+ return metaTypeForPropertyType(QV4::CompiledData::CommonType(typeId));
}
// lazily resolved type
- const QString typeName = stringAt(param.typeNameIndexOrBuiltinType());
+ const QString typeName = stringAt(param.typeNameIndexOrCommonType());
if (customTypeName)
*customTypeName = typeName;
QQmlType qmltype;
bool selfReference = false;
- if (!imports->resolveType(typeName, &qmltype, nullptr, nullptr, nullptr,
- QQmlType::AnyRegistrationType, &selfReference))
+ if (!imports->resolveType(
+ &enginePrivate->typeLoader, typeName, &qmltype, nullptr, nullptr, nullptr,
+ QQmlType::AnyRegistrationType, &selfReference))
return QMetaType();
if (!qmltype.isComposite()) {
const QMetaType typeId = param.isList() ? qmltype.qListTypeId() : qmltype.typeId();
if (!typeId.isValid() && qmltype.isInlineComponentType()) {
- const int objectId = qmltype.inlineComponentId();
- const auto typeIds = objectContainer->typeIdsForComponent(objectId);
- return param.isList() ? typeIds.listId : typeIds.id;
+ const QQmlType qmlType = objectContainer->qmlTypeForComponent(qmltype.elementName());
+ return param.isList() ? qmlType.qListTypeId() : qmlType.typeId();
} else {
return typeId;
}
}
if (selfReference) {
- const auto typeIds = objectContainer->typeIdsForComponent();
- return param.isList() ? typeIds.listId : typeIds.id;
+ const QQmlType qmlType = objectContainer->qmlTypeForComponent();
+ return param.isList() ? qmlType.qListTypeId() : qmlType.typeId();
}
- QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
- Q_ASSERT(tdata);
- Q_ASSERT(tdata->isComplete());
-
- auto compilationUnit = tdata->compilationUnit();
+ return param.isList() ? qmltype.qListTypeId() : qmltype.typeId();
+}
- return param.isList() ? compilationUnit->typeIds.listId : compilationUnit->typeIds.id;
+template <typename ObjectContainer, typename CompiledObject>
+int objectForId(const ObjectContainer *objectContainer, const CompiledObject &component, int id)
+{
+ for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
+ const int candidateIndex = component.namedObjectsInComponentTable()[i];
+ const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex);
+ if (candidate.objectId() == id)
+ return candidateIndex;
+ }
+ return -1;
}
template <typename ObjectContainer>
@@ -753,126 +825,27 @@ class QQmlPropertyCacheAliasCreator
public:
typedef typename ObjectContainer::CompiledObject CompiledObject;
- QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
-
- void appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv);
-
- QQmlError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv);
+ QQmlPropertyCacheAliasCreator(
+ QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
+ QQmlError appendAliasesToPropertyCache(
+ const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv);
private:
- void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv);
- QQmlError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, QMetaType *type, QTypeRevision *version, QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv);
-
- void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const;
-
- int objectForId(const CompiledObject &component, int id) const;
+ QQmlError propertyDataForAlias(
+ const CompiledObject &component, const QV4::CompiledData::Alias &alias, QMetaType *type,
+ QTypeRevision *version, QQmlPropertyData::Flags *propertyFlags,
+ QQmlEnginePrivate *enginePriv);
QQmlPropertyCacheVector *propertyCaches;
const ObjectContainer *objectContainer;
};
template <typename ObjectContainer>
-inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer)
+inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(
+ QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer)
: propertyCaches(propertyCaches)
, objectContainer(objectContainer)
{
-
-}
-
-template <typename ObjectContainer>
-inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv)
-{
- // skip the root object (index 0) as that one does not have a first object index originating
- // from a binding.
- for (int i = 1; i < objectContainer->objectCount(); ++i) {
- const CompiledObject &component = *objectContainer->objectAt(i);
- if (!component.hasFlag(QV4::CompiledData::Object::IsComponent))
- continue;
-
- const auto rootBinding = component.bindingsBegin();
- appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex, enginePriv);
- }
-
- const int rootObjectIndex = 0;
- appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex, enginePriv);
-}
-
-template <typename ObjectContainer>
-inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv)
-{
- QVector<int> objectsWithAliases;
- collectObjectsWithAliasesRecursively(firstObjectIndex, &objectsWithAliases);
- if (objectsWithAliases.isEmpty())
- return;
-
- const auto allAliasTargetsExist = [this, &component](const CompiledObject &object) {
- auto alias = object.aliasesBegin();
- auto end = object.aliasesEnd();
- for ( ; alias != end; ++alias) {
- Q_ASSERT(alias->hasFlag(QV4::CompiledData::Alias::Resolved));
-
- const int targetObjectIndex = objectForId(component, alias->targetObjectId());
- Q_ASSERT(targetObjectIndex >= 0);
-
- if (alias->isAliasToLocalAlias())
- continue;
-
- if (alias->encodedMetaPropertyIndex == -1)
- continue;
-
- const QQmlPropertyCache::ConstPtr targetCache
- = propertyCaches->at(targetObjectIndex);
- Q_ASSERT(targetCache);
-
- int coreIndex = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex();
- const QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
- if (!targetProperty)
- return false;
- }
- return true;
- };
-
- do {
- QVector<int> pendingObjects;
-
- for (int objectIndex: std::as_const(objectsWithAliases)) {
- const CompiledObject &object = *objectContainer->objectAt(objectIndex);
-
- if (allAliasTargetsExist(object)) {
- appendAliasesToPropertyCache(component, objectIndex, enginePriv);
- } else {
- pendingObjects.append(objectIndex);
- }
-
- }
- objectsWithAliases = std::move(pendingObjects);
- } while (!objectsWithAliases.isEmpty());
-}
-
-template <typename ObjectContainer>
-inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const
-{
- const CompiledObject &object = *objectContainer->objectAt(objectIndex);
- if (object.aliasCount() > 0)
- objectsWithAliases->append(objectIndex);
-
- // Stop at Component boundary
- if (object.hasFlag(QV4::CompiledData::Object::IsComponent) && objectIndex != /*root object*/0)
- return;
-
- auto binding = object.bindingsBegin();
- auto end = object.bindingsEnd();
- for (; binding != end; ++binding) {
- switch (binding->type()) {
- case QV4::CompiledData::Binding::Type_Object:
- case QV4::CompiledData::Binding::Type_AttachedProperty:
- case QV4::CompiledData::Binding::Type_GroupProperty:
- collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases);
- break;
- default:
- break;
- }
- }
}
template <typename ObjectContainer>
@@ -893,7 +866,8 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias});
do {
- const int targetObjectIndex = objectForId(component, lastAlias->targetObjectId());
+ const int targetObjectIndex = objectForId(
+ objectContainer, component, lastAlias->targetObjectId());
Q_ASSERT(targetObjectIndex >= 0);
const CompiledObject *targetObject = objectContainer->objectAt(targetObjectIndex);
Q_ASSERT(targetObject);
@@ -912,10 +886,11 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
lastAlias = targetAlias;
} while (lastAlias->isAliasToLocalAlias());
- return propertyDataForAlias(component, *lastAlias, type, version, propertyFlags, enginePriv);
+ return propertyDataForAlias(
+ component, *lastAlias, type, version, propertyFlags, enginePriv);
}
- const int targetObjectIndex = objectForId(component, alias.targetObjectId());
+ const int targetObjectIndex = objectForId(objectContainer, component, alias.targetObjectId());
Q_ASSERT(targetObjectIndex >= 0);
const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
@@ -934,20 +909,20 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
if (referencedType.isValid()) {
*type = referencedType.typeId();
if (!type->isValid() && referencedType.isInlineComponentType()) {
- int objectId = referencedType.inlineComponentId();
- *type = objectContainer->typeIdsForComponent(objectId).id;
+ *type = objectContainer->qmlTypeForComponent(referencedType.elementName()).typeId();
Q_ASSERT(type->isValid());
}
} else {
- *type = typeRef->compilationUnit()->typeIds.id;
+ *type = typeRef->compilationUnit()->metaType();
}
*version = typeRef->version();
- propertyFlags->type = QQmlPropertyData::Flags::QObjectDerivedType;
+ propertyFlags->setType(QQmlPropertyData::Flags::QObjectDerivedType);
} else {
int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex();
- int valueTypeIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).valueTypeIndex();
+ int valueTypeIndex = QQmlPropertyIndex::fromEncoded(
+ alias.encodedMetaPropertyIndex).valueTypeIndex();
QQmlPropertyCache::ConstPtr targetCache = propertyCaches->at(targetObjectIndex);
Q_ASSERT(targetCache);
@@ -955,53 +930,67 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
const QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
Q_ASSERT(targetProperty);
+ const QMetaType targetPropType = targetProperty->propType();
+
+ const auto populateWithPropertyData = [&](const QQmlPropertyData *property) {
+ *type = property->propType();
+ writable = property->isWritable();
+ resettable = property->isResettable();
+ bindable = property->isBindable();
+
+ if (property->isVarProperty())
+ propertyFlags->setType(QQmlPropertyData::Flags::QVariantType);
+ else
+ propertyFlags->copyPropertyTypeFlags(property->flags());
+ };
+
// for deep aliases, valueTypeIndex is always set
- if (!QQmlMetaType::isValueType(targetProperty->propType()) && valueTypeIndex != -1) {
+ if (!QQmlMetaType::isValueType(targetPropType) && valueTypeIndex != -1) {
// deep alias property
- *type = targetProperty->propType();
- QQmlPropertyCache::ConstPtr typeCache = QQmlMetaType::propertyCacheForType(*type);
- Q_ASSERT(typeCache);
- const QQmlPropertyData *typeProperty = typeCache->property(valueTypeIndex);
- if (typeProperty == nullptr) {
- return qQmlCompileError(alias.referenceLocation,
- QQmlPropertyCacheCreatorBase::tr("Invalid alias target"));
+ QQmlPropertyCache::ConstPtr typeCache
+ = QQmlMetaType::propertyCacheForType(targetPropType);
+
+ if (!typeCache) {
+ // See if it's a half-resolved composite type
+ if (const QV4::ResolvedTypeReference *typeRef
+ = objectContainer->resolvedType(targetPropType)) {
+ typeCache = typeRef->typePropertyCache();
+ }
}
- *type = typeProperty->propType();
- writable = typeProperty->isWritable();
- resettable = typeProperty->isResettable();
- bindable = typeProperty->isBindable();
+ const QQmlPropertyData *typeProperty = typeCache
+ ? typeCache->property(valueTypeIndex)
+ : nullptr;
+ if (typeProperty == nullptr) {
+ return qQmlCompileError(
+ alias.referenceLocation,
+ QQmlPropertyCacheCreatorBase::tr("Invalid alias target"));
+ }
+ populateWithPropertyData(typeProperty);
} else {
// value type or primitive type or enum
- *type = targetProperty->propType();
-
- writable = targetProperty->isWritable();
- resettable = targetProperty->isResettable();
- bindable = targetProperty->isBindable();
+ populateWithPropertyData(targetProperty);
if (valueTypeIndex != -1) {
- const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForValueType(*type);
- if (valueTypeMetaObject->property(valueTypeIndex).isEnumType())
- *type = QMetaType::fromType<int>();
- else
- *type = valueTypeMetaObject->property(valueTypeIndex).metaType();
- } else {
- if (targetProperty->isEnum()) {
- *type = QMetaType::fromType<int>();
- } else {
- // Copy type flags
- propertyFlags->copyPropertyTypeFlags(targetProperty->flags());
-
- if (targetProperty->isVarProperty())
- propertyFlags->type = QQmlPropertyData::Flags::QVariantType;
- }
+ const QMetaObject *valueTypeMetaObject
+ = QQmlMetaType::metaObjectForValueType(*type);
+ const QMetaProperty valueTypeMetaProperty
+ = valueTypeMetaObject->property(valueTypeIndex);
+ *type = valueTypeMetaProperty.metaType();
+
+ // We can only write or reset the value type property if we can write
+ // the value type itself.
+ resettable = writable && valueTypeMetaProperty.isResettable();
+ writable = writable && valueTypeMetaProperty.isWritable();
+
+ bindable = valueTypeMetaProperty.isBindable();
}
}
}
- propertyFlags->setIsWritable(!(alias.hasFlag(QV4::CompiledData::Alias::IsReadOnly))
- && writable);
+ propertyFlags->setIsWritable(
+ writable && !alias.hasFlag(QV4::CompiledData::Alias::IsReadOnly));
propertyFlags->setIsResettable(resettable);
propertyFlags->setIsBindable(bindable);
return QQmlError();
@@ -1047,18 +1036,6 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesTo
return QQmlError();
}
-template <typename ObjectContainer>
-inline int QQmlPropertyCacheAliasCreator<ObjectContainer>::objectForId(const CompiledObject &component, int id) const
-{
- for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
- const int candidateIndex = component.namedObjectsInComponentTable()[i];
- const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex);
- if (candidate.objectId() == id)
- return candidateIndex;
- }
- return -1;
-}
-
QT_END_NAMESPACE
#endif // QQMLPROPERTYCACHECREATOR_P_H
diff --git a/src/qml/qml/qqmlpropertycachevector_p.h b/src/qml/qml/qqmlpropertycachevector_p.h
index a208b3fa6e..1cee914220 100644
--- a/src/qml/qml/qqmlpropertycachevector_p.h
+++ b/src/qml/qml/qqmlpropertycachevector_p.h
@@ -42,28 +42,30 @@ public:
}
void clear()
{
+ for (int i = 0; i < data.size(); ++i)
+ releaseElement(i);
+ data.clear();
+ }
+
+ void resetAndResize(int size)
+ {
for (int i = 0; i < data.size(); ++i) {
- const auto &cache = data.at(i);
- if (cache.isT2()) {
- if (QQmlPropertyCache *data = cache.asT2())
- data->release();
- } else if (const QQmlPropertyCache *data = cache.asT1()) {
- data->release();
- }
+ releaseElement(i);
+ data[i] = BiPointer();
}
- data.clear();
+ data.resize(size);
}
void append(const QQmlPropertyCache::ConstPtr &cache) {
cache->addref();
- data.append(QBiPointer<const QQmlPropertyCache, QQmlPropertyCache>(cache.data()));
+ data.append(BiPointer(cache.data()));
Q_ASSERT(data.last().isT1());
Q_ASSERT(data.size() <= std::numeric_limits<int>::max());
}
void appendOwn(const QQmlPropertyCache::Ptr &cache) {
cache->addref();
- data.append(QBiPointer<const QQmlPropertyCache, QQmlPropertyCache>(cache.data()));
+ data.append(BiPointer(cache.data()));
Q_ASSERT(data.last().isT2());
Q_ASSERT(data.size() <= std::numeric_limits<int>::max());
}
@@ -122,8 +124,20 @@ public:
}
private:
+ void releaseElement(int i)
+ {
+ const auto &cache = data.at(i);
+ if (cache.isT2()) {
+ if (QQmlPropertyCache *data = cache.asT2())
+ data->release();
+ } else if (const QQmlPropertyCache *data = cache.asT1()) {
+ data->release();
+ }
+ }
+
Q_DISABLE_COPY(QQmlPropertyCacheVector)
- QVector<QBiPointer<const QQmlPropertyCache, QQmlPropertyCache>> data;
+ using BiPointer = QBiPointer<const QQmlPropertyCache, QQmlPropertyCache>;
+ QVector<BiPointer> data;
};
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlpropertydata_p.h b/src/qml/qml/qqmlpropertydata_p.h
index 19a045107d..bdfa41ab7a 100644
--- a/src/qml/qml/qqmlpropertydata_p.h
+++ b/src/qml/qml/qqmlpropertydata_p.h
@@ -37,20 +37,18 @@ public:
struct Flags {
friend class QQmlPropertyData;
- enum Types {
+ enum Type {
OtherType = 0,
FunctionType = 1, // Is an invokable
QObjectDerivedType = 2, // Property type is a QObject* derived type
EnumType = 3, // Property type is an enum
QListType = 4, // Property type is a QML list
- /*QmlBindingType = 5; was: Property type is a QQmlBinding*; now unused */
- QJSValueType = 6, // Property type is a QScriptValue
- // Gap, used to be V4HandleType
- VarPropertyType = 8, // Property type is a "var" property of VMEMO
- QVariantType = 9, // Property is a QVariant
- ValueType = 10 // Property type is a custom value type
+ VarPropertyType = 5, // Property type is a "var" property of VMEMO
+ QVariantType = 6, // Property is a QVariant
+ // One spot left for an extra type in the 3 bits used to store this.
};
+ private:
// The _otherBits (which "pad" the Flags struct to align it nicely) are used
// to store the relative property index. It will only get used when said index fits. See
// trySetStaticMetaCallFunction for details.
@@ -63,31 +61,32 @@ public:
// b when type equals FunctionType. For that reason, the semantic meaning of the bit is
// overloaded, and the accessor functions are used to get the correct value
//
- // Moreover, isSignalHandler, isOverload and isCloned make only sense
+ // Moreover, isSignalHandler, isOverridableSignal and isCloned make only sense
// for functions, too (and could at a later point be reused for flags that only make sense
// for non-functions)
//
// Lastly, isDirect and isOverridden apply to both functions and non-functions
- private:
unsigned isConst : 1; // Property: has CONST flag/Method: is const
unsigned isVMEFunction : 1; // Function was added by QML
unsigned isWritableORhasArguments : 1; // Has WRITE function OR Function takes arguments
unsigned isResettableORisSignal : 1; // Has RESET function OR Function is a signal
unsigned isAliasORisVMESignal : 1; // Is a QML alias to another property OR Signal was added by QML
- unsigned isFinalORisV4Function : 1; // Has FINAL flag OR Function takes QQmlV4Function* args
+ unsigned isFinalORisV4Function : 1; // Has FINAL flag OR Function takes QQmlV4FunctionPtr args
unsigned isSignalHandler : 1; // Function is a signal handler
- unsigned isOverload : 1; // Function is an overload of another function
+
+ // TODO: Remove this once we can. Signals should not be overridable.
+ unsigned isOverridableSignal : 1; // Function is an overridable signal
+
unsigned isRequiredORisCloned : 1; // Has REQUIRED flag OR The function was marked as cloned
unsigned isConstructorORisBindable : 1; // The function was marked is a constructor OR property is backed by QProperty<T>
unsigned isOverridden : 1; // Is overridden by a extension property
- public:
- unsigned type : 4; // stores an entry of Types
-
- // Apply only to IsFunctions
+ unsigned hasMetaObject : 1;
+ unsigned type : 3; // stores an entry of Types
// Internal QQmlPropertyCache flags
- unsigned overrideIndexIsProperty: 1;
+ unsigned overrideIndexIsProperty : 1;
+ public:
inline Flags();
inline bool operator==(const Flags &other) const;
inline void copyPropertyTypeFlags(Flags from);
@@ -157,9 +156,11 @@ public:
isSignalHandler = b;
}
- void setIsOverload(bool b) {
+ // TODO: Remove this once we can. Signals should not be overridable.
+ void setIsOverridableSignal(bool b) {
Q_ASSERT(type == FunctionType);
- isOverload = b;
+ Q_ASSERT(isResettableORisSignal);
+ isOverridableSignal = b;
}
void setIsCloned(bool b) {
@@ -172,6 +173,13 @@ public:
isConstructorORisBindable = b;
}
+ void setHasMetaObject(bool b) {
+ hasMetaObject = b;
+ }
+
+ void setType(Type newType) {
+ type = newType;
+ }
};
@@ -200,7 +208,6 @@ public:
bool isQObject() const { return m_flags.type == Flags::QObjectDerivedType; }
bool isEnum() const { return m_flags.type == Flags::EnumType; }
bool isQList() const { return m_flags.type == Flags::QListType; }
- bool isQJSValue() const { return m_flags.type == Flags::QJSValueType; }
bool isVarProperty() const { return m_flags.type == Flags::VarPropertyType; }
bool isQVariant() const { return m_flags.type == Flags::QVariantType; }
bool isVMEFunction() const { return isFunction() && m_flags.isVMEFunction; }
@@ -209,8 +216,11 @@ public:
bool isVMESignal() const { return isFunction() && m_flags.isAliasORisVMESignal; }
bool isV4Function() const { return isFunction() && m_flags.isFinalORisV4Function; }
bool isSignalHandler() const { return m_flags.isSignalHandler; }
- bool isOverload() const { return m_flags.isOverload; }
- void setOverload(bool onoff) { m_flags.isOverload = onoff; }
+ bool hasMetaObject() const { return m_flags.hasMetaObject; }
+
+ // TODO: Remove this once we can. Signals should not be overridable.
+ bool isOverridableSignal() const { return m_flags.isOverridableSignal; }
+
bool isCloned() const { return isFunction() && m_flags.isRequiredORisCloned; }
bool isConstructor() const { return isFunction() && m_flags.isConstructorORisBindable; }
bool isBindable() const { return !isFunction() && m_flags.isConstructorORisBindable; }
@@ -275,8 +285,36 @@ public:
QTypeRevision typeVersion() const { return m_typeVersion; }
void setTypeVersion(QTypeRevision typeVersion) { m_typeVersion = typeVersion; }
- QQmlPropertyCacheMethodArguments *arguments() const { return m_arguments; }
- void setArguments(QQmlPropertyCacheMethodArguments *args) { m_arguments = args; }
+ QQmlPropertyCacheMethodArguments *arguments() const
+ {
+ Q_ASSERT(!hasMetaObject());
+ return m_arguments;
+ }
+ void setArguments(QQmlPropertyCacheMethodArguments *args)
+ {
+ Q_ASSERT(!hasMetaObject());
+ m_arguments = args;
+ }
+
+ const QMetaObject *metaObject() const
+ {
+ Q_ASSERT(hasMetaObject());
+ return m_metaObject;
+ }
+
+ void setMetaObject(const QMetaObject *metaObject)
+ {
+ Q_ASSERT(!hasArguments() || !m_arguments);
+ m_flags.setHasMetaObject(true);
+ m_metaObject = metaObject;
+ }
+
+ QMetaMethod metaMethod() const
+ {
+ Q_ASSERT(hasMetaObject());
+ Q_ASSERT(isFunction());
+ return m_metaObject->method(m_coreIndex);
+ }
int metaObjectOffset() const { return m_metaObjectOffset; }
void setMetaObjectOffset(int off)
@@ -300,8 +338,17 @@ public:
static Flags flagsForProperty(const QMetaProperty &);
void load(const QMetaProperty &);
void load(const QMetaMethod &);
- QString name(QObject *) const;
- QString name(const QMetaObject *) const;
+
+ QString name(QObject *object) const { return object ? name(object->metaObject()) : QString(); }
+ QString name(const QMetaObject *metaObject) const
+ {
+ if (!metaObject || m_coreIndex == -1)
+ return QString();
+
+ return QString::fromUtf8(isFunction()
+ ? metaObject->method(m_coreIndex).name().constData()
+ : metaObject->property(m_coreIndex).name());
+ }
bool markAsOverrideOf(QQmlPropertyData *predecessor);
@@ -353,7 +400,7 @@ public:
static Flags defaultSignalFlags()
{
Flags f;
- f.type = Flags::FunctionType;
+ f.setType(Flags::FunctionType);
f.setIsSignal(true);
f.setIsVMESignal(true);
return f;
@@ -362,7 +409,7 @@ public:
static Flags defaultSlotFlags()
{
Flags f;
- f.type = Flags::FunctionType;
+ f.setType(Flags::FunctionType);
f.setIsVMEFunction(true);
return f;
}
@@ -388,6 +435,7 @@ private:
union {
QQmlPropertyCacheMethodArguments *m_arguments = nullptr;
StaticMetaCallFunction m_staticMetaCallFunction;
+ const QMetaObject *m_metaObject;
};
};
@@ -397,6 +445,8 @@ private:
Q_STATIC_ASSERT(sizeof(QQmlPropertyData) == 32);
#endif
+static_assert(std::is_trivially_copyable<QQmlPropertyData>::value);
+
bool QQmlPropertyData::operator==(const QQmlPropertyData &other) const
{
return flags() == other.flags() &&
@@ -415,13 +465,15 @@ QQmlPropertyData::Flags::Flags()
, isAliasORisVMESignal(false)
, isFinalORisV4Function(false)
, isSignalHandler(false)
- , isOverload(false)
+ , isOverridableSignal(false)
, isRequiredORisCloned(false)
, isConstructorORisBindable(false)
, isOverridden(false)
+ , hasMetaObject(false)
, type(OtherType)
, overrideIndexIsProperty(false)
-{}
+{
+}
bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) const
{
@@ -434,6 +486,7 @@ bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) c
isOverridden == other.isOverridden &&
isSignalHandler == other.isSignalHandler &&
isRequiredORisCloned == other.isRequiredORisCloned &&
+ hasMetaObject == other.hasMetaObject &&
type == other.type &&
isConstructorORisBindable == other.isConstructorORisBindable &&
overrideIndexIsProperty == other.overrideIndexIsProperty;
@@ -445,7 +498,6 @@ void QQmlPropertyData::Flags::copyPropertyTypeFlags(QQmlPropertyData::Flags from
case QObjectDerivedType:
case EnumType:
case QListType:
- case QJSValueType:
case QVariantType:
type = from.type;
}
diff --git a/src/qml/qml/qqmlpropertyresolver.cpp b/src/qml/qml/qqmlpropertyresolver.cpp
index ff29c38997..0217f7b7b5 100644
--- a/src/qml/qml/qqmlpropertyresolver.cpp
+++ b/src/qml/qml/qqmlpropertyresolver.cpp
@@ -3,6 +3,7 @@
#include "qqmlpropertyresolver_p.h"
#include <private/qqmlcontextdata_p.h>
+#include <private/qqmlsignalnames_p.h>
QT_BEGIN_NAMESPACE
@@ -43,10 +44,8 @@ const QQmlPropertyData *QQmlPropertyResolver::signal(const QString &name, bool *
return d;
}
- if (name.endsWith(QLatin1String("Changed"))) {
- QString propName = name.mid(0, name.size() - static_cast<int>(strlen("Changed")));
-
- d = property(propName, notInRevision);
+ if (auto propName = QQmlSignalNames::changedSignalNameToPropertyName(name)) {
+ d = property(*propName, notInRevision);
if (d)
return cache->signal(d->notifyIndex());
}
diff --git a/src/qml/qml/qqmlpropertytopropertybinding_p.h b/src/qml/qml/qqmlpropertytopropertybinding_p.h
index de6012ded9..d741c5a740 100644
--- a/src/qml/qml/qqmlpropertytopropertybinding_p.h
+++ b/src/qml/qml/qqmlpropertytopropertybinding_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlPropertyToPropertyBinding
+class Q_QML_EXPORT QQmlPropertyToPropertyBinding
: public QQmlAbstractBinding, public QQmlNotifierEndpoint
{
public:
diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp
index c6783842dc..ff22c3043a 100644
--- a/src/qml/qml/qqmlpropertyvalidator.cpp
+++ b/src/qml/qml/qqmlpropertyvalidator.cpp
@@ -9,6 +9,7 @@
#include <private/qqmlpropertycachecreator_p.h>
#include <private/qqmlpropertyresolver_p.h>
#include <private/qqmlstringconverters_p.h>
+#include <private/qqmlsignalnames_p.h>
#include <QtCore/qdatetime.h>
@@ -27,9 +28,8 @@ QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(HANDLE_PRIMITIVE);
}
}
-QQmlPropertyValidator::QQmlPropertyValidator(
- QQmlEnginePrivate *enginePrivate, const QQmlImports *imports,
- const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit)
+QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports *imports,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
: enginePrivate(enginePrivate)
, compilationUnit(compilationUnit)
, imports(imports)
@@ -87,6 +87,12 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
QQmlCustomParser *customParser = nullptr;
if (auto typeRef = resolvedType(obj->inheritedTypeNameIndex)) {
+
+ // This binding instantiates a separate object. The separate object can have an ID and its
+ // own group properties even if it's then assigned to a value type, for example a 'var', or
+ // anything with an invokable ctor taking a QObject*.
+ populatingValueTypeGroupProperty = false;
+
const auto type = typeRef->type();
if (type.isValid())
customParser = type.customParser();
@@ -126,7 +132,7 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
defaultProperty = propertyCache->defaultProperty();
}
- QV4::BindingPropertyData collectedBindingPropertyData(obj->nBindings);
+ QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings);
binding = obj->bindingTable();
for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
@@ -140,7 +146,7 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
customBindings << binding;
continue;
}
- } else if (QmlIR::IRBuilder::isSignalPropertyName(name)
+ } else if (QQmlSignalNames::isHandlerName(name)
&& !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
customBindings << binding;
continue;
@@ -194,7 +200,8 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
QQmlType type;
QQmlImportNamespace *typeNamespace = nullptr;
imports->resolveType(
- stringAt(binding->propertyNameIndex), &type, nullptr, &typeNamespace);
+ QQmlTypeLoader::get(enginePrivate), stringAt(binding->propertyNameIndex),
+ &type, nullptr, &typeNamespace);
if (typeNamespace)
return recordError(binding->location, tr("Invalid use of namespace"));
return recordError(binding->location, tr("Invalid attached object assignment"));
@@ -295,17 +302,25 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
return recordError(
binding->location,
tr("Invalid grouped property access: Property \"%1\" with primitive type \"%2\".")
- .arg(name)
- .arg(QString::fromUtf8(type.name()))
+ .arg(name, QString::fromUtf8(type.name()))
);
}
if (!QQmlMetaType::propertyCacheForType(type)) {
- return recordError(binding->location,
- tr("Invalid grouped property access: Property \"%1\" with type \"%2\", which is not a value type")
- .arg(name)
- .arg(QString::fromUtf8(type.name()))
- );
+ auto mo = type.metaObject();
+ if (!mo) {
+ return recordError(binding->location,
+ tr("Invalid grouped property access: Property \"%1\" with type \"%2\", which is neither a value nor an object type")
+ .arg(name, QString::fromUtf8(type.name()))
+ );
+ }
+ if (QMetaObjectPrivate::get(mo)->flags & DynamicMetaObject) {
+ return recordError(binding->location,
+ tr("Unsupported grouped property access: Property \"%1\" with type \"%2\" has a dynamic meta-object.")
+ .arg(name, QString::fromUtf8(type.name()))
+ );
+ }
+ // fall through, this is okay
}
}
}
@@ -335,7 +350,10 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
customParser->validator = this;
customParser->engine = enginePrivate;
customParser->imports = imports;
- customParser->verifyBindings(compilationUnit, customBindings);
+ customParser->verifyBindings(
+ enginePrivate->v4engine()->executableCompilationUnit(
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit>(compilationUnit)),
+ customBindings);
customParser->validator = nullptr;
customParser->engine = nullptr;
customParser->imports = (QQmlImports*)nullptr;
@@ -364,6 +382,11 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(
if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum))
return noError;
+ // TODO: For historical reasons you can assign any number to an enum property alias
+ // This can be fixed with an opt-out mechanism, for example a pragma.
+ if (property->isAlias() && binding->isNumberBinding())
+ return noError;
+
QString value = compilationUnit->bindingValueAsString(binding);
QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex());
bool ok;
@@ -379,19 +402,6 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(
}
auto warnOrError = [&](const QString &error) {
- if (binding->type() == QV4::CompiledData::Binding::Type_Null) {
- QQmlError warning;
- warning.setUrl(compilationUnit->url());
- warning.setLine(qmlConvertSourceCoordinate<quint32, int>(
- binding->valueLocation.line()));
- warning.setColumn(qmlConvertSourceCoordinate<quint32, int>(
- binding->valueLocation.column()));
- warning.setDescription(error + tr(" - Assigning null to incompatible properties in QML "
- "is deprecated. This will become a compile error in "
- "future versions of Qt."));
- enginePrivate->warning(warning);
- return noError;
- }
return qQmlCompileError(binding->valueLocation, error);
};
@@ -568,10 +578,10 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(
default: return QString();
}
};
- QVariant result(property->propType());
- if (!QQmlValueTypeProvider::createValueType(
+ const QVariant result = QQmlValueTypeProvider::createValueType(
compilationUnit->bindingValueAsString(binding),
- result.metaType(), result.data())) {
+ property->propType());
+ if (!result.isValid()) {
return warnOrError(tr("Invalid property assignment: %1 expected")
.arg(typeName()));
}
@@ -622,7 +632,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(
break;
}
- return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(property->propType().name())));
+ return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QLatin1StringView(property->propType().name())));
}
break;
}
@@ -643,7 +653,7 @@ bool QQmlPropertyValidator::canCoerce(QMetaType to, QQmlPropertyCache::ConstPtr
// only occurs after the whole file has been validated
// Therefore we need to check the ICs here
for (const auto& icDatum : compilationUnit->inlineComponentData) {
- if (icDatum.typeIds.id == to) {
+ if (icDatum.qmlType.typeId() == to) {
toMo = compilationUnit->propertyCaches.at(icDatum.objectIndex);
break;
}
@@ -685,13 +695,12 @@ QQmlError QQmlPropertyValidator::validateObjectBinding(const QQmlPropertyData *p
const QV4::CompiledData::Object *targetObject = compilationUnit->objectAt(binding->value.objectIndex);
if (auto *typeRef = resolvedType(targetObject->inheritedTypeNameIndex)) {
QQmlPropertyCache::ConstPtr cache = typeRef->createPropertyCache();
- const QMetaObject *mo = cache->firstCppMetaObject();
+ const QMetaObject *mo = cache ? cache->firstCppMetaObject() : nullptr;
QQmlType qmlType;
while (mo && !qmlType.isValid()) {
qmlType = QQmlMetaType::qmlType(mo);
mo = mo->superClass();
}
- Q_ASSERT(qmlType.isValid());
isValueSource = qmlType.propertyValueSourceCast() != -1;
isPropertyInterceptor = qmlType.propertyValueInterceptorCast() != -1;
@@ -754,8 +763,9 @@ QQmlError QQmlPropertyValidator::validateObjectBinding(const QQmlPropertyData *p
// only occurs after the whole file has been validated
// Therefore we need to check the ICs here
for (const auto& icDatum: compilationUnit->inlineComponentData) {
- if (icDatum.typeIds.id == property->propType()) {
- propertyMetaObject = compilationUnit->propertyCaches.at(icDatum.objectIndex);
+ if (icDatum.qmlType.typeId() == property->propType()) {
+ propertyMetaObject
+ = compilationUnit->propertyCaches.at(icDatum.objectIndex);
break;
}
}
diff --git a/src/qml/qml/qqmlpropertyvalidator_p.h b/src/qml/qml/qqmlpropertyvalidator_p.h
index e1154347ec..75787fcf68 100644
--- a/src/qml/qml/qqmlpropertyvalidator_p.h
+++ b/src/qml/qml/qqmlpropertyvalidator_p.h
@@ -28,7 +28,9 @@ class QQmlPropertyValidator
{
Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
public:
- QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports *imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit);
+ QQmlPropertyValidator(
+ QQmlEnginePrivate *enginePrivate, const QQmlImports *imports,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
QVector<QQmlError> validate();
@@ -58,12 +60,12 @@ private:
}
QQmlEnginePrivate *enginePrivate;
- QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
const QQmlImports *imports;
const QV4::CompiledData::Unit *qmlUnit;
const QQmlPropertyCacheVector &propertyCaches;
- QVector<QV4::BindingPropertyData> * const bindingPropertyDataPerObject;
+ QVector<QV4::CompiledData::BindingPropertyData> * const bindingPropertyDataPerObject;
};
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlpropertyvalueinterceptor_p.h b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h
index 90feabe565..9d1b0606cd 100644
--- a/src/qml/qml/qqmlpropertyvalueinterceptor_p.h
+++ b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h
@@ -23,7 +23,7 @@
QT_BEGIN_NAMESPACE
class QQmlProperty;
-class Q_QML_PRIVATE_EXPORT QQmlPropertyValueInterceptor
+class Q_QML_EXPORT QQmlPropertyValueInterceptor
{
public:
QQmlPropertyValueInterceptor();
diff --git a/src/qml/qml/qqmlproxymetaobject.cpp b/src/qml/qml/qqmlproxymetaobject.cpp
index ad67856c02..6ec4009c37 100644
--- a/src/qml/qml/qqmlproxymetaobject.cpp
+++ b/src/qml/qml/qqmlproxymetaobject.cpp
@@ -6,8 +6,8 @@
QT_BEGIN_NAMESPACE
-QQmlProxyMetaObject::QQmlProxyMetaObject(QObject *obj, QList<ProxyData> *mList)
-: metaObjects(mList), proxies(nullptr), parent(nullptr), object(obj)
+QQmlProxyMetaObject::QQmlProxyMetaObject(QObject *obj, const QList<ProxyData> *mList)
+ : metaObjects(mList), proxies(nullptr), parent(nullptr), object(obj)
{
metaObject = metaObjects->constFirst().metaObject;
@@ -132,4 +132,12 @@ QMetaObject *QQmlProxyMetaObject::toDynamicMetaObject(QObject *)
return metaObject;
}
+void QQmlProxyMetaObject::objectDestroyed(QObject *object)
+{
+ if (parent)
+ parent->objectDestroyed(object);
+ else
+ QDynamicMetaObjectData::objectDestroyed(object);
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlproxymetaobject_p.h b/src/qml/qml/qqmlproxymetaobject_p.h
index 6238dff4d1..18cf2d4bac 100644
--- a/src/qml/qml/qqmlproxymetaobject_p.h
+++ b/src/qml/qml/qqmlproxymetaobject_p.h
@@ -25,7 +25,7 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlProxyMetaObject : public QDynamicMetaObjectData
+class Q_QML_EXPORT QQmlProxyMetaObject : public QDynamicMetaObjectData
{
public:
struct ProxyData {
@@ -36,7 +36,7 @@ public:
int methodOffset;
};
- QQmlProxyMetaObject(QObject *, QList<ProxyData> *);
+ QQmlProxyMetaObject(QObject *, const QList<ProxyData> *);
~QQmlProxyMetaObject();
static constexpr int extensionObjectId(int id) noexcept
@@ -49,11 +49,12 @@ public:
protected:
int metaCall(QObject *o, QMetaObject::Call _c, int _id, void **_a) override;
QMetaObject *toDynamicMetaObject(QObject *) override;
+ void objectDestroyed(QObject *object) override;
private:
QObject *getProxy(int index);
- QList<ProxyData> *metaObjects;
+ const QList<ProxyData> *metaObjects;
QObject **proxies;
QDynamicMetaObjectData *parent;
diff --git a/src/qml/qml/qqmlregistration.h b/src/qml/qml/qqmlregistration.h
index f40b39687c..562a79eaf1 100644
--- a/src/qml/qml/qqmlregistration.h
+++ b/src/qml/qml/qqmlregistration.h
@@ -10,19 +10,6 @@
// satisfy configure, which warns about public headers not using those
QT_BEGIN_NAMESPACE
-#define QML_FOREIGN(FOREIGN_TYPE) \
- Q_CLASSINFO("QML.Foreign", #FOREIGN_TYPE) \
- using QmlForeignType = FOREIGN_TYPE; \
- template<class, class> friend struct QML_PRIVATE_NAMESPACE::QmlResolved; \
- template<typename... Args> \
- friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor, QList<int> *); \
- inline constexpr void qt_qmlMarker_foreign() {}
-
-#define QML_FOREIGN_NAMESPACE(FOREIGN_NAMESPACE) \
- Q_CLASSINFO("QML.Foreign", #FOREIGN_NAMESPACE)
-
-#define QML_CUSTOMPARSER Q_CLASSINFO("QML.HasCustomParser", "true")
-
QT_END_NAMESPACE
#endif // QQMLREGISTRATION_H
diff --git a/src/qml/qml/qqmlscriptblob.cpp b/src/qml/qml/qqmlscriptblob.cpp
index 72bc3a7c12..fa9a41e801 100644
--- a/src/qml/qml/qqmlscriptblob.cpp
+++ b/src/qml/qml/qqmlscriptblob.cpp
@@ -32,14 +32,18 @@ QQmlRefPointer<QQmlScriptData> QQmlScriptBlob::scriptData() const
return m_scriptData;
}
+bool QQmlScriptBlob::isNative() const
+{
+ return m_scriptData && !m_scriptData->m_value.isEmpty();
+}
+
void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
{
if (readCacheFile()) {
- QQmlRefPointer<QV4::ExecutableCompilationUnit> unit
- = QV4::ExecutableCompilationUnit::create();
+ auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>();
QString error;
if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) {
- initializeFromCompilationUnit(unit);
+ initializeFromCompilationUnit(std::move(unit));
return;
} else {
qCDebug(DBG_DISK_CACHE()) << "Error loading" << urlString() << "from disk cache:" << error;
@@ -61,7 +65,7 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
return;
}
- QV4::CompiledData::CompilationUnit unit;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
if (m_isModule) {
QList<QQmlJS::DiagnosticMessage> diagnostics;
@@ -96,28 +100,26 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
unit = std::move(irUnit.javaScriptCompilationUnit);
}
- auto executableUnit = QV4::ExecutableCompilationUnit::create(std::move(unit));
-
if (writeCacheFile()) {
QString errorString;
- if (executableUnit->saveToDisk(url(), &errorString)) {
+ if (unit->saveToDisk(url(), &errorString)) {
QString error;
- if (!executableUnit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) {
+ if (!unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) {
// ignore error, keep using the in-memory compilation unit.
}
} else {
qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of"
- << executableUnit->fileName() << "to disk:" << errorString;
+ << unit->fileName() << "to disk:" << errorString;
}
}
- initializeFromCompilationUnit(executableUnit);
+ initializeFromCompilationUnit(std::move(unit));
}
-void QQmlScriptBlob::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit)
+void QQmlScriptBlob::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *cachedUnit)
{
- initializeFromCompilationUnit(QV4::ExecutableCompilationUnit::create(
- QV4::CompiledData::CompilationUnit(unit->qmlData, unit->aotCompiledFunctions, urlString(), finalUrlString())));
+ initializeFromCompilationUnit(QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>(
+ cachedUnit->qmlData, cachedUnit->aotCompiledFunctions, urlString(), finalUrlString()));
}
void QQmlScriptBlob::done()
@@ -182,9 +184,12 @@ void QQmlScriptBlob::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob,
m_scripts << ref;
}
-void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit)
+void QQmlScriptBlob::initializeFromCompilationUnit(
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> &&unit)
{
Q_ASSERT(!m_scriptData);
+ Q_ASSERT(unit);
+
m_scriptData.adopt(new QQmlScriptData());
m_scriptData->url = finalUrl();
m_scriptData->urlString = finalUrlString();
@@ -192,12 +197,10 @@ void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::Exe
m_importCache->setBaseUrl(finalUrl(), finalUrlString());
- QQmlRefPointer<QV4::ExecutableCompilationUnit> script = m_scriptData->m_precompiledScript;
-
if (!m_isModule) {
QList<QQmlError> errors;
- for (quint32 i = 0, count = script->importCount(); i < count; ++i) {
- const QV4::CompiledData::Import *import = script->importAt(i);
+ for (quint32 i = 0, count = unit->importCount(); i < count; ++i) {
+ const QV4::CompiledData::Import *import = unit->importAt(i);
if (!addImport(import, {}, &errors)) {
Q_ASSERT(errors.size());
QQmlError error(errors.takeFirst());
@@ -211,22 +214,25 @@ void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::Exe
}
}
- auto *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine());
-
- v4->injectCompiledModule(unit);
+ const QStringList moduleRequests = unit->moduleRequests();
+ for (const QString &request: moduleRequests) {
+ const QUrl relativeRequest = QUrl(request);
+ if (m_typeLoader->injectedScript(relativeRequest))
+ continue;
- for (const QString &request: unit->moduleRequests()) {
- const auto module = v4->moduleForUrl(QUrl(request), unit.data());
- if (module.compiled || module.native)
+ const QUrl absoluteRequest = unit->finalUrl().resolved(relativeRequest);
+ QQmlRefPointer<QQmlScriptBlob> absoluteBlob = typeLoader()->getScript(absoluteRequest);
+ if (absoluteBlob->m_scriptData && absoluteBlob->m_scriptData->m_precompiledScript)
continue;
- const QUrl absoluteRequest = unit->finalUrl().resolved(QUrl(request));
- QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(absoluteRequest);
- addDependency(blob.data());
- scriptImported(blob, /* ### */QV4::CompiledData::Location(), /*qualifier*/QString(), /*namespace*/QString());
+ addDependency(absoluteBlob.data());
+ scriptImported(
+ absoluteBlob, /* ### */QV4::CompiledData::Location(), /*qualifier*/QString(),
+ /*namespace*/QString());
}
}
+
/*!
\internal
diff --git a/src/qml/qml/qqmlscriptblob_p.h b/src/qml/qml/qqmlscriptblob_p.h
index ad9c573400..59f969859b 100644
--- a/src/qml/qml/qqmlscriptblob_p.h
+++ b/src/qml/qml/qqmlscriptblob_p.h
@@ -39,6 +39,7 @@ public:
};
QQmlRefPointer<QQmlScriptData> scriptData() const;
+ bool isNative() const;
protected:
void dataReceived(const SourceCodeData &) override;
@@ -49,7 +50,7 @@ protected:
private:
void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
- void initializeFromCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit);
+ void initializeFromCompilationUnit(QQmlRefPointer<QV4::CompiledData::CompilationUnit> &&cu);
void initializeFromNative(const QV4::Value &value);
QList<ScriptReference> m_scripts;
diff --git a/src/qml/qml/qqmlscriptdata.cpp b/src/qml/qml/qqmlscriptdata.cpp
index 1a6a8157d0..9337f25c6e 100644
--- a/src/qml/qml/qqmlscriptdata.cpp
+++ b/src/qml/qml/qqmlscriptdata.cpp
@@ -13,11 +13,6 @@
QT_BEGIN_NAMESPACE
-QQmlScriptData::QQmlScriptData()
- : m_loaded(false)
-{
-}
-
QQmlRefPointer<QQmlContextData> QQmlScriptData::qmlContextDataForContext(
const QQmlRefPointer<QQmlContextData> &parentQmlContextData)
{
@@ -86,7 +81,11 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(
/* scopeObject: */ nullptr);
}
- QV4::Scoped<QV4::Module> module(scope, m_precompiledScript->instantiate(v4));
+ QV4::Scoped<QV4::Module> module(
+ scope,
+ v4->executableCompilationUnit(QQmlRefPointer<QV4::CompiledData::CompilationUnit>(
+ m_precompiledScript))->instantiate());
+
if (module) {
if (qmlExecutionContext) {
module->d()->scope->outer.set(v4, qmlExecutionContext->d());
diff --git a/src/qml/qml/qqmlscriptdata_p.h b/src/qml/qml/qqmlscriptdata_p.h
index c7100861e2..ad5ffb3d07 100644
--- a/src/qml/qml/qqmlscriptdata_p.h
+++ b/src/qml/qml/qqmlscriptdata_p.h
@@ -19,7 +19,7 @@
#include <private/qqmlscriptblob_p.h>
#include <private/qv4value_p.h>
#include <private/qv4persistent_p.h>
-#include <private/qv4executablecompilationunit_p.h>
+#include <private/qv4compileddata_p.h>
#include <QtCore/qurl.h>
@@ -28,12 +28,12 @@ QT_BEGIN_NAMESPACE
class QQmlTypeNameCache;
class QQmlContextData;
-class Q_AUTOTEST_EXPORT QQmlScriptData : public QQmlRefCount
+class Q_AUTOTEST_EXPORT QQmlScriptData final : public QQmlRefCounted<QQmlScriptData>
{
private:
friend class QQmlTypeLoader;
- QQmlScriptData();
+ QQmlScriptData() = default;
public:
QUrl url;
@@ -43,7 +43,10 @@ public:
QV4::ReturnedValue scriptValueForContext(const QQmlRefPointer<QQmlContextData> &parentCtxt);
- QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit() const { return m_precompiledScript; }
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit() const
+ {
+ return m_precompiledScript;
+ }
private:
friend class QQmlScriptBlob;
@@ -51,8 +54,8 @@ private:
QQmlRefPointer<QQmlContextData> qmlContextDataForContext(
const QQmlRefPointer<QQmlContextData> &parentQmlContextData);
- bool m_loaded;
- QQmlRefPointer<QV4::ExecutableCompilationUnit> m_precompiledScript;
+ bool m_loaded = false;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_precompiledScript;
QV4::PersistentValue m_value;
};
diff --git a/src/qml/qml/qqmlscriptstring.cpp b/src/qml/qml/qqmlscriptstring.cpp
index 370573b199..9f47841031 100644
--- a/src/qml/qml/qqmlscriptstring.cpp
+++ b/src/qml/qml/qqmlscriptstring.cpp
@@ -44,7 +44,7 @@ const QQmlScriptStringPrivate* QQmlScriptStringPrivate::get(const QQmlScriptStri
Constructs an empty instance.
*/
QQmlScriptString::QQmlScriptString()
-: d(new QQmlScriptStringPrivate)
+: d()
{
}
@@ -92,6 +92,8 @@ bool QQmlScriptString::operator==(const QQmlScriptString &other) const
{
if (d == other.d)
return true;
+ if (!d || !other.d)
+ return false;
if (d->isNumberLiteral || other.d->isNumberLiteral)
return d->isNumberLiteral && other.d->isNumberLiteral && d->numberValue == other.d->numberValue;
@@ -126,6 +128,8 @@ Returns whether the QQmlScriptString is empty.
*/
bool QQmlScriptString::isEmpty() const
{
+ if (!d)
+ return true;
if (!d->script.isEmpty())
return false;
return d->bindingId == -1;
@@ -136,7 +140,7 @@ Returns whether the content of the QQmlScriptString is the \c undefined literal.
*/
bool QQmlScriptString::isUndefinedLiteral() const
{
- return d->script == QLatin1String("undefined");
+ return d && d->script == QLatin1String("undefined");
}
/*!
@@ -144,7 +148,7 @@ Returns whether the content of the QQmlScriptString is the \c null literal.
*/
bool QQmlScriptString::isNullLiteral() const
{
- return d->script == QLatin1String("null");
+ return d && d->script == QLatin1String("null");
}
/*!
@@ -153,7 +157,7 @@ Otherwise returns a null QString.
*/
QString QQmlScriptString::stringLiteral() const
{
- if (d->isStringLiteral)
+ if (d && d->isStringLiteral)
return d->script.mid(1, d->script.size()-2);
return QString();
}
@@ -165,8 +169,8 @@ sets \a ok to true. Otherwise returns 0.0 and sets \a ok to false.
qreal QQmlScriptString::numberLiteral(bool *ok) const
{
if (ok)
- *ok = d->isNumberLiteral;
- return d->isNumberLiteral ? d->numberValue : 0.;
+ *ok = d && d->isNumberLiteral;
+ return (d && d->isNumberLiteral) ? d->numberValue : 0.;
}
/*!
@@ -175,8 +179,8 @@ sets \a ok to true. Otherwise returns false and sets \a ok to false.
*/
bool QQmlScriptString::booleanLiteral(bool *ok) const
{
- bool isTrue = d->script == QLatin1String("true");
- bool isFalse = !isTrue && d->script == QLatin1String("false");
+ bool isTrue = d && d->script == QLatin1String("true");
+ bool isFalse = !isTrue && d && d->script == QLatin1String("false");
if (ok)
*ok = isTrue || isFalse;
return isTrue ? true : false;
diff --git a/src/qml/qml/qqmlscriptstring.h b/src/qml/qml/qqmlscriptstring.h
index 51e86089e8..debc3d16c1 100644
--- a/src/qml/qml/qqmlscriptstring.h
+++ b/src/qml/qml/qqmlscriptstring.h
@@ -23,7 +23,6 @@ namespace QV4 {
class Q_QML_EXPORT QQmlScriptString
{
Q_GADGET
- QML_ANONYMOUS
public:
QQmlScriptString();
QQmlScriptString(const QQmlScriptString &);
diff --git a/src/qml/qml/qqmlstringconverters.cpp b/src/qml/qml/qqmlstringconverters.cpp
index c1625b6667..a1f8cce67c 100644
--- a/src/qml/qml/qqmlstringconverters.cpp
+++ b/src/qml/qml/qqmlstringconverters.cpp
@@ -40,8 +40,8 @@ QVariant QQmlStringConverters::variantFromString(const QString &s, QMetaType pre
case QMetaType::QRect:
return QVariant::fromValue(rectFFromString(s, ok).toRect());
default: {
- QVariant ret(preferredType);
- if (QQmlValueTypeProvider::createValueType(s, preferredType, ret.data())) {
+ const QVariant ret = QQmlValueTypeProvider::createValueType(s, preferredType);
+ if (ret.isValid()) {
if (ok)
*ok = true;
return ret;
@@ -89,77 +89,19 @@ QDateTime QQmlStringConverters::dateTimeFromString(const QString &s, bool *ok)
//expects input of "x,y"
QPointF QQmlStringConverters::pointFFromString(const QString &s, bool *ok)
{
- if (s.count(QLatin1Char(',')) != 1) {
- if (ok)
- *ok = false;
- return QPointF();
- }
-
- bool xGood, yGood;
- int index = s.indexOf(QLatin1Char(','));
- qreal xCoord = QStringView{s}.left(index).toDouble(&xGood);
- qreal yCoord = QStringView{s}.mid(index+1).toDouble(&yGood);
- if (!xGood || !yGood) {
- if (ok)
- *ok = false;
- return QPointF();
- }
-
- if (ok)
- *ok = true;
- return QPointF(xCoord, yCoord);
+ return valueTypeFromNumberString<QPointF, 2, u','>(s, ok);
}
//expects input of "widthxheight"
QSizeF QQmlStringConverters::sizeFFromString(const QString &s, bool *ok)
{
- if (s.count(QLatin1Char('x')) != 1) {
- if (ok)
- *ok = false;
- return QSizeF();
- }
-
- bool wGood, hGood;
- int index = s.indexOf(QLatin1Char('x'));
- qreal width = QStringView{s}.left(index).toDouble(&wGood);
- qreal height = QStringView{s}.mid(index+1).toDouble(&hGood);
- if (!wGood || !hGood) {
- if (ok)
- *ok = false;
- return QSizeF();
- }
-
- if (ok)
- *ok = true;
- return QSizeF(width, height);
+ return valueTypeFromNumberString<QSizeF, 2, u'x'>(s, ok);
}
//expects input of "x,y,widthxheight" //### use space instead of second comma?
QRectF QQmlStringConverters::rectFFromString(const QString &s, bool *ok)
{
- if (s.count(QLatin1Char(',')) != 2 || s.count(QLatin1Char('x')) != 1) {
- if (ok)
- *ok = false;
- return QRectF();
- }
-
- bool xGood, yGood, wGood, hGood;
- int index = s.indexOf(QLatin1Char(','));
- qreal x = QStringView{s}.left(index).toDouble(&xGood);
- int index2 = s.indexOf(QLatin1Char(','), index+1);
- qreal y = QStringView{s}.mid(index+1, index2-index-1).toDouble(&yGood);
- index = s.indexOf(QLatin1Char('x'), index2+1);
- qreal width = QStringView{s}.mid(index2+1, index-index2-1).toDouble(&wGood);
- qreal height = QStringView{s}.mid(index+1).toDouble(&hGood);
- if (!xGood || !yGood || !wGood || !hGood) {
- if (ok)
- *ok = false;
- return QRectF();
- }
-
- if (ok)
- *ok = true;
- return QRectF(x, y, width, height);
+ return valueTypeFromNumberString<QRectF, 4, u',', u',', u'x'>(s, ok);
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlstringconverters_p.h b/src/qml/qml/qqmlstringconverters_p.h
index 5a07b947ca..0d220b11fc 100644
--- a/src/qml/qml/qqmlstringconverters_p.h
+++ b/src/qml/qml/qqmlstringconverters_p.h
@@ -30,19 +30,92 @@ class QByteArray;
namespace QQmlStringConverters
{
- Q_QML_PRIVATE_EXPORT QVariant variantFromString(const QString &, QMetaType preferredType, bool *ok = nullptr);
+ Q_QML_EXPORT QVariant variantFromString(const QString &, QMetaType preferredType, bool *ok = nullptr);
- Q_QML_PRIVATE_EXPORT QVariant colorFromString(const QString &, bool *ok = nullptr);
- Q_QML_PRIVATE_EXPORT unsigned rgbaFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT QVariant colorFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT unsigned rgbaFromString(const QString &, bool *ok = nullptr);
#if QT_CONFIG(datestring)
- Q_QML_PRIVATE_EXPORT QDate dateFromString(const QString &, bool *ok = nullptr);
- Q_QML_PRIVATE_EXPORT QTime timeFromString(const QString &, bool *ok = nullptr);
- Q_QML_PRIVATE_EXPORT QDateTime dateTimeFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT QDate dateFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT QTime timeFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT QDateTime dateTimeFromString(const QString &, bool *ok = nullptr);
#endif
- Q_QML_PRIVATE_EXPORT QPointF pointFFromString(const QString &, bool *ok = nullptr);
- Q_QML_PRIVATE_EXPORT QSizeF sizeFFromString(const QString &, bool *ok = nullptr);
- Q_QML_PRIVATE_EXPORT QRectF rectFFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT QPointF pointFFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT QSizeF sizeFFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT QRectF rectFFromString(const QString &, bool *ok = nullptr);
+
+ // checks if the string contains a list of doubles separated by separators, like "double1
+ // separators1 double2 separators2 ..." for example.
+ template<int NumParams, char16_t... separators>
+ bool isValidNumberString(const QString &s, std::array<double, NumParams> *numbers = nullptr)
+ {
+ Q_STATIC_ASSERT_X(
+ NumParams == 2 || NumParams == 3 || NumParams == 4 || NumParams == 16,
+ "Unsupported number of params; add an additional case below if necessary.");
+ constexpr std::array<char16_t, NumParams - 1> separatorArray{ separators... };
+ // complain about missing separators when first or last entry is initialized with 0
+ Q_STATIC_ASSERT_X(separatorArray[0] != 0,
+ "Did not specify any separators for isValidNumberString.");
+ Q_STATIC_ASSERT_X(separatorArray[NumParams - 2] != 0,
+ "Did not specify enough separators for isValidNumberString.");
+
+ bool floatOk = true;
+ QStringView view(s);
+ for (qsizetype i = 0; i < NumParams - 1; ++i) {
+ const qsizetype commaIndex = view.indexOf(separatorArray[i]);
+ if (commaIndex == -1)
+ return false;
+ const auto current = view.first(commaIndex).toDouble(&floatOk);
+ if (!floatOk)
+ return false;
+ if (numbers)
+ (*numbers)[i] = current;
+
+ view = view.sliced(commaIndex + 1);
+ }
+ const auto current = view.toDouble(&floatOk);
+ if (!floatOk)
+ return false;
+ if (numbers)
+ (*numbers)[NumParams - 1] = current;
+
+ return true;
+ }
+
+ // Constructs a value type T from the given string that contains NumParams double values
+ // separated by separators, like "double1 separators1 double2 separators2 ..." for example.
+ template<typename T, int NumParams, char16_t... separators>
+ T valueTypeFromNumberString(const QString &s, bool *ok = nullptr)
+ {
+ Q_STATIC_ASSERT_X(
+ NumParams == 2 || NumParams == 3 || NumParams == 4 || NumParams == 16,
+ "Unsupported number of params; add an additional case below if necessary.");
+
+ std::array<double, NumParams> parameters;
+ if (!isValidNumberString<NumParams, separators...>(s, &parameters)) {
+ if (ok)
+ *ok = false;
+ return T{};
+ }
+
+ if (ok)
+ *ok = true;
+
+ if constexpr (NumParams == 2) {
+ return T(parameters[0], parameters[1]);
+ } else if constexpr (NumParams == 3) {
+ return T(parameters[0], parameters[1], parameters[2]);
+ } else if constexpr (NumParams == 4) {
+ return T(parameters[0], parameters[1], parameters[2], parameters[3]);
+ } else if constexpr (NumParams == 16) {
+ return T(parameters[0], parameters[1], parameters[2], parameters[3], parameters[4],
+ parameters[5], parameters[6], parameters[7], parameters[8], parameters[9],
+ parameters[10], parameters[11], parameters[12], parameters[13], parameters[14],
+ parameters[15]);
+ }
+
+ Q_UNREACHABLE_RETURN(T{});
+ }
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp
index d01aa5ba11..533c3909ba 100644
--- a/src/qml/qml/qqmltype.cpp
+++ b/src/qml/qml/qqmltype.cpp
@@ -17,47 +17,44 @@
QT_BEGIN_NAMESPACE
QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type)
- : regType(type), iid(nullptr), revision(QTypeRevision::zero()),
- containsRevisionedAttributes(false), baseMetaObject(nullptr),
- index(-1), isSetup(false), isEnumFromCacheSetup(false), isEnumFromBaseSetup(false),
- haveSuperType(false)
+ : regType(type)
{
switch (type) {
case QQmlType::CppType:
- extraData.cd = new QQmlCppTypeData;
- extraData.cd->allocationSize = 0;
- extraData.cd->newFunc = nullptr;
- extraData.cd->createValueTypeFunc = nullptr;
- extraData.cd->parserStatusCast = -1;
- extraData.cd->extFunc = nullptr;
- extraData.cd->extMetaObject = nullptr;
- extraData.cd->customParser = nullptr;
- extraData.cd->attachedPropertiesFunc = nullptr;
- extraData.cd->attachedPropertiesType = nullptr;
- extraData.cd->propertyValueSourceCast = -1;
- extraData.cd->propertyValueInterceptorCast = -1;
- extraData.cd->finalizerCast = -1;
- extraData.cd->registerEnumClassesUnscoped = true;
- extraData.cd->registerEnumsFromRelatedTypes = true;
+ extraData.cppTypeData = new QQmlCppTypeData;
+ extraData.cppTypeData->allocationSize = 0;
+ extraData.cppTypeData->newFunc = nullptr;
+ extraData.cppTypeData->createValueTypeFunc = nullptr;
+ extraData.cppTypeData->parserStatusCast = -1;
+ extraData.cppTypeData->extFunc = nullptr;
+ extraData.cppTypeData->extMetaObject = nullptr;
+ extraData.cppTypeData->customParser = nullptr;
+ extraData.cppTypeData->attachedPropertiesFunc = nullptr;
+ extraData.cppTypeData->attachedPropertiesType = nullptr;
+ extraData.cppTypeData->propertyValueSourceCast = -1;
+ extraData.cppTypeData->propertyValueInterceptorCast = -1;
+ extraData.cppTypeData->finalizerCast = -1;
+ extraData.cppTypeData->registerEnumClassesUnscoped = true;
+ extraData.cppTypeData->registerEnumsFromRelatedTypes = true;
break;
case QQmlType::SingletonType:
case QQmlType::CompositeSingletonType:
- extraData.sd = new QQmlSingletonTypeData;
- extraData.sd->singletonInstanceInfo = nullptr;
- extraData.sd->extFunc = nullptr;
- extraData.sd->extMetaObject = nullptr;
+ extraData.singletonTypeData = new QQmlSingletonTypeData;
+ extraData.singletonTypeData->singletonInstanceInfo = nullptr;
+ extraData.singletonTypeData->extFunc = nullptr;
+ extraData.singletonTypeData->extMetaObject = nullptr;
break;
case QQmlType::InterfaceType:
- extraData.cd = nullptr;
+ extraData.interfaceTypeData = nullptr;
break;
case QQmlType::CompositeType:
- extraData.fd = new QQmlCompositeTypeData;
+ new (&extraData.compositeTypeData) QUrl();
break;
case QQmlType::InlineComponentType:
- extraData.id = new QQmlInlineTypeData;
+ new (&extraData.inlineComponentTypeData) QUrl();
break;
case QQmlType::SequentialContainerType:
- extraData.ld = new QQmlSequenceTypeData;
+ new (&extraData.sequentialContainerTypeData) QMetaSequence();
break;
default: qFatal("QQmlTypePrivate Internal Error.");
}
@@ -65,27 +62,32 @@ QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type)
QQmlTypePrivate::~QQmlTypePrivate()
{
- qDeleteAll(scopedEnums);
- for (const auto &metaObject : metaObjects)
- free(metaObject.metaObject);
+ delete enums.fetchAndStoreAcquire(nullptr);
+ delete proxyMetaObjects.fetchAndStoreAcquire(nullptr);
+
+ if (const QtPrivate::QMetaTypeInterface *iface = typeId.iface()) {
+ if (iface->metaObjectFn == &dynamicQmlMetaObject)
+ QQmlMetaType::unregisterInternalCompositeType(typeId, listId);
+ }
+
switch (regType) {
case QQmlType::CppType:
- delete extraData.cd->customParser;
- delete extraData.cd;
+ delete extraData.cppTypeData->customParser;
+ delete extraData.cppTypeData;
break;
case QQmlType::SingletonType:
case QQmlType::CompositeSingletonType:
- delete extraData.sd->singletonInstanceInfo;
- delete extraData.sd;
+ extraData.singletonTypeData->singletonInstanceInfo.reset();
+ delete extraData.singletonTypeData;
break;
case QQmlType::CompositeType:
- delete extraData.fd;
+ extraData.compositeTypeData.~QUrl();
break;
case QQmlType::InlineComponentType:
- delete extraData.id;
+ extraData.inlineComponentTypeData.~QUrl();
break;
case QQmlType::SequentialContainerType:
- delete extraData.ld;
+ extraData.sequentialContainerTypeData.~QMetaSequence();
break;
default: //Also InterfaceType, because it has no extra data
break;
@@ -144,7 +146,7 @@ QQmlType QQmlTypePrivate::resolveCompositeBaseType(QQmlEnginePrivate *engine) co
QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()));
if (td.isNull() || !td->isComplete())
return QQmlType();
- QV4::ExecutableCompilationUnit *compilationUnit = td->compilationUnit();
+ QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit();
const QMetaObject *mo = compilationUnit->rootPropertyCache()->firstCppMetaObject();
return QQmlMetaType::qmlType(mo);
}
@@ -159,7 +161,7 @@ QQmlPropertyCache::ConstPtr QQmlTypePrivate::compositePropertyCache(
QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()));
if (td.isNull() || !td->isComplete())
return nullptr;
- QV4::ExecutableCompilationUnit *compilationUnit = td->compilationUnit();
+ QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit();
return compilationUnit->rootPropertyCache();
}
@@ -168,21 +170,29 @@ static bool isPropertyRevisioned(const QMetaObject *mo, int index)
return mo->property(index).revision();
}
-void QQmlTypePrivate::init() const
+const QQmlTypePrivate::ProxyMetaObjects *QQmlTypePrivate::init() const
{
- if (isSetup.loadAcquire())
- return;
+ if (const ProxyMetaObjects *result = proxyMetaObjects.loadRelaxed())
+ return result;
- QMutexLocker lock(QQmlMetaType::typeRegistrationLock());
- if (isSetup.loadAcquire())
- return;
+ ProxyMetaObjects *proxies = new ProxyMetaObjects;
+ auto finalize = [this, proxies]() -> const ProxyMetaObjects *{
+ const ProxyMetaObjects *concurrentModification;
+ if (proxyMetaObjects.testAndSetOrdered(nullptr, proxies, concurrentModification))
+ return proxies;
+
+ delete proxies;
+ return concurrentModification;
+ };
const QMetaObject *mo = baseMetaObject;
if (!mo) {
// version 0 singleton type without metaobject information
- return;
+ return finalize();
}
+ QList<QQmlProxyMetaObject::ProxyData> metaObjects;
+
auto setupExtendedMetaObject = [&](const QMetaObject *extMetaObject,
QObject *(*extFunc)(QObject *)) {
if (!extMetaObject)
@@ -200,9 +210,9 @@ void QQmlTypePrivate::init() const
};
if (regType == QQmlType::SingletonType)
- setupExtendedMetaObject(extraData.sd->extMetaObject, extraData.sd->extFunc);
+ setupExtendedMetaObject(extraData.singletonTypeData->extMetaObject, extraData.singletonTypeData->extFunc);
else if (regType == QQmlType::CppType)
- setupExtendedMetaObject(extraData.cd->extMetaObject, extraData.cd->extFunc);
+ setupExtendedMetaObject(extraData.cppTypeData->extMetaObject, extraData.cppTypeData->extFunc);
metaObjects.append(QQmlMetaType::proxyData(
mo, baseMetaObject, metaObjects.isEmpty() ? nullptr
@@ -215,6 +225,8 @@ void QQmlTypePrivate::init() const
metaObjects.at(ii).metaObject->methodOffset();
}
+ bool containsRevisionedAttributes = false;
+
// Check for revisioned details
{
const QMetaObject *mo = nullptr;
@@ -234,46 +246,54 @@ void QQmlTypePrivate::init() const
}
}
- isSetup.storeRelease(true);
- lock.unlock();
+ proxies->data = std::move(metaObjects);
+ proxies->containsRevisionedAttributes = containsRevisionedAttributes;
+
+ return finalize();
}
-void QQmlTypePrivate::initEnums(QQmlEnginePrivate *engine) const
+const QQmlTypePrivate::Enums *QQmlTypePrivate::initEnums(QQmlEnginePrivate *engine) const
{
- QQmlPropertyCache::ConstPtr cache = (!isEnumFromCacheSetup.loadAcquire() && isComposite())
- ? compositePropertyCache(engine)
- : QQmlPropertyCache::ConstPtr();
+ if (const Enums *result = enums.loadRelaxed())
+ return result;
- // beware: It could be a singleton type without metaobject
- const QMetaObject *metaObject = !isEnumFromBaseSetup.loadAcquire()
- ? baseMetaObject
- : nullptr;
+ QQmlPropertyCache::ConstPtr cache;
+ if (isComposite()) {
+ cache = compositePropertyCache(engine);
+ if (!cache)
+ return nullptr; // Composite type not ready, yet.
+ }
- if (!cache && !metaObject)
- return;
+ Enums *newEnums = new Enums;
- init(); // init() can add to the metaObjects list. Therefore, check metaObjects only below
+ // beware: It could be a singleton type without metaobject
- QMutexLocker lock(QQmlMetaType::typeRegistrationLock());
+ if (cache)
+ insertEnumsFromPropertyCache(newEnums, cache);
- if (cache) {
- insertEnumsFromPropertyCache(cache);
- isEnumFromCacheSetup.storeRelease(true);
+ if (baseMetaObject) {
+ // init() can add to the metaObjects list. Therefore, check proxies->data only below
+ const ProxyMetaObjects *proxies = init();
+ insertEnums(
+ newEnums,
+ proxies->data.isEmpty() ? baseMetaObject : proxies->data.constFirst().metaObject);
}
- if (metaObject) {
- insertEnums(metaObjects.isEmpty() ? baseMetaObject : metaObjects.constFirst().metaObject);
- isEnumFromBaseSetup.storeRelease(true);
- }
+ const Enums *concurrentModification;
+ if (enums.testAndSetOrdered(nullptr, newEnums, concurrentModification))
+ return newEnums;
+
+ delete newEnums;
+ return concurrentModification;
}
-void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const
+void QQmlTypePrivate::insertEnums(Enums *enums, const QMetaObject *metaObject) const
{
// Add any enum values defined by 'related' classes
- if (regType != QQmlType::CppType || extraData.cd->registerEnumsFromRelatedTypes) {
+ if (regType != QQmlType::CppType || extraData.cppTypeData->registerEnumsFromRelatedTypes) {
if (const auto *related = metaObject->d.relatedMetaObjects) {
while (const QMetaObject *relatedMetaObject = *related) {
- insertEnums(relatedMetaObject);
+ insertEnums(enums, relatedMetaObject);
++related;
}
}
@@ -282,6 +302,19 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const
QSet<QString> localEnums;
const QMetaObject *localMetaObject = nullptr;
+ // ### TODO (QTBUG-123294): track this at instance creation time
+ auto shouldSingletonAlsoRegisterUnscoped = [&](){
+ Q_ASSERT(regType == QQmlType::SingletonType);
+ if (!baseMetaObject)
+ return true;
+ int idx = baseMetaObject->indexOfClassInfo("RegisterEnumClassesUnscoped");
+ if (idx == -1)
+ return true;
+ if (qstrcmp(baseMetaObject->classInfo(idx).value(), "false") == 0)
+ return false;
+ return true;
+ };
+
// Add any enum values defined by this class, overwriting any inherited values
for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) {
QMetaEnum e = metaObject->enumerator(ii);
@@ -298,29 +331,33 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const
localEnums.clear();
localMetaObject = e.enclosingMetaObject();
}
+ const bool shouldRegisterUnscoped = !isScoped
+ || (regType == QQmlType::CppType && extraData.cppTypeData->registerEnumClassesUnscoped)
+ || (regType == QQmlType::SingletonType && shouldSingletonAlsoRegisterUnscoped())
+ ;
for (int jj = 0; jj < e.keyCount(); ++jj) {
const QString key = QString::fromUtf8(e.key(jj));
const int value = e.value(jj);
- if (!isScoped || (regType == QQmlType::CppType && extraData.cd->registerEnumClassesUnscoped)) {
+ if (shouldRegisterUnscoped) {
if (localEnums.contains(key)) {
- auto existingEntry = enums.find(key);
- if (existingEntry != enums.end() && existingEntry.value() != value) {
+ auto existingEntry = enums->enums.find(key);
+ if (existingEntry != enums->enums.end() && existingEntry.value() != value) {
qWarning("Previously registered enum will be overwritten due to name clash: %s.%s", metaObject->className(), key.toUtf8().constData());
createEnumConflictReport(metaObject, key);
}
} else {
localEnums.insert(key);
}
- enums.insert(key, value);
+ enums->enums.insert(key, value);
}
if (isScoped)
scoped->insert(key, value);
}
if (isScoped) {
- scopedEnums << scoped;
- scopedEnumIndex.insert(QString::fromUtf8(e.name()), scopedEnums.size()-1);
+ enums->scopedEnums << scoped;
+ enums->scopedEnumIndex.insert(QString::fromUtf8(e.name()), enums->scopedEnums.size()-1);
}
}
}
@@ -379,7 +416,7 @@ void QQmlTypePrivate::createEnumConflictReport(const QMetaObject *metaObject, co
}
void QQmlTypePrivate::insertEnumsFromPropertyCache(
- const QQmlPropertyCache::ConstPtr &cache) const
+ Enums *enums, const QQmlPropertyCache::ConstPtr &cache) const
{
const QMetaObject *cppMetaObject = cache->firstCppMetaObject();
@@ -394,20 +431,14 @@ void QQmlTypePrivate::insertEnumsFromPropertyCache(
for (int jj = 0; jj < enumData->values.size(); ++jj) {
const QQmlEnumValue &value = enumData->values.at(jj);
- enums.insert(value.namedValue, value.value);
+ enums->enums.insert(value.namedValue, value.value);
scoped->insert(value.namedValue, value.value);
}
- scopedEnums << scoped;
- scopedEnumIndex.insert(enumData->name, scopedEnums.size()-1);
+ enums->scopedEnums << scoped;
+ enums->scopedEnumIndex.insert(enumData->name, enums->scopedEnums.size()-1);
}
}
- insertEnums(cppMetaObject);
-}
-
-void QQmlTypePrivate::setContainingType(QQmlType *containingType)
-{
- Q_ASSERT(regType == QQmlType::InlineComponentType);
- extraData.id->containingType = containingType->d.data();
+ insertEnums(enums, cppMetaObject);
}
void QQmlTypePrivate::setName(const QString &uri, const QString &element)
@@ -421,11 +452,9 @@ QByteArray QQmlType::typeName() const
{
if (d) {
if (d->regType == SingletonType || d->regType == CompositeSingletonType)
- return d->extraData.sd->singletonInstanceInfo->typeName.toUtf8();
+ return d->extraData.singletonTypeData->singletonInstanceInfo->typeName;
else if (d->baseMetaObject)
return d->baseMetaObject->className();
- else if (d->regType == InlineComponentType)
- return d->extraData.id->inlineComponentName.toUtf8();
}
return QByteArray();
}
@@ -473,13 +502,11 @@ QObject *QQmlType::create(void **memory, size_t additionalMemory) const
if (!d || !isCreatable())
return nullptr;
- d->init();
-
- QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize + additionalMemory);
- d->extraData.cd->newFunc(rv, d->extraData.cd->userdata);
+ QObject *rv = (QObject *)operator new(d->extraData.cppTypeData->allocationSize + additionalMemory);
+ d->extraData.cppTypeData->newFunc(rv, d->extraData.cppTypeData->userdata);
createProxy(rv);
- *memory = ((char *)rv) + d->extraData.cd->allocationSize;
+ *memory = ((char *)rv) + d->extraData.cppTypeData->allocationSize;
return rv;
}
@@ -494,21 +521,20 @@ QObject *QQmlType::createWithQQmlData() const
auto instance = create(&ddataMemory, sizeof(QQmlData));
if (!instance)
return nullptr;
- QQmlData *ddata = new (ddataMemory) QQmlData;
- ddata->ownMemory = false;
QObjectPrivate* p = QObjectPrivate::get(instance);
Q_ASSERT(!p->isDeletingChildren);
- p->declarativeData = ddata;
+ if (!p->declarativeData)
+ p->declarativeData = new (ddataMemory) QQmlData(QQmlData::DoesNotOwnMemory);
return instance;
}
-QQmlType::SingletonInstanceInfo *QQmlType::singletonInstanceInfo() const
+QQmlType::SingletonInstanceInfo::ConstPtr QQmlType::singletonInstanceInfo() const
{
if (!d)
- return nullptr;
+ return {};
if (d->regType != SingletonType && d->regType != CompositeSingletonType)
- return nullptr;
- return d->extraData.sd->singletonInstanceInfo;
+ return {};
+ return d->extraData.singletonTypeData->singletonInstanceInfo;
}
QQmlCustomParser *QQmlType::customParser() const
@@ -517,47 +543,47 @@ QQmlCustomParser *QQmlType::customParser() const
return nullptr;
if (d->regType != CppType)
return nullptr;
- return d->extraData.cd->customParser;
+ return d->extraData.cppTypeData->customParser;
}
QQmlType::CreateValueTypeFunc QQmlType::createValueTypeFunction() const
{
if (!d || d->regType != CppType)
return nullptr;
- return d->extraData.cd->createValueTypeFunc;
+ return d->extraData.cppTypeData->createValueTypeFunc;
}
bool QQmlType::canConstructValueType() const
{
if (!d || d->regType != CppType)
return false;
- return d->extraData.cd->constructValueType;
+ return d->extraData.cppTypeData->constructValueType;
}
bool QQmlType::canPopulateValueType() const
{
if (!d || d->regType != CppType)
return false;
- return d->extraData.cd->populateValueType;
+ return d->extraData.cppTypeData->populateValueType;
}
QQmlType::CreateFunc QQmlType::createFunction() const
{
if (!d || d->regType != CppType)
return nullptr;
- return d->extraData.cd->newFunc;
+ return d->extraData.cppTypeData->newFunc;
}
QString QQmlType::noCreationReason() const
{
if (!d || d->regType != CppType)
return QString();
- return d->extraData.cd->noCreationReason;
+ return d->extraData.cppTypeData->noCreationReason;
}
bool QQmlType::isCreatable() const
{
- return d && d->regType == CppType && d->extraData.cd->newFunc;
+ return d && d->regType == CppType && d->extraData.cppTypeData->newFunc;
}
QQmlType::ExtensionFunc QQmlType::extensionFunction() const
@@ -567,9 +593,9 @@ QQmlType::ExtensionFunc QQmlType::extensionFunction() const
switch (d->regType) {
case CppType:
- return d->extraData.cd->extFunc;
+ return d->extraData.cppTypeData->extFunc;
case SingletonType:
- return d->extraData.sd->extFunc;
+ return d->extraData.singletonTypeData->extFunc;
default:
return nullptr;
}
@@ -582,9 +608,9 @@ const QMetaObject *QQmlType::extensionMetaObject() const
switch (d->regType) {
case CppType:
- return d->extraData.cd->extMetaObject;
+ return d->extraData.cppTypeData->extMetaObject;
case SingletonType:
- return d->extraData.sd->extMetaObject;
+ return d->extraData.singletonTypeData->extMetaObject;
default:
return nullptr;
}
@@ -592,11 +618,7 @@ const QMetaObject *QQmlType::extensionMetaObject() const
bool QQmlType::isExtendedType() const
{
- if (!d)
- return false;
- d->init();
-
- return !d->metaObjects.isEmpty();
+ return d && !d->init()->data.isEmpty();
}
bool QQmlType::isSingleton() const
@@ -625,12 +647,12 @@ bool QQmlType::isCompositeSingleton() const
bool QQmlType::isQObjectSingleton() const
{
- return d && d->regType == SingletonType && d->extraData.sd->singletonInstanceInfo->qobjectCallback;
+ return d && d->regType == SingletonType && d->extraData.singletonTypeData->singletonInstanceInfo->qobjectCallback;
}
bool QQmlType::isQJSValueSingleton() const
{
- return d && d->regType == SingletonType && d->extraData.sd->singletonInstanceInfo->scriptCallback;
+ return d && d->regType == SingletonType && d->extraData.singletonTypeData->singletonInstanceInfo->scriptCallback;
}
bool QQmlType::isSequentialContainer() const
@@ -638,6 +660,11 @@ bool QQmlType::isSequentialContainer() const
return d && d->regType == SequentialContainerType;
}
+bool QQmlType::isValueType() const
+{
+ return d && d->isValueType();
+}
+
QMetaType QQmlType::typeId() const
{
return d ? d->typeId : QMetaType{};
@@ -650,20 +677,18 @@ QMetaType QQmlType::qListTypeId() const
QMetaSequence QQmlType::listMetaSequence() const
{
- return isSequentialContainer() ? *d->extraData.ld : QMetaSequence();
+ return isSequentialContainer() ? d->extraData.sequentialContainerTypeData : QMetaSequence();
}
const QMetaObject *QQmlType::metaObject() const
{
- if (!d)
- return nullptr;
- d->init();
-
- if (d->metaObjects.isEmpty())
- return d->baseMetaObject;
- else
- return d->metaObjects.constFirst().metaObject;
+ return d ? d->metaObject() : nullptr;
+}
+const QMetaObject *QQmlType::metaObjectForValueType() const
+{
+ Q_ASSERT(d);
+ return d->metaObjectForValueType();
}
const QMetaObject *QQmlType::baseMetaObject() const
@@ -673,11 +698,7 @@ const QMetaObject *QQmlType::baseMetaObject() const
bool QQmlType::containsRevisionedAttributes() const
{
- if (!d)
- return false;
- d->init();
-
- return d->containsRevisionedAttributes;
+ return d && d->init()->containsRevisionedAttributes;
}
QTypeRevision QQmlType::metaObjectRevision() const
@@ -688,14 +709,14 @@ QTypeRevision QQmlType::metaObjectRevision() const
QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivate *engine) const
{
if (const QQmlTypePrivate *base = d ? d->attachedPropertiesBase(engine) : nullptr)
- return base->extraData.cd->attachedPropertiesFunc;
+ return base->extraData.cppTypeData->attachedPropertiesFunc;
return nullptr;
}
const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) const
{
if (const QQmlTypePrivate *base = d ? d->attachedPropertiesBase(engine) : nullptr)
- return base->extraData.cd->attachedPropertiesType;
+ return base->extraData.cppTypeData->attachedPropertiesType;
return nullptr;
}
@@ -703,35 +724,35 @@ int QQmlType::parserStatusCast() const
{
if (!d || d->regType != CppType)
return -1;
- return d->extraData.cd->parserStatusCast;
+ return d->extraData.cppTypeData->parserStatusCast;
}
int QQmlType::propertyValueSourceCast() const
{
if (!d || d->regType != CppType)
return -1;
- return d->extraData.cd->propertyValueSourceCast;
+ return d->extraData.cppTypeData->propertyValueSourceCast;
}
int QQmlType::propertyValueInterceptorCast() const
{
if (!d || d->regType != CppType)
return -1;
- return d->extraData.cd->propertyValueInterceptorCast;
+ return d->extraData.cppTypeData->propertyValueInterceptorCast;
}
int QQmlType::finalizerCast() const
{
if (!d || d->regType != CppType)
return -1;
- return d->extraData.cd->finalizerCast;
+ return d->extraData.cppTypeData->finalizerCast;
}
const char *QQmlType::interfaceIId() const
{
if (!d || d->regType != InterfaceType)
return nullptr;
- return d->iid;
+ return d->extraData.interfaceTypeData;
}
int QQmlType::index() const
@@ -743,200 +764,49 @@ bool QQmlType::isInlineComponentType() const {
return d ? d->regType == QQmlType::InlineComponentType : false;
}
-int QQmlType::inlineComponentId() const {
- bool ok = false;
- if (d->regType == QQmlType::RegistrationType::InlineComponentType) {
- Q_ASSERT(d->extraData.id->objectId != -1);
- return d->extraData.id->objectId;
- }
- int subObjectId = sourceUrl().fragment().toInt(&ok);
- return ok ? subObjectId : -1;
-}
-
QUrl QQmlType::sourceUrl() const
{
- auto url = d ? d->sourceUrl() : QUrl();
- if (url.isValid() && d->regType == QQmlType::RegistrationType::InlineComponentType && d->extraData.id->objectId) {
- Q_ASSERT(url.hasFragment());
- url.setFragment(QString::number(inlineComponentId()));
- }
- return url;
+ return d ? d->sourceUrl() : QUrl();
}
int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const
{
- Q_ASSERT(ok);
- if (d) {
- *ok = true;
-
- d->initEnums(engine);
-
- int *rv = d->enums.value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
+ return QQmlTypePrivate::enumValue(d, engine, name, ok);
}
int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name, bool *ok) const
{
- Q_ASSERT(ok);
- if (d) {
- *ok = true;
-
- d->initEnums(engine);
-
- int *rv = d->enums.value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
+ return QQmlTypePrivate::enumValue(d, engine, name, ok);
}
int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const
{
- Q_ASSERT(ok);
- if (d) {
- *ok = true;
-
- d->initEnums(engine);
-
- int *rv = d->enums.value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
+ return QQmlTypePrivate::enumValue(d, engine, name, ok);
}
int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const
{
- Q_ASSERT(ok);
- if (d) {
- *ok = true;
-
- d->initEnums(engine);
-
- int *rv = d->scopedEnumIndex.value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
+ return QQmlTypePrivate::scopedEnumIndex(d, engine, name, ok);
}
int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bool *ok) const
{
- Q_ASSERT(ok);
- if (d) {
- *ok = true;
-
- d->initEnums(engine);
-
- int *rv = d->scopedEnumIndex.value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
+ return QQmlTypePrivate::scopedEnumIndex(d, engine, name, ok);
}
int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *name, bool *ok) const
{
- Q_UNUSED(engine);
- Q_ASSERT(ok);
- *ok = true;
-
- if (d) {
- Q_ASSERT(index > -1 && index < d->scopedEnums.size());
- int *rv = d->scopedEnums.at(index)->value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
+ return QQmlTypePrivate::scopedEnumValue(d, engine, index, name, ok);
}
int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &name, bool *ok) const
{
- Q_UNUSED(engine);
- Q_ASSERT(ok);
- *ok = true;
-
- if (d) {
- Q_ASSERT(index > -1 && index < d->scopedEnums.size());
- int *rv = d->scopedEnums.at(index)->value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
+ return QQmlTypePrivate::scopedEnumValue(d, engine, index, name, ok);
}
-int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scopedEnumName, const QByteArray &name, bool *ok) const
+int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QHashedStringRef &scopedEnumName, const QHashedStringRef &name, bool *ok) const
{
- Q_ASSERT(ok);
- if (d) {
- *ok = true;
-
- d->initEnums(engine);
-
- int *rv = d->scopedEnumIndex.value(QHashedCStringRef(scopedEnumName.constData(), scopedEnumName.size()));
- if (rv) {
- int index = *rv;
- Q_ASSERT(index > -1 && index < d->scopedEnums.size());
- rv = d->scopedEnums.at(index)->value(QHashedCStringRef(name.constData(), name.size()));
- if (rv)
- return *rv;
- }
- }
-
- *ok = false;
- return -1;
-}
-
-int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, QStringView scopedEnumName, QStringView name, bool *ok) const
-{
- Q_ASSERT(ok);
- if (d) {
- *ok = true;
-
- d->initEnums(engine);
-
- int *rv = d->scopedEnumIndex.value(QHashedStringRef(scopedEnumName));
- if (rv) {
- int index = *rv;
- Q_ASSERT(index > -1 && index < d->scopedEnums.size());
- rv = d->scopedEnums.at(index)->value(QHashedStringRef(name));
- if (rv)
- return *rv;
- }
- }
-
- *ok = false;
- return -1;
-}
-
-int QQmlType::inlineComponentObjectId() const
-{
- if (!isInlineComponentType())
- return -1;
- return d->extraData.id->objectId;
-}
-
-void QQmlType::setInlineComponentObjectId(int id) const
-{
- Q_ASSERT(d && d->regType == QQmlType::InlineComponentType);
- d->extraData.id->objectId = id;
+ return QQmlTypePrivate::scopedEnumValue(d, engine, scopedEnumName, name, ok);
}
void QQmlType::refHandle(const QQmlTypePrivate *priv)
@@ -958,72 +828,11 @@ int QQmlType::refCount(const QQmlTypePrivate *priv)
return -1;
}
-int QQmlType::lookupInlineComponentIdByName(const QString &name) const
-{
- Q_ASSERT(d);
- return d->namesToInlineComponentObjectIndex.value(name, -1);
-}
-
-QQmlType QQmlType::containingType() const
-{
- Q_ASSERT(d && d->regType == QQmlType::RegistrationType::InlineComponentType);
- auto ret = QQmlType {d->extraData.id->containingType};
- Q_ASSERT(!ret.isInlineComponentType());
- return ret;
-}
-
-QQmlType QQmlType::lookupInlineComponentById(int objectid) const
-{
- Q_ASSERT(d);
- return d->objectIdToICType.value(objectid, QQmlType(nullptr));
-}
-
-int QQmlType::generatePlaceHolderICId() const
-{
- Q_ASSERT(d);
- int id = -2;
- for (auto it = d->objectIdToICType.keyBegin(); it != d->objectIdToICType.keyEnd(); ++it)
- if (*it < id)
- id = *it;
- return id;
-}
-
-void QQmlType::associateInlineComponent(const QString &name, int objectID, const CompositeMetaTypeIds &metaTypeIds, QQmlType existingType)
-{
- bool const reuseExistingType = existingType.isValid();
- auto priv = reuseExistingType ? const_cast<QQmlTypePrivate *>(existingType.d.data()) : new QQmlTypePrivate { RegistrationType::InlineComponentType } ;
- priv->setName( QString::fromUtf8(typeName()), name);
- auto icUrl = QUrl(sourceUrl());
- icUrl.setFragment(QString::number(objectID));
- priv->extraData.id->url = icUrl;
- priv->extraData.id->containingType = d.data();
- priv->extraData.id->objectId = objectID;
- priv->typeId = metaTypeIds.id;
- priv->listId = metaTypeIds.listId;
- d->namesToInlineComponentObjectIndex.insert(name, objectID);
- QQmlType icType(priv);
- d->objectIdToICType.insert(objectID, icType);
- if (!reuseExistingType)
- priv->release();
-}
-
-void QQmlType::setPendingResolutionName(const QString &name)
-{
- Q_ASSERT(d && d->regType == QQmlType::RegistrationType::InlineComponentType);
- Q_ASSERT(d->extraData.id->inlineComponentName == name|| d->extraData.id->inlineComponentName.isEmpty());
- d->extraData.id->inlineComponentName = name;
-}
-
-QString QQmlType::pendingResolutionName() const
-{
- Q_ASSERT(d && d->regType == QQmlType::RegistrationType::InlineComponentType);
- return d->extraData.id->inlineComponentName;
-}
-
void QQmlType::createProxy(QObject *instance) const
{
- if (!d->metaObjects.isEmpty())
- (void)new QQmlProxyMetaObject(instance, &d->metaObjects);
+ const QQmlTypePrivate::ProxyMetaObjects *proxies = d->init();
+ if (!proxies->data.isEmpty())
+ (void)new QQmlProxyMetaObject(instance, &proxies->data);
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h
index c2c85c9c2e..622e91a4b2 100644
--- a/src/qml/qml/qqmltype_p.h
+++ b/src/qml/qml/qqmltype_p.h
@@ -39,9 +39,8 @@ class QQmlPropertyCache;
namespace QV4 {
struct String;
}
-struct CompositeMetaTypeIds;
-class Q_QML_PRIVATE_EXPORT QQmlType
+class Q_QML_EXPORT QQmlType
{
public:
QQmlType();
@@ -52,10 +51,6 @@ public:
explicit QQmlType(const QQmlTypePrivate *priv);
~QQmlType();
- bool operator ==(const QQmlType &other) const {
- return d.data() == other.d.data();
- }
-
bool isValid() const { return !d.isNull(); }
QByteArray typeName() const;
@@ -97,12 +92,17 @@ public:
bool isQObjectSingleton() const;
bool isQJSValueSingleton() const;
bool isSequentialContainer() const;
+ bool isValueType() const;
QMetaType typeId() const;
QMetaType qListTypeId() const;
QMetaSequence listMetaSequence() const;
const QMetaObject *metaObject() const;
+
+ // Precondition: The type is actually a value type!
+ const QMetaObject *metaObjectForValueType() const;
+
const QMetaObject *baseMetaObject() const;
QTypeRevision metaObjectRevision() const;
bool containsRevisionedAttributes() const;
@@ -119,17 +119,25 @@ public:
int index() const;
bool isInlineComponentType() const;
- int inlineComponentId() const;
- struct Q_QML_PRIVATE_EXPORT SingletonInstanceInfo
+ struct Q_QML_EXPORT SingletonInstanceInfo final
+ : public QQmlRefCounted<SingletonInstanceInfo>
{
+ using Ptr = QQmlRefPointer<SingletonInstanceInfo>;
+ using ConstPtr = QQmlRefPointer<const SingletonInstanceInfo>;
+
+ static Ptr create() { return Ptr(new SingletonInstanceInfo, Ptr::Adopt); }
+
std::function<QJSValue(QQmlEngine *, QJSEngine *)> scriptCallback = {};
std::function<QObject *(QQmlEngine *, QJSEngine *)> qobjectCallback = {};
- const QMetaObject *instanceMetaObject = nullptr;
- QString typeName;
+ QByteArray typeName;
QUrl url; // used by composite singletons
+
+ private:
+ Q_DISABLE_COPY_MOVE(SingletonInstanceInfo)
+ SingletonInstanceInfo() = default;
};
- SingletonInstanceInfo *singletonInstanceInfo() const;
+ SingletonInstanceInfo::ConstPtr singletonInstanceInfo() const;
QUrl sourceUrl() const;
@@ -141,10 +149,7 @@ public:
int scopedEnumIndex(QQmlEnginePrivate *engine, const QString &, bool *ok) const;
int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *, bool *ok) const;
int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &, bool *ok) const;
- int scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &, const QByteArray &, bool *ok) const;
- int scopedEnumValue(QQmlEnginePrivate *engine, QStringView, QStringView, bool *ok) const;
- int inlineComponentObjectId() const;
- void setInlineComponentObjectId(int id) const; // TODO: const setters are BAD
+ int scopedEnumValue(QQmlEnginePrivate *engine, const QHashedStringRef &, const QHashedStringRef &, bool *ok) const;
const QQmlTypePrivate *priv() const { return d.data(); }
static void refHandle(const QQmlTypePrivate *priv);
@@ -162,20 +167,20 @@ public:
AnyRegistrationType = 255
};
- QQmlType containingType() const;
- int lookupInlineComponentIdByName(const QString &name) const;
- QQmlType lookupInlineComponentById(int objectid) const;
- int generatePlaceHolderICId() const;
-
- void associateInlineComponent(const QString &name, int objectID, const CompositeMetaTypeIds &metaTypeIds, QQmlType existingType);
- void setPendingResolutionName(const QString &name);
- QString pendingResolutionName() const;
-
void createProxy(QObject *instance) const;
private:
friend class QQmlTypePrivate;
friend size_t qHash(const QQmlType &t, size_t seed);
+ friend bool operator==(const QQmlType &a, const QQmlType &b) noexcept
+ {
+ return a.d.data() == b.d.data();
+ }
+ friend bool operator!=(const QQmlType &a, const QQmlType &b) noexcept
+ {
+ return !(a == b);
+ }
+
QQmlRefPointer<const QQmlTypePrivate> d;
};
diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h
index d2b282955f..2bf83ddb8b 100644
--- a/src/qml/qml/qqmltype_p_p.h
+++ b/src/qml/qml/qqmltype_p_p.h
@@ -21,32 +21,52 @@
#include <private/qqmlrefcount_p.h>
#include <private/qqmlpropertycache_p.h>
#include <private/qqmlmetatype_p.h>
+#include <private/qqmltypeloader_p.h>
+#include <private/qv4executablecompilationunit_p.h>
+#include <private/qv4engine_p.h>
#include <QAtomicInteger>
QT_BEGIN_NAMESPACE
-class QQmlTypePrivate : public QQmlRefCount
+class QQmlTypePrivate final : public QQmlRefCounted<QQmlTypePrivate>
{
Q_DISABLE_COPY_MOVE(QQmlTypePrivate)
public:
+ struct ProxyMetaObjects
+ {
+ ~ProxyMetaObjects()
+ {
+ for (const QQmlProxyMetaObject::ProxyData &metaObject : data)
+ free(metaObject.metaObject);
+ }
+
+ QList<QQmlProxyMetaObject::ProxyData> data;
+ bool containsRevisionedAttributes = false;
+ };
+
+ struct Enums
+ {
+ ~Enums() { qDeleteAll(scopedEnums); }
+
+ QStringHash<int> enums;
+ QStringHash<int> scopedEnumIndex; // maps from enum name to index in scopedEnums
+ QList<QStringHash<int> *> scopedEnums;
+ };
+
QQmlTypePrivate(QQmlType::RegistrationType type);
- void init() const;
- void initEnums(QQmlEnginePrivate *engine) const;
- void insertEnums(const QMetaObject *metaObject) const;
- void insertEnumsFromPropertyCache(const QQmlPropertyCache::ConstPtr &cache) const;
- void setContainingType(QQmlType *containingType);
+ const ProxyMetaObjects *init() const;
QUrl sourceUrl() const
{
switch (regType) {
case QQmlType::CompositeType:
- return extraData.fd->url;
+ return extraData.compositeTypeData;
case QQmlType::CompositeSingletonType:
- return extraData.sd->singletonInstanceInfo->url;
+ return extraData.singletonTypeData->singletonInstanceInfo->url;
case QQmlType::InlineComponentType:
- return extraData.id->url;
+ return extraData.inlineComponentTypeData;
default:
return QUrl();
}
@@ -56,7 +76,7 @@ public:
{
for (const QQmlTypePrivate *d = this; d; d = d->resolveCompositeBaseType(engine).d.data()) {
if (d->regType == QQmlType::CppType)
- return d->extraData.cd->attachedPropertiesType ? d : nullptr;
+ return d->extraData.cppTypeData->attachedPropertiesType ? d : nullptr;
if (d->regType != QQmlType::CompositeType)
return nullptr;
@@ -69,11 +89,14 @@ public:
return regType == QQmlType::CompositeType || regType == QQmlType::CompositeSingletonType;
}
+ bool isValueType() const
+ {
+ return regType == QQmlType::CppType && !(typeId.flags() & QMetaType::PointerToQObject);
+ }
+
QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const;
QQmlPropertyCache::ConstPtr compositePropertyCache(QQmlEnginePrivate *engine) const;
- QQmlType::RegistrationType regType;
-
struct QQmlCppTypeData
{
int allocationSize;
@@ -98,66 +121,146 @@ public:
struct QQmlSingletonTypeData
{
- QQmlType::SingletonInstanceInfo *singletonInstanceInfo;
+ QQmlType::SingletonInstanceInfo::ConstPtr singletonInstanceInfo;
QObject *(*extFunc)(QObject *);
const QMetaObject *extMetaObject;
};
- struct QQmlCompositeTypeData
- {
- QUrl url;
- };
-
- struct QQmlInlineTypeData
- {
- QUrl url = QUrl();
- // The containing type stores a pointer to the inline component type
- // Using QQmlType here would create a reference cycle
- // As the inline component type cannot outlive the containing type
- // this should still be fine
- QQmlTypePrivate const * containingType = nullptr;
- QString inlineComponentName = QString();
- int objectId = -1;
- };
-
- using QQmlSequenceTypeData = QMetaSequence;
+ int index = -1;
union extraData {
- QQmlCppTypeData* cd;
- QQmlSingletonTypeData* sd;
- QQmlCompositeTypeData* fd;
- QQmlInlineTypeData* id;
- QQmlSequenceTypeData* ld;
+ extraData() {} // QQmlTypePrivate() does the actual construction.
+ ~extraData() {} // ~QQmlTypePrivate() does the actual destruction.
+
+ QQmlCppTypeData *cppTypeData;
+ QQmlSingletonTypeData *singletonTypeData;
+ QUrl compositeTypeData;
+ QUrl inlineComponentTypeData;
+ QMetaSequence sequentialContainerTypeData;
+ const char *interfaceTypeData;
} extraData;
+ static_assert(sizeof(extraData) == sizeof(void *));
- const char *iid;
QHashedString module;
QString name;
QString elementName;
QMetaType typeId;
QMetaType listId;
+ QQmlType::RegistrationType regType;
QTypeRevision version;
- QTypeRevision revision;
- mutable bool containsRevisionedAttributes;
- mutable QQmlType superType;
- const QMetaObject *baseMetaObject;
-
- int index;
- mutable QAtomicInteger<bool> isSetup;
- mutable QAtomicInteger<bool> isEnumFromCacheSetup;
- mutable QAtomicInteger<bool> isEnumFromBaseSetup;
- mutable bool haveSuperType;
- mutable QList<QQmlProxyMetaObject::ProxyData> metaObjects;
- mutable QStringHash<int> enums;
- mutable QStringHash<int> scopedEnumIndex; // maps from enum name to index in scopedEnums
- mutable QList<QStringHash<int>*> scopedEnums;
+ QTypeRevision revision = QTypeRevision::zero();
+ const QMetaObject *baseMetaObject = nullptr;
void setName(const QString &uri, const QString &element);
- mutable QHash<QString, int> namesToInlineComponentObjectIndex;
- mutable QHash<int, QQmlType> objectIdToICType;
+
+ template<typename String>
+ static int enumValue(
+ const QQmlRefPointer<const QQmlTypePrivate> &d, QQmlEnginePrivate *engine,
+ const String &name, bool *ok)
+ {
+ return doGetEnumValue(d, engine, [&](const QQmlTypePrivate::Enums *enums) {
+ return enums->enums.value(name);
+ }, ok);
+ }
+
+ template<typename String>
+ static int scopedEnumIndex(
+ const QQmlRefPointer<const QQmlTypePrivate> &d, QQmlEnginePrivate *engine,
+ const String &name, bool *ok)
+ {
+ return doGetEnumValue(d, engine, [&](const QQmlTypePrivate::Enums *enums) {
+ return enums->scopedEnumIndex.value(name);
+ }, ok);
+ }
+
+ template<typename String>
+ static int scopedEnumValue(
+ const QQmlRefPointer<const QQmlTypePrivate> &d, QQmlEnginePrivate *engine, int index,
+ const String &name, bool *ok)
+ {
+ return doGetEnumValue(d, engine, [&](const QQmlTypePrivate::Enums *enums) {
+ Q_ASSERT(index > -1 && index < enums->scopedEnums.size());
+ return enums->scopedEnums.at(index)->value(name);
+ }, ok);
+ }
+
+ template<typename String1, typename String2>
+ static int scopedEnumValue(
+ const QQmlRefPointer<const QQmlTypePrivate> &d, QQmlEnginePrivate *engine,
+ const String1 &scopedEnumName, const String2 &name, bool *ok)
+ {
+ return doGetEnumValue(d, engine, [&](const QQmlTypePrivate::Enums *enums) -> const int * {
+ const int *rv = enums->scopedEnumIndex.value(scopedEnumName);
+ if (!rv)
+ return nullptr;
+
+ const int index = *rv;
+ Q_ASSERT(index > -1 && index < enums->scopedEnums.size());
+ return enums->scopedEnums.at(index)->value(name);
+ }, ok);
+ }
+
+ const QMetaObject *metaObject() const
+ {
+ if (isValueType())
+ return metaObjectForValueType();
+
+ const QQmlTypePrivate::ProxyMetaObjects *proxies = init();
+ return proxies->data.isEmpty()
+ ? baseMetaObject
+ : proxies->data.constFirst().metaObject;
+ }
+
+ const QMetaObject *metaObjectForValueType() const
+ {
+ Q_ASSERT(isValueType());
+
+ // Prefer the extension meta object, if any.
+ // Extensions allow registration of non-gadget value types.
+ if (const QMetaObject *extensionMetaObject = extraData.cppTypeData->extMetaObject) {
+ // This may be a namespace even if the original metaType isn't.
+ // You can do such things with QML_FOREIGN declarations.
+ if (extensionMetaObject->metaType().flags() & QMetaType::IsGadget)
+ return extensionMetaObject;
+ }
+
+ if (baseMetaObject) {
+ // This may be a namespace even if the original metaType isn't.
+ // You can do such things with QML_FOREIGN declarations.
+ if (baseMetaObject->metaType().flags() & QMetaType::IsGadget)
+ return baseMetaObject;
+ }
+
+ return nullptr;
+ }
+
+ static QQmlType compositeQmlType(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit,
+ QQmlTypeLoader *typeLoader, const QString &type)
+ {
+ Q_ASSERT(typeLoader);
+
+ const QQmlType qmltype
+ = unit->typeNameCache->query<QQmlImport::AllowRecursion>(type, typeLoader).type;
+ if (!qmltype.isValid())
+ return qmltype;
+
+ if (qmltype.isInlineComponentType()
+ && !QQmlMetaType::obtainCompilationUnit(qmltype.typeId())) {
+ // If it seems to be an IC type, make sure there is an actual
+ // compilation unit for it. We create inline component types speculatively.
+ return QQmlType();
+ }
+
+ return qmltype;
+ }
private:
- ~QQmlTypePrivate() override;
+ mutable QAtomicPointer<const ProxyMetaObjects> proxyMetaObjects;
+ mutable QAtomicPointer<const Enums> enums;
+
+ ~QQmlTypePrivate();
+ friend class QQmlRefCounted<QQmlTypePrivate>;
struct EnumInfo {
QStringList path;
@@ -168,6 +271,29 @@ private:
bool scoped;
};
+ template<typename Op>
+ static int doGetEnumValue(
+ const QQmlRefPointer<const QQmlTypePrivate> &d, QQmlEnginePrivate *engine,
+ Op &&op, bool *ok)
+ {
+ Q_ASSERT(ok);
+ if (d) {
+ if (const QQmlTypePrivate::Enums *enums = d->initEnums(engine)) {
+ if (const int *rv = op(enums)) {
+ *ok = true;
+ return *rv;
+ }
+ }
+ }
+
+ *ok = false;
+ return -1;
+ }
+
+ const Enums *initEnums(QQmlEnginePrivate *engine) const;
+ void insertEnums(Enums *enums, const QMetaObject *metaObject) const;
+ void insertEnumsFromPropertyCache(Enums *enums, const QQmlPropertyCache::ConstPtr &cache) const;
+
void createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const;
void createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const;
};
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp
index 203a004765..94e6e8f351 100644
--- a/src/qml/qml/qqmltypecompiler.cpp
+++ b/src/qml/qml/qqmltypecompiler.cpp
@@ -8,6 +8,8 @@
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlcomponent_p.h>
#include <private/qqmlpropertyresolver_p.h>
+#include <private/qqmlcomponentandaliasresolver_p.h>
+#include <private/qqmlsignalnames_p.h>
#define COMPILE_EXCEPTION(token, desc) \
{ \
@@ -17,21 +19,24 @@
QT_BEGIN_NAMESPACE
+DEFINE_BOOL_CONFIG_OPTION(
+ disableInternalDeferredProperties, QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES);
+
Q_LOGGING_CATEGORY(lcQmlTypeCompiler, "qt.qml.typecompiler");
-QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData,
- QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
+QQmlTypeCompiler::QQmlTypeCompiler(
+ QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *parsedQML,
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
+ const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
: resolvedTypes(resolvedTypeCache)
, engine(engine)
, dependencyHasher(dependencyHasher)
, document(parsedQML)
- , typeNameCache(typeNameCache)
, typeData(typeData)
{
}
-QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile()
{
// Build property caches and VME meta object data
@@ -63,9 +68,11 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
} else {
// Resolve component boundaries and aliases
- QQmlComponentAndAliasResolver resolver(this);
- if (!resolver.resolve(result.processedRoot))
+ QQmlComponentAndAliasResolver resolver(this, enginePrivate(), &m_propertyCaches);
+ if (QQmlError error = resolver.resolve(result.processedRoot); error.isValid()) {
+ recordError(error);
return nullptr;
+ }
pendingGroupPropertyBindings.resolveMissingPropertyCaches(&m_propertyCaches);
pendingGroupPropertyBindings.clear(); // anything that can be processed is now processed
}
@@ -105,7 +112,7 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
return nullptr;
}
- if (!document->javaScriptCompilationUnit.unitData()) {
+ if (!document->javaScriptCompilationUnit || !document->javaScriptCompilationUnit->unitData()) {
// Compile JS binding expressions and signal handlers if necessary
{
// We can compile script strings ahead of time, but they must be compiled
@@ -135,14 +142,7 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
if (!errors.isEmpty())
return nullptr;
- QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit
- = QV4::ExecutableCompilationUnit::create(std::move(
- document->javaScriptCompilationUnit));
- compilationUnit->typeNameCache = typeNameCache;
- compilationUnit->resolvedTypes = *resolvedTypes;
- compilationUnit->propertyCaches = std::move(m_propertyCaches);
- Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->objectCount()));
- return compilationUnit;
+ return std::move(document->javaScriptCompilationUnit);
}
void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description)
@@ -189,7 +189,7 @@ int QQmlTypeCompiler::registerConstant(QV4::ReturnedValue v)
const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const
{
- return document->javaScriptCompilationUnit.unitData();
+ return document->javaScriptCompilationUnit->unitData();
}
const QQmlImports *QQmlTypeCompiler::imports() const
@@ -202,10 +202,9 @@ QVector<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects() const
return &document->objects;
}
-void QQmlTypeCompiler::setPropertyCaches(QQmlPropertyCacheVector &&caches)
+QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches()
{
- m_propertyCaches = std::move(caches);
- Q_ASSERT(m_propertyCaches.count() > 0);
+ return &m_propertyCaches;
}
const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const
@@ -213,11 +212,6 @@ const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const
return &m_propertyCaches;
}
-QQmlPropertyCacheVector &&QQmlTypeCompiler::takePropertyCaches()
-{
- return std::move(m_propertyCaches);
-}
-
QQmlJS::MemoryPool *QQmlTypeCompiler::memoryPool()
{
return document->jsParserEngine.pool();
@@ -259,9 +253,9 @@ void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier
document->imports.append(import);
}
-CompositeMetaTypeIds QQmlTypeCompiler::typeIdsForComponent(int objectId) const
+QQmlType QQmlTypeCompiler::qmlTypeForComponent(const QString &inlineComponentName) const
{
- return typeData->typeIds(objectId);
+ return typeData->qmlType(inlineComponentName);
}
QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler)
@@ -313,8 +307,11 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions(
const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex);
auto *typeRef = resolvedType(binding->propertyNameIndex);
QQmlType type = typeRef ? typeRef->type() : QQmlType();
- if (!type.isValid())
- imports->resolveType(bindingPropertyName, &type, nullptr, nullptr, nullptr);
+ if (!type.isValid()) {
+ imports->resolveType(
+ QQmlTypeLoader::get(enginePrivate), bindingPropertyName, &type, nullptr,
+ nullptr);
+ }
const QMetaObject *attachedType = type.attachedPropertiesType(enginePrivate);
if (!attachedType)
@@ -325,18 +322,21 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions(
continue;
}
- if (!QmlIR::IRBuilder::isSignalPropertyName(bindingPropertyName))
+ QString qPropertyName;
+ QString signalName;
+ if (auto propertyName =
+ QQmlSignalNames::changedHandlerNameToPropertyName(bindingPropertyName)) {
+ qPropertyName = *propertyName;
+ signalName = *QQmlSignalNames::changedHandlerNameToSignalName(bindingPropertyName);
+ } else {
+ signalName = QQmlSignalNames::handlerNameToSignalName(bindingPropertyName)
+ .value_or(QString());
+ }
+ if (signalName.isEmpty())
continue;
QQmlPropertyResolver resolver(propertyCache);
- const QString signalName = QmlIR::IRBuilder::signalNameFromSignalPropertyName(
- bindingPropertyName);
-
- QString qPropertyName;
- if (signalName.endsWith(QLatin1String("Changed")))
- qPropertyName = signalName.mid(0, signalName.size() - static_cast<int>(strlen("Changed")));
-
bool notInRevision = false;
const QQmlPropertyData * const signal = resolver.signal(signalName, &notInRevision);
const QQmlPropertyData * const signalPropertyData = resolver.property(signalName, /*notInRevision ptr*/nullptr);
@@ -545,7 +545,8 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(
return true;
}
QQmlType type;
- imports->resolveType(typeName, &type, nullptr, nullptr, nullptr);
+ imports->resolveType(
+ QQmlTypeLoader::get(compiler->enginePrivate()), typeName, &type, nullptr, nullptr);
if (!type.isValid() && !isQtObject)
return true;
@@ -607,7 +608,8 @@ int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, QStringView enumNam
if (scope != QLatin1String("Qt")) {
QQmlType type;
- imports->resolveType(scope, &type, nullptr, nullptr, nullptr);
+ imports->resolveType(
+ QQmlTypeLoader::get(compiler->enginePrivate()), scope, &type, nullptr, nullptr);
if (!type.isValid())
return -1;
if (!enumName.isEmpty())
@@ -730,357 +732,99 @@ void QQmlScriptStringScanner::scan()
}
}
-QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler)
- : QQmlCompilePass(typeCompiler)
- , enginePrivate(typeCompiler->enginePrivate())
- , pool(typeCompiler->memoryPool())
- , qmlObjects(typeCompiler->qmlObjects())
- , propertyCaches(std::move(typeCompiler->takePropertyCaches()))
+template<>
+void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::allocateNamedObjects(
+ QmlIR::Object *object) const
{
+ object->namedObjectsInComponent.allocate(m_compiler->memoryPool(), m_idToObjectIndex);
}
-static bool isUsableComponent(const QMetaObject *metaObject)
+template<>
+bool QQmlComponentAndAliasResolver<QQmlTypeCompiler>::markAsComponent(int index) const
{
- // The metaObject is a component we're interested in if it either is a QQmlComponent itself
- // or if any of its parents is a QQmlAbstractDelegateComponent. We don't want to include
- // qqmldelegatecomponent_p.h because it belongs to QtQmlModels.
-
- if (metaObject == &QQmlComponent::staticMetaObject)
- return true;
-
- for (; metaObject; metaObject = metaObject->superClass()) {
- if (qstrcmp(metaObject->className(), "QQmlAbstractDelegateComponent") == 0)
- return true;
- }
-
- return false;
+ m_compiler->qmlObjects()->at(index)->flags |= QV4::CompiledData::Object::IsComponent;
+ return true;
}
-void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(
- const QmlIR::Object *obj, const QQmlPropertyCache::ConstPtr &propertyCache)
+template<>
+void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::setObjectId(int index) const
{
- QQmlPropertyResolver propertyResolver(propertyCache);
-
- const QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
-
- for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type() != QV4::CompiledData::Binding::Type_Object)
- continue;
- if (binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerObject))
- continue;
-
- const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex);
- auto *tr = resolvedType(targetObject->inheritedTypeNameIndex);
- Q_ASSERT(tr);
-
- const QMetaObject *firstMetaObject = nullptr;
- const auto type = tr->type();
- if (type.isValid())
- firstMetaObject = type.metaObject();
- else if (const auto compilationUnit = tr->compilationUnit())
- firstMetaObject = compilationUnit->rootPropertyCache()->firstCppMetaObject();
- if (isUsableComponent(firstMetaObject))
- continue;
- // if here, not a QQmlComponent, so needs wrapping
-
- const QQmlPropertyData *pd = nullptr;
- if (binding->propertyNameIndex != quint32(0)) {
- bool notInRevision = false;
- pd = propertyResolver.property(stringAt(binding->propertyNameIndex), &notInRevision);
- } else {
- pd = defaultProperty;
- }
- if (!pd || !pd->isQObject())
- continue;
-
- // If the version is given, use it and look up by QQmlType.
- // Otherwise, make sure we look up by metaobject.
- // TODO: Is this correct?
- QQmlPropertyCache::ConstPtr pc = pd->typeVersion().hasMinorVersion()
- ? QQmlMetaType::rawPropertyCacheForType(pd->propType(), pd->typeVersion())
- : QQmlMetaType::rawPropertyCacheForType(pd->propType());
- const QMetaObject *mo = pc ? pc->firstCppMetaObject() : nullptr;
- while (mo) {
- if (mo == &QQmlComponent::staticMetaObject)
- break;
- mo = mo->superClass();
- }
-
- if (!mo)
- continue;
-
- // emulate "import QML 1.0" and then wrap the component in "QML.Component {}"
- QQmlType componentType = QQmlMetaType::qmlType(
- &QQmlComponent::staticMetaObject, QStringLiteral("QML"),
- QTypeRevision::fromVersion(1, 0));
- Q_ASSERT(componentType.isValid());
- const QString qualifier = QStringLiteral("QML");
-
- compiler->addImport(componentType.module(), qualifier, componentType.version());
-
- QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>();
- syntheticComponent->init(
- pool,
- compiler->registerString(qualifier + QLatin1Char('.') + componentType.elementName()),
- compiler->registerString(QString()), binding->valueLocation);
- syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent;
-
- if (!containsResolvedType(syntheticComponent->inheritedTypeNameIndex)) {
- auto typeRef = new QV4::ResolvedTypeReference;
- typeRef->setType(componentType);
- typeRef->setVersion(componentType.version());
- insertResolvedType(syntheticComponent->inheritedTypeNameIndex, typeRef);
- }
-
- qmlObjects->append(syntheticComponent);
- const int componentIndex = qmlObjects->size() - 1;
- // Keep property caches symmetric
- QQmlPropertyCache::ConstPtr componentCache
- = QQmlMetaType::propertyCache(&QQmlComponent::staticMetaObject);
- propertyCaches.append(componentCache);
-
- QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>();
- *syntheticBinding = *binding;
-
- // The synthetic binding inside Component has no name. It's just "Component { Foo {} }".
- syntheticBinding->propertyNameIndex = 0;
-
- syntheticBinding->setType(QV4::CompiledData::Binding::Type_Object);
- QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false);
- Q_ASSERT(error.isEmpty());
- Q_UNUSED(error);
-
- binding->value.objectIndex = componentIndex;
-
- componentRoots.append(componentIndex);
- }
+ m_compiler->qmlObjects()->at(index)->id = m_idToObjectIndex.size();
}
-// Resolve ignores everything relating to inline components, except for implicit components.
-bool QQmlComponentAndAliasResolver::resolve(int root)
+template<>
+bool QQmlComponentAndAliasResolver<QQmlTypeCompiler>::wrapImplicitComponent(QmlIR::Binding *binding)
{
- // Detect real Component {} objects as well as implicitly defined components, such as
- // someItemDelegate: Item {}
- // In the implicit case Item is surrounded by a synthetic Component {} because the property
- // on the left hand side is of QQmlComponent type.
- const int objCountWithoutSynthesizedComponents = qmlObjects->size();
- const int startObjectIndex = root == 0 ? root : root+1; // root+1, as ic root is handled at the end
- for (int i = startObjectIndex; i < objCountWithoutSynthesizedComponents; ++i) {
- QmlIR::Object *obj = qmlObjects->at(i);
- const bool isInlineComponentRoot
- = obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot;
- const bool isPartOfInlineComponent
- = obj->flags & QV4::CompiledData::Object::IsPartOfInlineComponent;
- QQmlPropertyCache::ConstPtr cache = propertyCaches.at(i);
-
- bool isExplicitComponent = false;
- if (obj->inheritedTypeNameIndex) {
- auto *tref = resolvedType(obj->inheritedTypeNameIndex);
- Q_ASSERT(tref);
- if (tref->type().metaObject() == &QQmlComponent::staticMetaObject)
- isExplicitComponent = true;
- }
-
- if (isInlineComponentRoot && isExplicitComponent) {
- qCWarning(lcQmlTypeCompiler).nospace().noquote()
- << compiler->url().toString() << ":" << obj->location.line() << ":"
- << obj->location.column()
- << ": Using a Component as the root of an inline component is deprecated: "
- "inline components are "
- "automatically wrapped into Components when needed.";
- }
-
- if (root == 0) {
- // normal component root, skip over anything inline component related
- if (isInlineComponentRoot || isPartOfInlineComponent)
- continue;
- } else if (!isPartOfInlineComponent || isInlineComponentRoot) {
- // We've left the current inline component (potentially entered a new one),
- // but we still need to resolve implicit components which are part of inline components.
- if (cache && !isExplicitComponent)
- findAndRegisterImplicitComponents(obj, cache);
- break;
- }
-
- if (obj->inheritedTypeNameIndex == 0 && !cache)
- continue;
-
- if (!isExplicitComponent) {
- if (cache)
- findAndRegisterImplicitComponents(obj, cache);
- continue;
- }
-
- obj->flags |= QV4::CompiledData::Object::IsComponent;
-
- // check if this object is the root
- if (i == 0) {
- if (isExplicitComponent)
- qCWarning(lcQmlTypeCompiler).nospace().noquote()
- << compiler->url().toString() << ":" << obj->location.line() << ":"
- << obj->location.column()
- << ": Using a Component as the root of a qmldocument is deprecated: types "
- "defined in qml documents are "
- "automatically wrapped into Components when needed.";
- }
-
- if (obj->functionCount() > 0)
- COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions."));
- if (obj->propertyCount() > 0 || obj->aliasCount() > 0)
- COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties."));
- if (obj->signalCount() > 0)
- COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals."));
-
- if (obj->bindingCount() == 0)
- COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification"));
-
- const QmlIR::Binding *rootBinding = obj->firstBinding();
-
- for (const QmlIR::Binding *b = rootBinding; b; b = b->next) {
- if (b->propertyNameIndex != 0)
- COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id"));
- }
-
- if (rootBinding->next || rootBinding->type() != QV4::CompiledData::Binding::Type_Object)
- COMPILE_EXCEPTION(obj, tr("Invalid component body specification"));
-
- // For the root object, we are going to collect ids/aliases and resolve them for as a separate
- // last pass.
- if (i != 0)
- componentRoots.append(i);
-
+ QQmlJS::MemoryPool *pool = m_compiler->memoryPool();
+ QVector<QmlIR::Object *> *qmlObjects = m_compiler->qmlObjects();
+
+ // emulate "import QML 1.0" and then wrap the component in "QML.Component {}"
+ QQmlType componentType = QQmlMetaType::qmlType(
+ &QQmlComponent::staticMetaObject, QStringLiteral("QML"),
+ QTypeRevision::fromVersion(1, 0));
+ Q_ASSERT(componentType.isValid());
+ const QString qualifier = QStringLiteral("QML");
+
+ m_compiler->addImport(componentType.module(), qualifier, componentType.version());
+
+ QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>();
+ syntheticComponent->init(
+ pool,
+ m_compiler->registerString(
+ qualifier + QLatin1Char('.') + componentType.elementName()),
+ m_compiler->registerString(QString()), binding->valueLocation);
+ syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent;
+
+ if (!m_compiler->resolvedTypes->contains(syntheticComponent->inheritedTypeNameIndex)) {
+ auto typeRef = new QV4::ResolvedTypeReference;
+ typeRef->setType(componentType);
+ typeRef->setVersion(componentType.version());
+ m_compiler->resolvedTypes->insert(syntheticComponent->inheritedTypeNameIndex, typeRef);
}
- for (int i = 0; i < componentRoots.size(); ++i) {
- QmlIR::Object *component = qmlObjects->at(componentRoots.at(i));
- const QmlIR::Binding *rootBinding = component->firstBinding();
+ qmlObjects->append(syntheticComponent);
+ const int componentIndex = qmlObjects->size() - 1;
+ // Keep property caches symmetric
+ QQmlPropertyCache::ConstPtr componentCache
+ = QQmlMetaType::propertyCache(&QQmlComponent::staticMetaObject);
+ m_propertyCaches->append(componentCache);
- _idToObjectIndex.clear();
+ QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>();
+ *syntheticBinding = *binding;
- _objectsWithAliases.clear();
+ // The synthetic binding inside Component has no name. It's just "Component { Foo {} }".
+ syntheticBinding->propertyNameIndex = 0;
- if (!collectIdsAndAliases(rootBinding->value.objectIndex))
- return false;
+ syntheticBinding->setType(QV4::CompiledData::Binding::Type_Object);
+ QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false);
+ Q_ASSERT(error.isEmpty());
+ Q_UNUSED(error);
- component->namedObjectsInComponent.allocate(pool, _idToObjectIndex);
-
- if (!resolveAliases(componentRoots.at(i)))
- return false;
- }
-
- // Collect ids and aliases for root
- _idToObjectIndex.clear();
- _objectsWithAliases.clear();
-
- collectIdsAndAliases(root);
-
- QmlIR::Object *rootComponent = qmlObjects->at(root);
- rootComponent->namedObjectsInComponent.allocate(pool, _idToObjectIndex);
-
- if (!resolveAliases(root))
- return false;
-
- // Implicit component insertion may have added objects and thus we also need
- // to extend the symmetric propertyCaches.
- compiler->setPropertyCaches(std::move(propertyCaches));
- compiler->setComponentRoots(componentRoots);
+ binding->value.objectIndex = componentIndex;
+ m_componentRoots.append(componentIndex);
return true;
}
-bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
+template<>
+void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveGeneralizedGroupProperty(
+ const CompiledObject &component, CompiledBinding *binding)
{
- QmlIR::Object *obj = qmlObjects->at(objectIndex);
-
- if (obj->idNameIndex != 0) {
- if (_idToObjectIndex.contains(obj->idNameIndex)) {
- recordError(obj->locationOfIdProperty, tr("id is not unique"));
- return false;
- }
- obj->id = _idToObjectIndex.size();
- _idToObjectIndex.insert(obj->idNameIndex, objectIndex);
- }
-
- if (obj->aliasCount() > 0)
- _objectsWithAliases.append(objectIndex);
-
- // Stop at Component boundary
- if (obj->flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0)
- return true;
-
- for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- switch (binding->type()) {
- case QV4::CompiledData::Binding::Type_Object:
- case QV4::CompiledData::Binding::Type_AttachedProperty:
- case QV4::CompiledData::Binding::Type_GroupProperty:
- if (!collectIdsAndAliases(binding->value.objectIndex))
- return false;
- break;
- default:
- break;
- }
- }
-
- return true;
+ Q_UNUSED(component);
+ // We cannot make it fail here. It might be a custom-parsed property
+ const int targetObjectIndex = m_idToObjectIndex.value(binding->propertyNameIndex, -1);
+ if (targetObjectIndex != -1)
+ m_propertyCaches->set(binding->value.objectIndex, m_propertyCaches->at(targetObjectIndex));
}
-bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
+template<>
+typename QQmlComponentAndAliasResolver<QQmlTypeCompiler>::AliasResolutionResult
+QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveAliasesInObject(
+ const CompiledObject &component, int objectIndex, QQmlError *error)
{
- if (_objectsWithAliases.isEmpty())
- return true;
-
- QQmlPropertyCacheAliasCreator<QQmlTypeCompiler> aliasCacheCreator(&propertyCaches, compiler);
-
- bool atLeastOneAliasResolved;
- do {
- atLeastOneAliasResolved = false;
- QVector<int> pendingObjects;
-
- for (int objectIndex: std::as_const(_objectsWithAliases)) {
-
- QQmlError error;
- const auto result = resolveAliasesInObject(objectIndex, &error);
-
- if (error.isValid()) {
- recordError(error);
- return false;
- }
-
- if (result == AllAliasesResolved) {
- QQmlError error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex, enginePrivate);
- if (error.isValid()) {
- recordError(error);
- return false;
- }
- atLeastOneAliasResolved = true;
- } else if (result == SomeAliasesResolved) {
- atLeastOneAliasResolved = true;
- pendingObjects.append(objectIndex);
- } else {
- pendingObjects.append(objectIndex);
- }
- }
- qSwap(_objectsWithAliases, pendingObjects);
- } while (!_objectsWithAliases.isEmpty() && atLeastOneAliasResolved);
-
- if (!atLeastOneAliasResolved && !_objectsWithAliases.isEmpty()) {
- const QmlIR::Object *obj = qmlObjects->at(_objectsWithAliases.first());
- for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
- if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved)) {
- recordError(alias->location, tr("Circular alias reference detected"));
- return false;
- }
- }
- }
-
- return true;
-}
+ Q_UNUSED(component);
-QQmlComponentAndAliasResolver::AliasResolutionResult
-QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
- QQmlError *error)
-{
- const QmlIR::Object * const obj = qmlObjects->at(objectIndex);
+ const QmlIR::Object * const obj = m_compiler->objectAt(objectIndex);
if (!obj->aliasCount())
return AllAliasesResolved;
@@ -1094,15 +838,15 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
seenUnresolvedAlias = true;
const int idIndex = alias->idIndex();
- const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1);
+ const int targetObjectIndex = m_idToObjectIndex.value(idIndex, -1);
if (targetObjectIndex == -1) {
*error = qQmlCompileError(
alias->referenceLocation,
- tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex)));
+ QQmlComponentAndAliasResolverBase::tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex)));
break;
}
- const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex);
+ const QmlIR::Object *targetObject = m_compiler->objectAt(targetObjectIndex);
Q_ASSERT(targetObject->id >= 0);
alias->setTargetObjectId(targetObject->id);
alias->setIsAliasToLocalAlias(false);
@@ -1124,11 +868,11 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
if (property.isEmpty()) {
alias->setFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject);
} else {
- QQmlPropertyCache::ConstPtr targetCache = propertyCaches.at(targetObjectIndex);
+ QQmlPropertyCache::ConstPtr targetCache = m_propertyCaches->at(targetObjectIndex);
if (!targetCache) {
*error = qQmlCompileError(
alias->referenceLocation,
- tr("Invalid alias target location: %1").arg(property.toString()));
+ QQmlComponentAndAliasResolverBase::tr("Invalid alias target location: %1").arg(property.toString()));
break;
}
@@ -1165,7 +909,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
if (!targetProperty || targetProperty->coreIndex() > 0x0000FFFF) {
*error = qQmlCompileError(
alias->referenceLocation,
- tr("Invalid alias target location: %1").arg(property.toString()));
+ QQmlComponentAndAliasResolverBase::tr("Invalid alias target location: %1").arg(property.toString()));
break;
}
@@ -1180,8 +924,8 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
isDeepAlias = false;
for (auto it = targetObject->bindingsBegin(); it != targetObject->bindingsEnd(); ++it) {
auto binding = *it;
- if (compiler->stringAt(binding.propertyNameIndex) == property) {
- resolver = QQmlPropertyResolver(propertyCaches.at(binding.value.objectIndex));
+ if (m_compiler->stringAt(binding.propertyNameIndex) == property) {
+ resolver = QQmlPropertyResolver(m_propertyCaches->at(binding.value.objectIndex));
const QQmlPropertyData *actualProperty = resolver.property(subProperty.toString());
if (actualProperty) {
propIdx = QQmlPropertyIndex(propIdx.coreIndex(), actualProperty->coreIndex());
@@ -1193,7 +937,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
if (!isDeepAlias) {
*error = qQmlCompileError(
alias->referenceLocation,
- tr("Invalid alias target location: %1").arg(subProperty.toString()));
+ QQmlComponentAndAliasResolverBase::tr("Invalid alias target location: %1").arg(subProperty.toString()));
break;
}
} else {
@@ -1203,7 +947,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
if (valueTypeIndex == -1) {
*error = qQmlCompileError(
alias->referenceLocation,
- tr("Invalid alias target location: %1").arg(subProperty.toString()));
+ QQmlComponentAndAliasResolverBase::tr("Invalid alias target location: %1").arg(subProperty.toString()));
break;
}
Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
@@ -1316,7 +1060,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(
obj->flags |= Object::HasCustomParserBindings;
continue;
}
- } else if (QmlIR::IRBuilder::isSignalPropertyName(name)
+ } else if (QQmlSignalNames::isHandlerName(name)
&& !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
obj->flags |= Object::HasCustomParserBindings;
binding->setFlag(Binding::IsCustomParserBinding);
@@ -1384,10 +1128,13 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(
COMPILE_EXCEPTION(binding, tr("You cannot assign an id to an object assigned "
"to a deferred property."));
}
- isDeferred = true;
- } else if (!deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
- if (!seenSubObjectWithId && binding->type() != Binding::Type_GroupProperty)
+ if (isExternal || !disableInternalDeferredProperties())
isDeferred = true;
+ } else if (!deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
+ if (!seenSubObjectWithId && binding->type() != Binding::Type_GroupProperty) {
+ if (isExternal || !disableInternalDeferredProperties())
+ isDeferred = true;
+ }
}
if (binding->type() >= Binding::Type_Object) {
diff --git a/src/qml/qml/qqmltypecompiler_p.h b/src/qml/qml/qqmltypecompiler_p.h
index 02edbb0c08..6d9e5a77ca 100644
--- a/src/qml/qml/qqmltypecompiler_p.h
+++ b/src/qml/qml/qqmltypecompiler_p.h
@@ -46,20 +46,24 @@ public:
QQmlTypeCompiler(QQmlEnginePrivate *engine,
QQmlTypeData *typeData,
QmlIR::Document *document,
- const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- QV4::ResolvedTypeReferenceMap *resolvedTypeCache,
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
const QV4::CompiledData::DependentTypesHasher &dependencyHasher);
// --- interface used by QQmlPropertyCacheCreator
typedef QmlIR::Object CompiledObject;
+ typedef QmlIR::Binding CompiledBinding;
using ListPropertyAssignBehavior = QmlIR::Pragma::ListPropertyAssignBehaviorValue;
+ // Deliberate choice of map over hash here to ensure stable generated output.
+ using IdToObjectMap = QMap<int, int>;
+
const QmlIR::Object *objectAt(int index) const { return document->objects.at(index); }
+ QmlIR::Object *objectAt(int index) { return document->objects.at(index); }
int objectCount() const { return document->objects.size(); }
QString stringAt(int idx) const;
QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); }
QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); }
- QV4::ResolvedTypeReferenceMap *resolvedTypes = nullptr;
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes = nullptr;
ListPropertyAssignBehavior listPropertyAssignBehavior() const
{
for (const QmlIR::Pragma *pragma: document->pragmas) {
@@ -70,7 +74,7 @@ public:
}
// ---
- QQmlRefPointer<QV4::ExecutableCompilationUnit> compile();
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile();
QList<QQmlError> compilationErrors() const { return errors; }
void recordError(const QV4::CompiledData::Location &location, const QString &description);
@@ -86,11 +90,8 @@ public:
QQmlEnginePrivate *enginePrivate() const { return engine; }
const QQmlImports *imports() const;
QVector<QmlIR::Object *> *qmlObjects() const;
- void setPropertyCaches(QQmlPropertyCacheVector &&caches);
+ QQmlPropertyCacheVector *propertyCaches();
const QQmlPropertyCacheVector *propertyCaches() const;
- QQmlPropertyCacheVector &&takePropertyCaches();
- void setComponentRoots(const QVector<quint32> &roots) { m_componentRoots = roots; }
- const QVector<quint32> &componentRoots() const { return m_componentRoots; }
QQmlJS::MemoryPool *memoryPool();
QStringView newStringRef(const QString &string);
const QV4::Compiler::StringTableGenerator *stringPool() const;
@@ -106,7 +107,16 @@ public:
return resolvedTypes->value(id);
}
- CompositeMetaTypeIds typeIdsForComponent(int objectId = 0) const;
+ QV4::ResolvedTypeReference *resolvedType(QMetaType type) const
+ {
+ for (QV4::ResolvedTypeReference *ref : std::as_const(*resolvedTypes)) {
+ if (ref->type().typeId() == type)
+ return ref;
+ }
+ return nullptr;
+ }
+
+ QQmlType qmlTypeForComponent(const QString &inlineComponentName = QString()) const;
private:
QList<QQmlError> errors;
@@ -117,10 +127,8 @@ private:
QHash<int, QQmlCustomParser*> customParsers;
// index in first hash is component index, vector inside contains object indices of objects with id property
- QVector<quint32> m_componentRoots;
QQmlPropertyCacheVector m_propertyCaches;
- QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
QQmlTypeData *typeData;
};
@@ -132,16 +140,9 @@ struct QQmlCompilePass
protected:
void recordError(const QV4::CompiledData::Location &location, const QString &description) const
{ compiler->recordError(location, description); }
- void recordError(const QQmlError &error)
- { compiler->recordError(error); }
QV4::ResolvedTypeReference *resolvedType(int id) const
{ return compiler->resolvedType(id); }
- bool containsResolvedType(int id) const
- { return compiler->resolvedTypes->contains(id); }
- QV4::ResolvedTypeReferenceMap::iterator insertResolvedType(
- int id, QV4::ResolvedTypeReference *value)
- { return compiler->resolvedTypes->insert(id, value); }
QQmlTypeCompiler *compiler;
};
@@ -235,44 +236,6 @@ private:
const QQmlPropertyCacheVector * const propertyCaches;
};
-class QQmlComponentAndAliasResolver : public QQmlCompilePass
-{
- Q_DECLARE_TR_FUNCTIONS(QQmlAnonymousComponentResolver)
-public:
- QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler);
-
- bool resolve(int root = 0);
-
-protected:
- void findAndRegisterImplicitComponents(
- const QmlIR::Object *obj, const QQmlPropertyCache::ConstPtr &propertyCache);
- bool collectIdsAndAliases(int objectIndex);
- bool resolveAliases(int componentIndex);
- void propertyDataForAlias(QmlIR::Alias *alias, int *type, quint32 *propertyFlags);
-
- enum AliasResolutionResult {
- NoAliasResolved,
- SomeAliasesResolved,
- AllAliasesResolved
- };
-
- AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlError *error);
-
- QQmlEnginePrivate *enginePrivate;
- QQmlJS::MemoryPool *pool;
-
- QVector<QmlIR::Object*> *qmlObjects;
-
- // indices of the objects that are actually Component {}
- QVector<quint32> componentRoots;
-
- // Deliberate choice of map over hash here to ensure stable generated output.
- QMap<int, int> _idToObjectIndex;
- QVector<int> _objectsWithAliases;
-
- QQmlPropertyCacheVector propertyCaches;
-};
-
class QQmlDeferredAndCustomParserBindingScanner : public QQmlCompilePass
{
Q_DECLARE_TR_FUNCTIONS(QQmlDeferredAndCustomParserBindingScanner)
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp
index 460a3440c7..2b189cd264 100644
--- a/src/qml/qml/qqmltypedata.cpp
+++ b/src/qml/qml/qqmltypedata.cpp
@@ -1,15 +1,16 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include <private/qqmltypedata_p.h>
+#include <private/qqmlcomponentandaliasresolver_p.h>
#include <private/qqmlengine_p.h>
-#include <private/qqmlpropertycachecreator_p.h>
-#include <private/qqmlpropertyvalidator_p.h>
#include <private/qqmlirbuilder_p.h>
#include <private/qqmlirloader_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
+#include <private/qqmlpropertyvalidator_p.h>
#include <private/qqmlscriptblob_p.h>
#include <private/qqmlscriptdata_p.h>
#include <private/qqmltypecompiler_p.h>
+#include <private/qqmltypedata_p.h>
#include <private/qqmltypeloaderqmldircontent_p.h>
#include <QtCore/qloggingcategory.h>
@@ -18,7 +19,7 @@
#include <memory>
Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE)
-Q_LOGGING_CATEGORY(lcCycle, "qt.qml.typeresolution.cycle")
+Q_LOGGING_CATEGORY(lcCycle, "qt.qml.typeresolution.cycle", QtWarningMsg)
QT_BEGIN_NAMESPACE
@@ -50,34 +51,11 @@ QQmlTypeData::~QQmlTypeData()
m_resolvedTypes.clear();
}
-const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const
-{
- return m_scripts;
-}
-
-QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnit() const
+QV4::CompiledData::CompilationUnit *QQmlTypeData::compilationUnit() const
{
return m_compiledData.data();
}
-QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnitForInlineComponent(unsigned int icObjectId) const
-{
- Q_ASSERT(m_document || m_compiledData);
- if (m_compiledData)
- return m_compiledData.data();
- for (auto it = m_document->objects.begin(); it != m_document->objects.end(); ++it) {
- auto object = *it;
- auto icIt = std::find_if(object->inlineComponentsBegin(), object->inlineComponentsEnd(), [&](const QV4::CompiledData::InlineComponent &ic) {
- return ic.objectIndex == icObjectId;
- });
- if (icIt != object->inlineComponentsEnd()) {
- Q_ASSERT(m_inlineComponentToCompiledData.contains(icIt->nameIndex));
- return m_inlineComponentToCompiledData[icIt->nameIndex].data();
- }
- }
- Q_UNREACHABLE_RETURN(nullptr);
-}
-
void QQmlTypeData::registerCallback(TypeDataCallback *callback)
{
Q_ASSERT(!m_callbacks.contains(callback));
@@ -91,11 +69,11 @@ void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
Q_ASSERT(!m_callbacks.contains(callback));
}
-CompositeMetaTypeIds QQmlTypeData::typeIds(int objectId) const
+QQmlType QQmlTypeData::qmlType(const QString &inlineComponentName) const
{
- if (objectId != 0)
- return m_inlineComponentData[objectId].typeIds;
- return m_typeIds;
+ if (inlineComponentName.isEmpty())
+ return m_qmlType;
+ return m_inlineComponentData[inlineComponentName].qmlType;
}
bool QQmlTypeData::tryLoadFromDiskCache()
@@ -103,11 +81,7 @@ bool QQmlTypeData::tryLoadFromDiskCache()
if (!readCacheFile())
return false;
- QV4::ExecutionEngine *v4 = typeLoader()->engine()->handle();
- if (!v4)
- return false;
-
- QQmlRefPointer<QV4::ExecutableCompilationUnit> unit = QV4::ExecutableCompilationUnit::create();
+ auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>();
{
QString error;
if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
@@ -117,11 +91,11 @@ bool QQmlTypeData::tryLoadFromDiskCache()
}
if (unit->unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation) {
- restoreIR(std::move(*unit));
+ restoreIR(unit);
return true;
}
- m_compiledData = unit;
+ m_compiledData = std::move(unit);
QVector<QV4::CompiledData::InlineComponent> ics;
for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) {
@@ -183,42 +157,133 @@ bool QQmlTypeData::tryLoadFromDiskCache()
for (auto&& ic: ics) {
QString const nameString = m_compiledData->stringAt(ic.nameIndex);
auto importUrl = finalUrl();
- importUrl.setFragment(QString::number(ic.objectIndex));
+ importUrl.setFragment(nameString);
auto import = new QQmlImportInstance();
- m_importCache->addInlineComponentImport(import, nameString, importUrl, QQmlType());
+ m_importCache->addInlineComponentImport(import, nameString, importUrl);
}
return true;
}
-void QQmlTypeData::createTypeAndPropertyCaches(
+template<>
+void QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::allocateNamedObjects(
+ const QV4::CompiledData::Object *object) const
+{
+ Q_UNUSED(object);
+}
+
+template<>
+bool QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::markAsComponent(int index) const
+{
+ return m_compiler->objectAt(index)->hasFlag(QV4::CompiledData::Object::IsComponent);
+}
+
+template<>
+void QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::setObjectId(int index) const
+{
+ Q_UNUSED(index)
+ // we cannot sanity-check the index here because bindings are sorted in a different order
+ // in the CU vs the IR.
+}
+
+template<>
+void QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::resolveGeneralizedGroupProperty(
+ const CompiledObject &component, CompiledBinding *binding)
+{
+ // We cannot make it fail here. It might be a custom-parsed property
+ for (int i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
+ const int candidateIndex = component.namedObjectsInComponentTable()[i];
+ if (m_compiler->objectAt(candidateIndex)->idNameIndex == binding->propertyNameIndex) {
+ m_propertyCaches->set(binding->value.objectIndex, m_propertyCaches->at(candidateIndex));
+ return;
+ }
+ }
+}
+
+template<>
+typename QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::AliasResolutionResult
+QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::resolveAliasesInObject(
+ const CompiledObject &component, int objectIndex, QQmlError *error)
+{
+ const CompiledObject *obj = m_compiler->objectAt(objectIndex);
+ for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
+ if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved)) {
+ *error = qQmlCompileError(alias->referenceLocation,
+ QQmlComponentAndAliasResolverBase::tr("Unresolved alias found"));
+ return NoAliasResolved;
+ }
+
+ if (alias->isAliasToLocalAlias() || alias->encodedMetaPropertyIndex == -1)
+ continue;
+
+ const int targetObjectIndex
+ = objectForId(m_compiler, component, alias->targetObjectId());
+ const int coreIndex
+ = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex();
+
+ QQmlPropertyCache::ConstPtr targetCache = m_propertyCaches->at(targetObjectIndex);
+ Q_ASSERT(targetCache);
+
+ if (!targetCache->property(coreIndex))
+ return SomeAliasesResolved;
+ }
+
+ return AllAliasesResolved;
+}
+
+template<>
+bool QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::wrapImplicitComponent(
+ const QV4::CompiledData::Binding *binding)
+{
+ // This should have been done when creating the CU.
+ Q_UNUSED(binding);
+ return false;
+}
+
+QQmlError QQmlTypeData::createTypeAndPropertyCaches(
const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- const QV4::ResolvedTypeReferenceMap &resolvedTypeCache)
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache)
{
Q_ASSERT(m_compiledData);
m_compiledData->typeNameCache = typeNameCache;
m_compiledData->resolvedTypes = resolvedTypeCache;
+ m_compiledData->inlineComponentData = m_inlineComponentData;
QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings;
{
- QQmlPropertyCacheCreator<QV4::ExecutableCompilationUnit> propertyCacheCreator(
+ QQmlPropertyCacheCreator<QV4::CompiledData::CompilationUnit> propertyCacheCreator(
&m_compiledData->propertyCaches, &pendingGroupPropertyBindings, engine,
m_compiledData.data(), m_importCache.data(), typeClassName());
- QQmlError error = propertyCacheCreator.buildMetaObjects();
- if (error.isValid()) {
- setError(error);
- return;
- }
- QQmlPropertyCacheAliasCreator<QV4::ExecutableCompilationUnit> aliasCreator(
- &m_compiledData->propertyCaches, m_compiledData.data());
- aliasCreator.appendAliasPropertiesToMetaObjects(engine);
+ QQmlError error = propertyCacheCreator.verifyNoICCycle();
+ if (error.isValid())
+ return error;
+
+ QQmlPropertyCacheCreatorBase::IncrementalResult result;
+ do {
+ result = propertyCacheCreator.buildMetaObjectsIncrementally();
+ if (result.error.isValid()) {
+ return result.error;
+ } else {
+ QQmlComponentAndAliasResolver resolver(
+ m_compiledData.data(), engine, &m_compiledData->propertyCaches);
+ if (const QQmlError error = resolver.resolve(result.processedRoot);
+ error.isValid()) {
+ return error;
+ }
+ pendingGroupPropertyBindings.resolveMissingPropertyCaches(
+ &m_compiledData->propertyCaches);
+ pendingGroupPropertyBindings.clear(); // anything that can be processed is now processed
+ }
+
+ } while (result.canResume);
}
pendingGroupPropertyBindings.resolveMissingPropertyCaches(&m_compiledData->propertyCaches);
+ return QQmlError();
}
static bool addTypeReferenceChecksumsToHash(
@@ -229,8 +294,8 @@ static bool addTypeReferenceChecksumsToHash(
if (typeRef.typeData) {
const auto unit = typeRef.typeData->compilationUnit()->unitData();
hash->addData({unit->md5Checksum, sizeof(unit->md5Checksum)});
- } else if (typeRef.type.isValid()) {
- const auto propertyCache = QQmlMetaType::propertyCache(typeRef.type.metaObject());
+ } else if (const QMetaObject *mo = typeRef.type.metaObject()) {
+ const auto propertyCache = QQmlMetaType::propertyCache(mo);
bool ok = false;
hash->addData(propertyCache->checksum(checksums, &ok));
if (!ok)
@@ -242,15 +307,25 @@ static bool addTypeReferenceChecksumsToHash(
// local helper function for inline components
namespace {
+using InlineComponentData = QV4::CompiledData::InlineComponentData;
+
template<typename ObjectContainer>
-void setupICs(const ObjectContainer &container, QHash<int, InlineComponentData> *icData, const QUrl &finalUrl) {
+void setupICs(
+ const ObjectContainer &container, QHash<QString, InlineComponentData> *icData,
+ const QUrl &baseUrl,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) {
Q_ASSERT(icData->empty());
for (int i = 0; i != container->objectCount(); ++i) {
auto root = container->objectAt(i);
for (auto it = root->inlineComponentsBegin(); it != root->inlineComponentsEnd(); ++it) {
- const QByteArray &className = QQmlPropertyCacheCreatorBase::createClassNameForInlineComponent(finalUrl, it->objectIndex);
- InlineComponentData icDatum(CompositeMetaTypeIds::fromCompositeName(className), int(it->objectIndex), int(it->nameIndex), 0, 0, 0);
- icData->insert(it->objectIndex, icDatum);
+ // We cannot re-use a previously finalized inline component type here. We need our own.
+ // We can and should re-use speculative type references, though.
+ InlineComponentData icDatum(
+ QQmlMetaType::findInlineComponentType(
+ baseUrl, container->stringAt(it->nameIndex), compilationUnit),
+ int(it->objectIndex), int(it->nameIndex), 0, 0, 0);
+
+ icData->insert(container->stringAt(it->nameIndex), icDatum);
}
}
};
@@ -324,16 +399,18 @@ void QQmlTypeData::done()
++it) {
const TypeReference &type = *it;
Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError() || type.type.isInlineComponentType());
- if (type.type.isInlineComponentType() && !type.type.pendingResolutionName().isEmpty()) {
- auto containingType = type.type.containingType();
- auto objectId = containingType.lookupInlineComponentIdByName(type.type.pendingResolutionName());
- if (objectId < 0) { // can be any negative number if we tentatively resolved it in QQmlImport but it actually was not an inline component
+
+ if (type.type.isInlineComponentType()) {
+ const QUrl url = type.type.sourceUrl();
+ if (!QQmlMetaType::equalBaseUrls(url, finalUrl())
+ && !QQmlMetaType::obtainCompilationUnit(type.type.typeId())) {
const QString &typeName = stringAt(it.key());
int lastDot = typeName.lastIndexOf(u'.');
- createError(type, QQmlTypeLoader::tr("Type %1 has no inline component type called %2").arg(QStringView{typeName}.left(lastDot), type.type.pendingResolutionName()));
+ createError(
+ type,
+ QQmlTypeLoader::tr("Type %1 has no inline component type called %2")
+ .arg(QStringView{typeName}.left(lastDot), type.type.elementName()));
return;
- } else {
- type.type.setInlineComponentObjectId(objectId);
}
}
if (type.typeData && type.typeData->isError()) {
@@ -355,17 +432,23 @@ void QQmlTypeData::done()
}
}
- m_typeClassName = QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(finalUrl());
- if (!m_typeClassName.isEmpty())
- m_typeIds = CompositeMetaTypeIds::fromCompositeName(m_typeClassName);
-
- if (m_document) {
- setupICs(m_document, &m_inlineComponentData, finalUrl());
- } else {
- setupICs(m_compiledData, &m_inlineComponentData, finalUrl());
+ if (QQmlPropertyCacheCreatorBase::canCreateClassNameTypeByUrl(finalUrl())) {
+ const bool isSingleton = m_document
+ ? m_document.data()->isSingleton()
+ : (m_compiledData->unitData()->flags & QV4::CompiledData::Unit::IsSingleton);
+ m_qmlType = QQmlMetaType::findCompositeType(
+ finalUrl(), m_compiledData, isSingleton
+ ? QQmlMetaType::Singleton
+ : QQmlMetaType::NonSingleton);
+ m_typeClassName = QByteArray(m_qmlType.typeId().name()).chopped(1);
}
- QV4::ResolvedTypeReferenceMap resolvedTypeCache;
+ if (m_document)
+ setupICs(m_document, &m_inlineComponentData, finalUrl(), m_compiledData);
+ else
+ setupICs(m_compiledData, &m_inlineComponentData, finalUrl(), m_compiledData);
+
+ QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache;
QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
{
QQmlError error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache);
@@ -386,27 +469,57 @@ void QQmlTypeData::done()
};
// verify if any dependencies changed if we're using a cache
- if (m_document.isNull() && !m_compiledData->verifyChecksum(dependencyHasher)) {
- qCDebug(DBG_DISK_CACHE) << "Checksum mismatch for cached version of" << m_compiledData->fileName();
- if (!loadFromSource())
- return;
- m_compiledData.reset();
+ if (m_document.isNull()) {
+ const QQmlError error = createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache);
+ if (!error.isValid() && m_compiledData->verifyChecksum(dependencyHasher)) {
+ setCompileUnit(m_compiledData);
+ } else {
+
+ if (error.isValid()) {
+ qCDebug(DBG_DISK_CACHE)
+ << "Failed to create property caches for"
+ << m_compiledData->fileName()
+ << "because" << error.description();
+ } else {
+ qCDebug(DBG_DISK_CACHE)
+ << "Checksum mismatch for cached version of"
+ << m_compiledData->fileName();
+ }
+
+ if (!loadFromSource())
+ return;
+
+ // We want to keep our resolve types ...
+ m_compiledData->resolvedTypes.clear();
+ // ... but we don't want the property caches we've created for the broken CU.
+ for (QV4::ResolvedTypeReference *ref: std::as_const(resolvedTypeCache)) {
+ const auto compilationUnit = ref->compilationUnit();
+ if (compilationUnit.isNull()) {
+ // Inline component references without CU belong to the surrounding CU.
+ // We have to clear them. Inline component references to other documents
+ // have a CU.
+ if (!ref->type().isInlineComponentType())
+ continue;
+ } else if (compilationUnit != m_compiledData) {
+ continue;
+ }
+ ref->setTypePropertyCache(QQmlPropertyCache::ConstPtr());
+ ref->setCompilationUnit(QQmlRefPointer<QV4::CompiledData::CompilationUnit>());
+ }
+
+ m_compiledData.reset();
+ }
}
if (!m_document.isNull()) {
// Compile component
compile(typeNameCache, &resolvedTypeCache, dependencyHasher);
- if (!isError())
+ if (isError())
+ return;
+ else
setCompileUnit(m_document);
- } else {
- createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache);
- if (!isError())
- setCompileUnit(m_compiledData);
}
- if (isError())
- return;
-
{
QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine());
m_compiledData->inlineComponentData = m_inlineComponentData;
@@ -420,7 +533,7 @@ void QQmlTypeData::done()
}
}
- m_compiledData->finalizeCompositeType(enginePrivate, typeIds());
+ m_compiledData->finalizeCompositeType(qmlType());
}
{
@@ -448,33 +561,6 @@ void QQmlTypeData::done()
}
}
- // associate inline components to root component
- {
- auto fileName = finalUrl().fileName();
- QStringView typeName = [&]() {
- // extract base name (QFileInfo::baseName would require constructing a QFileInfo)
- auto dotIndex = fileName.indexOf(u'.');
- if (dotIndex < 0)
- return QStringView();
- return QStringView(fileName).first(dotIndex);
- }();
- // typeName can be empty if a QQmlComponent was constructed with an empty QUrl parameter
- if (!typeName.isEmpty() && typeName.at(0).isUpper() && !m_inlineComponentData.isEmpty()) {
- QHashedStringRef const hashedStringRef { typeName };
- QList<QQmlError> errors;
- auto type = QQmlMetaType::typeForUrl(finalUrlString(), hashedStringRef, false, &errors);
- Q_ASSERT(errors.empty());
- if (type.isValid()) {
- for (auto const &icDatum : std::as_const(m_inlineComponentData)) {
- Q_ASSERT(icDatum.typeIds.isValid());
- QQmlType existingType = type.lookupInlineComponentById(type.lookupInlineComponentIdByName(m_compiledData->stringAt(icDatum.nameIndex)));
- type.associateInlineComponent(m_compiledData->stringAt(icDatum.nameIndex),
- icDatum.objectIndex, icDatum.typeIds, existingType);
- }
- }
- }
- }
-
{
// Collect imported scripts
m_compiledData->dependentScripts.reserve(m_scripts.size());
@@ -490,7 +576,8 @@ void QQmlTypeData::done()
qualifier = qualifier.mid(lastDotIndex+1);
}
- m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace);
+ m_compiledData->typeNameCache->add(
+ qualifier.toString(), scriptIndex, enclosingNamespace);
QQmlRefPointer<QQmlScriptData> scriptData = script.script->scriptData();
m_compiledData->dependentScripts << scriptData;
}
@@ -512,13 +599,12 @@ bool QQmlTypeData::loadImplicitImport()
m_importCache->setBaseUrl(finalUrl(), finalUrlString());
- QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
// For local urls, add an implicit import "." as most overridden lookup.
// This will also trigger the loading of the qmldir and the import of any native
// types from available plugins.
QList<QQmlError> implicitImportErrors;
QString localQmldir;
- m_importCache->addImplicitImport(importDatabase, &localQmldir, &implicitImportErrors);
+ m_importCache->addImplicitImport(typeLoader(), &localQmldir, &implicitImportErrors);
// When loading with QQmlImports::ImportImplicit, the imports are _appended_ to the namespace
// in the order they are loaded. Therefore, the addImplicitImport above gets the highest
@@ -575,7 +661,10 @@ void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *un
loader.load();
m_document->jsModule.fileName = urlString();
m_document->jsModule.finalUrl = finalUrlString();
- m_document->javaScriptCompilationUnit = QV4::CompiledData::CompilationUnit(unit->qmlData, unit->aotCompiledFunctions);
+ m_document->javaScriptCompilationUnit
+ = QQmlRefPointer<QV4::CompiledData::CompilationUnit>(
+ new QV4::CompiledData::CompilationUnit(unit->qmlData, unit->aotCompiledFunctions),
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit>::Adopt);
continueLoadFromIR();
}
@@ -610,31 +699,26 @@ bool QQmlTypeData::loadFromSource()
return true;
}
-void QQmlTypeData::restoreIR(QV4::CompiledData::CompilationUnit &&unit)
+void QQmlTypeData::restoreIR(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit)
{
m_document.reset(new QmlIR::Document(isDebugging()));
- QQmlIRLoader loader(unit.unitData(), m_document.data());
+ QQmlIRLoader loader(unit->unitData(), m_document.data());
loader.load();
m_document->jsModule.fileName = urlString();
m_document->jsModule.finalUrl = finalUrlString();
- m_document->javaScriptCompilationUnit = std::move(unit);
+ m_document->javaScriptCompilationUnit = unit;
continueLoadFromIR();
}
void QQmlTypeData::continueLoadFromIR()
{
- QQmlType containingType;
- auto containingTypeName = finalUrl().fileName().split(QLatin1Char('.')).first();
- QTypeRevision version;
- QQmlImportNamespace *ns = nullptr;
- m_importCache->resolveType(containingTypeName, &containingType, &version, &ns);
for (auto const& object: m_document->objects) {
for (auto it = object->inlineComponentsBegin(); it != object->inlineComponentsEnd(); ++it) {
QString const nameString = m_document->stringAt(it->nameIndex);
auto importUrl = finalUrl();
- importUrl.setFragment(QString::number(it->objectIndex));
+ importUrl.setFragment(nameString);
auto import = new QQmlImportInstance(); // Note: The cache takes ownership of the QQmlImportInstance
- m_importCache->addInlineComponentImport(import, nameString, importUrl, containingType);
+ m_importCache->addInlineComponentImport(import, nameString, importUrl);
}
}
@@ -733,18 +817,22 @@ QString QQmlTypeData::stringAt(int index) const
}
void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- QV4::ResolvedTypeReferenceMap *resolvedTypeCache,
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
{
Q_ASSERT(m_compiledData.isNull());
- const bool typeRecompilation = m_document && m_document->javaScriptCompilationUnit.unitData()
- && (m_document->javaScriptCompilationUnit.unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation);
+ const bool typeRecompilation = m_document
+ && m_document->javaScriptCompilationUnit
+ && m_document->javaScriptCompilationUnit->unitData()
+ && (m_document->javaScriptCompilationUnit->unitData()->flags
+ & QV4::CompiledData::Unit::PendingTypeCompilation);
QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine());
- QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache, dependencyHasher);
- m_compiledData = compiler.compile();
- if (!m_compiledData) {
+ QQmlTypeCompiler compiler(
+ enginePrivate, this, m_document.data(), resolvedTypeCache, dependencyHasher);
+ auto compilationUnit = compiler.compile();
+ if (!compilationUnit) {
qDeleteAll(*resolvedTypeCache);
resolvedTypeCache->clear();
setError(compiler.compilationErrors());
@@ -754,19 +842,31 @@ void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCach
const bool trySaveToDisk = writeCacheFile() && !typeRecompilation;
if (trySaveToDisk) {
QString errorString;
- if (m_compiledData->saveToDisk(url(), &errorString)) {
+ if (compilationUnit->saveToDisk(url(), &errorString)) {
QString error;
- if (!m_compiledData->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
+ if (!compilationUnit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
// ignore error, keep using the in-memory compilation unit.
}
} else {
- qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->fileName() << "to disk:" << errorString;
+ qCDebug(DBG_DISK_CACHE) << "Error saving cached version of"
+ << compilationUnit->fileName() << "to disk:" << errorString;
}
}
+
+ m_compiledData = std::move(compilationUnit);
+ m_compiledData->typeNameCache = typeNameCache;
+ m_compiledData->resolvedTypes = *resolvedTypeCache;
+ m_compiledData->propertyCaches = std::move(*compiler.propertyCaches());
+ Q_ASSERT(m_compiledData->propertyCaches.count()
+ == static_cast<int>(m_compiledData->objectCount()));
}
void QQmlTypeData::resolveTypes()
{
+ // Load the implicit import since it may have additional scripts.
+ if (!m_implicitImportLoaded && !loadImplicitImport())
+ return;
+
// Add any imported scripts to our resolved set
const auto resolvedScripts = m_importCache->resolvedScripts();
for (const QQmlImports::ScriptReference &script : resolvedScripts) {
@@ -808,8 +908,8 @@ void QQmlTypeData::resolveTypes()
if (ref.type.isCompositeSingleton()) {
ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
if (ref.typeData->isWaiting() || m_waitingOnMe.contains(ref.typeData.data())) {
- qCWarning(lcCycle) << "Cyclic dependency detected between"
- << ref.typeData->urlString() << "and" << urlString();
+ qCDebug(lcCycle) << "Possible cyclic dependency detected between"
+ << ref.typeData->urlString() << "and" << urlString();
continue;
}
addDependency(ref.typeData.data());
@@ -842,11 +942,11 @@ void QQmlTypeData::resolveTypes()
addDependency(ref.typeData.data());
}
if (ref.type.isInlineComponentType()) {
- auto containingType = ref.type.containingType();
- if (containingType.isValid()) {
- auto const url = containingType.sourceUrl();
- if (url.isValid()) {
- auto typeData = typeLoader()->getType(url);
+ QUrl containingTypeUrl = ref.type.sourceUrl();
+ containingTypeUrl.setFragment(QString());
+ if (!containingTypeUrl.isEmpty()) {
+ auto typeData = typeLoader()->getType(containingTypeUrl);
+ if (typeData.data() != this) {
ref.typeData = typeData;
addDependency(typeData.data());
}
@@ -866,7 +966,7 @@ void QQmlTypeData::resolveTypes()
QQmlError QQmlTypeData::buildTypeResolutionCaches(
QQmlRefPointer<QQmlTypeNameCache> *typeNameCache,
- QV4::ResolvedTypeReferenceMap *resolvedTypeCache) const
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache) const
{
typeNameCache->adopt(new QQmlTypeNameCache(m_importCache));
@@ -889,36 +989,31 @@ QQmlError QQmlTypeData::buildTypeResolutionCaches(
ref->setCompilationUnit(resolvedType->typeData->compilationUnit());
if (resolvedType->type.isInlineComponentType()) {
// Inline component which is part of an already resolved type
- int objectId = -1;
- if (qmlType.containingType().isValid()) {
- objectId = qmlType.containingType().lookupInlineComponentIdByName(QString::fromUtf8(qmlType.typeName()));
- qmlType.setInlineComponentObjectId(objectId);
- } else {
- objectId = resolvedType->type.inlineComponentId();
- }
- Q_ASSERT(objectId != -1);
- ref->setTypePropertyCache(resolvedType->typeData->compilationUnit()->propertyCaches.at(objectId));
- ref->setType(qmlType);
+ QString icName = qmlType.elementName();
+ Q_ASSERT(!icName.isEmpty());
+
+ const auto compilationUnit = resolvedType->typeData->compilationUnit();
+ ref->setTypePropertyCache(compilationUnit->propertyCaches.at(
+ compilationUnit->inlineComponentId(icName)));
+ ref->setType(std::move(qmlType));
Q_ASSERT(ref->type().isInlineComponentType());
}
} else if (resolvedType->type.isInlineComponentType()) {
- // Inline component, defined in the file we are currently compiling
- if (!m_inlineComponentToCompiledData.contains(resolvedType.key())) {
- ref->setType(qmlType);
- if (qmlType.isValid()) {
- // this is required for inline components in singletons
- auto type = qmlType.lookupInlineComponentById(qmlType.inlineComponentId()).typeId();
- auto exUnit = QQmlMetaType::obtainExecutableCompilationUnit(type);
- if (exUnit) {
- ref->setCompilationUnit(exUnit);
- ref->setTypePropertyCache(QQmlMetaType::propertyCacheForType(type));
- }
+ ref->setType(qmlType);
+
+ // Inline component
+ // If it's defined in the same file we're currently compiling, we don't want to use it.
+ // We're going to fill in the property caches later after all.
+ if (qmlType.isValid()
+ && !QQmlMetaType::equalBaseUrls(finalUrl(), qmlType.sourceUrl())) {
+
+ // this is required for inline components in singletons
+ const QMetaType type = qmlType.typeId();
+ if (auto unit = QQmlMetaType::obtainCompilationUnit(type)) {
+ ref->setCompilationUnit(std::move(unit));
+ ref->setTypePropertyCache(QQmlMetaType::propertyCacheForType(type));
}
- } else {
- ref->setCompilationUnit(m_inlineComponentToCompiledData[resolvedType.key()]);
- ref->setTypePropertyCache(m_inlineComponentToCompiledData[resolvedType.key()]->rootPropertyCache());
}
-
} else if (qmlType.isValid() && !resolvedType->selfReference) {
ref->setType(qmlType);
Q_ASSERT(ref->type().isValid());
@@ -930,8 +1025,12 @@ QQmlError QQmlTypeData::buildTypeResolutionCaches(
return qQmlCompileError(resolvedType->location, reason);
}
- if (qmlType.containsRevisionedAttributes())
- ref->setTypePropertyCache(QQmlMetaType::propertyCache(qmlType, resolvedType->version));
+ if (qmlType.containsRevisionedAttributes()) {
+ // It can only have (revisioned) properties or methods if it has a metaobject
+ Q_ASSERT(qmlType.metaObject());
+ ref->setTypePropertyCache(
+ QQmlMetaType::propertyCache(qmlType, resolvedType->version));
+ }
}
ref->setVersion(resolvedType->version);
ref->doDynamicTypeCheck();
@@ -949,17 +1048,17 @@ bool QQmlTypeData::resolveType(const QString &typeName, QTypeRevision &version,
QQmlImportNamespace *typeNamespace = nullptr;
QList<QQmlError> errors;
- bool typeFound = m_importCache->resolveType(typeName, &ref.type, &version,
- &typeNamespace, &errors, registrationType,
- typeRecursionDetected);
+ bool typeFound = m_importCache->resolveType(
+ typeLoader(), typeName, &ref.type, &version, &typeNamespace, &errors, registrationType,
+ typeRecursionDetected);
if (!typeNamespace && !typeFound && !m_implicitImportLoaded) {
// Lazy loading of implicit import
if (loadImplicitImport()) {
// Try again to find the type
errors.clear();
- typeFound = m_importCache->resolveType(typeName, &ref.type, &version,
- &typeNamespace, &errors, registrationType,
- typeRecursionDetected);
+ typeFound = m_importCache->resolveType(
+ typeLoader(), typeName, &ref.type, &version, &typeNamespace, &errors,
+ registrationType, typeRecursionDetected);
} else {
return false; //loadImplicitImport() hit an error, and called setError already
}
@@ -997,12 +1096,14 @@ bool QQmlTypeData::resolveType(const QString &typeName, QTypeRevision &version,
return true;
}
-void QQmlTypeData::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/)
+void QQmlTypeData::scriptImported(
+ const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location,
+ const QString &nameSpace, const QString &qualifier)
{
ScriptReference ref;
ref.script = blob;
ref.location = location;
- ref.qualifier = qualifier;
+ ref.qualifier = qualifier.isEmpty() ? nameSpace : qualifier + QLatin1Char('.') + nameSpace;
m_scripts << ref;
}
diff --git a/src/qml/qml/qqmltypedata_p.h b/src/qml/qml/qqmltypedata_p.h
index d5398b0b5f..97419b916b 100644
--- a/src/qml/qml/qqmltypedata_p.h
+++ b/src/qml/qml/qqmltypedata_p.h
@@ -55,10 +55,7 @@ private:
public:
~QQmlTypeData() override;
- const QList<ScriptReference> &resolvedScripts() const;
-
- QV4::ExecutableCompilationUnit *compilationUnit() const;
- QV4::ExecutableCompilationUnit *compilationUnitForInlineComponent(unsigned int icObjectId) const;
+ QV4::CompiledData::CompilationUnit *compilationUnit() const;
// Used by QQmlComponent to get notifications
struct TypeDataCallback {
@@ -69,7 +66,7 @@ public:
void registerCallback(TypeDataCallback *);
void unregisterCallback(TypeDataCallback *);
- CompositeMetaTypeIds typeIds(int objectId = 0) const;
+ QQmlType qmlType(const QString &inlineComponentName = QString()) const;
QByteArray typeClassName() const { return m_typeClassName; }
SourceCodeData backupSourceCode() const { return m_backupSourceCode; }
@@ -84,27 +81,32 @@ protected:
QString stringAt(int index) const override;
private:
+ using InlineComponentData = QV4::CompiledData::InlineComponentData;
+
bool tryLoadFromDiskCache();
bool loadFromSource();
- void restoreIR(QV4::CompiledData::CompilationUnit &&unit);
+ void restoreIR(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit);
void continueLoadFromIR();
void resolveTypes();
QQmlError buildTypeResolutionCaches(
QQmlRefPointer<QQmlTypeNameCache> *typeNameCache,
- QV4::ResolvedTypeReferenceMap *resolvedTypeCache
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache
) const;
void compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- QV4::ResolvedTypeReferenceMap *resolvedTypeCache,
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
const QV4::CompiledData::DependentTypesHasher &dependencyHasher);
- void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- const QV4::ResolvedTypeReferenceMap &resolvedTypeCache);
+ QQmlError createTypeAndPropertyCaches(
+ const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache);
bool resolveType(const QString &typeName, QTypeRevision &version,
TypeReference &ref, int lineNumber = -1, int columnNumber = -1,
bool reportErrors = true,
QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType,
bool *typeRecursionDetected = nullptr);
- void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
+ void scriptImported(
+ const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location,
+ const QString &nameSpace, const QString &qualifier) override;
SourceCodeData m_backupSourceCode; // used when cache verification fails.
QScopedPointer<QmlIR::Document> m_document;
@@ -121,16 +123,15 @@ private:
QMap<int, TypeReference> m_resolvedTypes;
bool m_typesResolved:1;
- // Used for self-referencing types, otherwise -1.
- CompositeMetaTypeIds m_typeIds;
+ // Used for self-referencing types, otherwise invalid.
+ QQmlType m_qmlType;
QByteArray m_typeClassName; // used for meta-object later
- using ExecutableCompilationUnitPtr = QQmlRefPointer<QV4::ExecutableCompilationUnit>;
+ using CompilationUnitPtr = QQmlRefPointer<QV4::CompiledData::CompilationUnit>;
- QHash<int, InlineComponentData> m_inlineComponentData;
+ QHash<QString, InlineComponentData> m_inlineComponentData;
- ExecutableCompilationUnitPtr m_compiledData;
- QHash<int, ExecutableCompilationUnitPtr> m_inlineComponentToCompiledData;
+ CompilationUnitPtr m_compiledData;
QList<TypeDataCallback *> m_callbacks;
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index a30d25c30a..a74397bd93 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -46,6 +46,9 @@ namespace {
};
}
+Q_TRACE_POINT(qtqml, QQmlCompiling_entry, const QUrl &url)
+Q_TRACE_POINT(qtqml, QQmlCompiling_exit)
+
/*!
\class QQmlTypeLoader
\brief The QQmlTypeLoader class abstracts loading files and their dependencies over the network.
@@ -452,7 +455,7 @@ QQmlTypeLoader::Blob::PendingImport::PendingImport(
QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoader *loader)
: QQmlDataBlob(url, type, loader)
- , m_importCache(new QQmlImports(loader), QQmlRefPointer<QQmlImports>::Adopt)
+ , m_importCache(new QQmlImports(), QQmlRefPointer<QQmlImports>::Adopt)
{
}
@@ -527,8 +530,7 @@ bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &da
typeLoader()->setQmldirContent(qmldirIdentifier, data->content());
const QTypeRevision version = m_importCache->updateQmldirContent(
- typeLoader()->importDatabase(), import->uri, import->qualifier, qmldirIdentifier,
- qmldirUrl, errors);
+ typeLoader(), import->uri, import->qualifier, qmldirIdentifier, qmldirUrl, errors);
if (!version.isValid())
return false;
@@ -551,15 +553,12 @@ bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &da
bool QQmlTypeLoader::Blob::addScriptImport(const QQmlTypeLoader::Blob::PendingImportPtr &import)
{
const QUrl url(import->uri);
- const auto module = m_typeLoader->engine()->handle()->moduleForUrl(url);
- QQmlRefPointer<QQmlScriptBlob> blob;
- if (module.native) {
- blob.adopt(new QQmlScriptBlob(url, m_typeLoader));
- blob->initializeFromNative(*module.native);
- blob->tryDone();
- } else {
- blob = typeLoader()->getScript(finalUrl().resolved(url));
- }
+ QQmlTypeLoader *loader = typeLoader();
+ QQmlRefPointer<QQmlScriptBlob> blob = loader->injectedScript(url);
+ if (!blob)
+ blob = loader->getScript(finalUrl().resolved(url));
+ else
+ Q_ASSERT(blob->status() == QQmlDataBlob::Status::Complete);
addDependency(blob.data());
scriptImported(blob, import->location, import->qualifier, QString());
return true;
@@ -567,7 +566,6 @@ bool QQmlTypeLoader::Blob::addScriptImport(const QQmlTypeLoader::Blob::PendingIm
bool QQmlTypeLoader::Blob::addFileImport(const QQmlTypeLoader::Blob::PendingImportPtr &import, QList<QQmlError> *errors)
{
- QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
QQmlImports::ImportFlags flags;
QUrl importUrl(import->uri);
@@ -581,8 +579,8 @@ bool QQmlTypeLoader::Blob::addFileImport(const QQmlTypeLoader::Blob::PendingImpo
}
const QTypeRevision version = m_importCache->addFileImport(
- importDatabase, import->uri, import->qualifier, import->version, flags,
- import->precedence, nullptr, errors);
+ typeLoader(), import->uri, import->qualifier, import->version, flags,
+ import->precedence, nullptr, errors);
if (!version.isValid())
return false;
@@ -604,6 +602,28 @@ bool QQmlTypeLoader::Blob::addFileImport(const QQmlTypeLoader::Blob::PendingImpo
return true;
}
+static void addDependencyImportError(
+ const QQmlTypeLoader::Blob::PendingImportPtr &import, QList<QQmlError> *errors)
+{
+ QQmlError error;
+ QString reason = errors->front().description();
+ if (reason.size() > 512)
+ reason = reason.first(252) + QLatin1String("... ...") + reason.last(252);
+ if (import->version.hasMajorVersion()) {
+ error.setDescription(QQmlImportDatabase::tr(
+ "module \"%1\" version %2.%3 cannot be imported because:\n%4")
+ .arg(import->uri).arg(import->version.majorVersion())
+ .arg(import->version.hasMinorVersion()
+ ? QString::number(import->version.minorVersion())
+ : QLatin1String("x"))
+ .arg(reason));
+ } else {
+ error.setDescription(QQmlImportDatabase::tr("module \"%1\" cannot be imported because:\n%2")
+ .arg(import->uri, reason));
+ }
+ errors->prepend(error);
+}
+
bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingImportPtr &import, QList<QQmlError> *errors)
{
QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
@@ -619,9 +639,8 @@ bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingI
[&](const QString &qmldirFilePath, const QString &qmldirUrl) {
// This is a local library import
const QTypeRevision actualVersion = m_importCache->addLibraryImport(
- importDatabase, import->uri, import->qualifier,
- import->version, qmldirFilePath, qmldirUrl, import->flags, import->precedence,
- errors);
+ typeLoader(), import->uri, import->qualifier, import->version, qmldirFilePath,
+ qmldirUrl, import->flags, import->precedence, errors);
if (!actualVersion.isValid())
return false;
@@ -630,20 +649,7 @@ bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingI
import->version = actualVersion;
if (!loadImportDependencies(import, qmldirFilePath, import->flags, errors)) {
- QQmlError error;
- if (import->version.hasMajorVersion()) {
- error.setDescription(QQmlImportDatabase::tr(
- "module \"%1\" version %2.%3 cannot be imported because\n%4")
- .arg(import->uri).arg(import->version.majorVersion())
- .arg(import->version.hasMinorVersion()
- ? QString::number(import->version.minorVersion())
- : QLatin1String("x"))
- .arg(errors->front().description()));
- } else {
- error.setDescription(QQmlImportDatabase::tr("module \"%1\" cannot be imported because\n%2")
- .arg(import->uri, errors->front().description()));
- }
- errors->prepend(error);
+ addDependencyImportError(import, errors);
return false;
}
@@ -654,7 +660,13 @@ bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingI
switch (qmldirResult) {
case QQmlImportDatabase::QmldirFound:
return true;
- case QQmlImportDatabase::QmldirNotFound:
+ case QQmlImportDatabase::QmldirNotFound: {
+ if (!loadImportDependencies(import, QString(), import->flags, errors)) {
+ addDependencyImportError(import, errors);
+ return false;
+ }
+ break;
+ }
case QQmlImportDatabase::QmldirInterceptedToRemote:
break;
case QQmlImportDatabase::QmldirRejected:
@@ -676,8 +688,8 @@ bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingI
|| QQmlMetaType::latestModuleVersion(import->uri).isValid())) {
if (!m_importCache->addLibraryImport(
- importDatabase, import->uri, import->qualifier, import->version,
- QString(), QString(), import->flags, import->precedence, errors).isValid()) {
+ typeLoader(), import->uri, import->qualifier, import->version, QString(),
+ QString(), import->flags, import->precedence, errors).isValid()) {
return false;
}
} else {
@@ -696,9 +708,9 @@ bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingI
if (!remotePathList.isEmpty()) {
// Add this library and request the possible locations for it
const QTypeRevision version = m_importCache->addLibraryImport(
- importDatabase, import->uri, import->qualifier, import->version,
- QString(), QString(), import->flags | QQmlImports::ImportIncomplete,
- import->precedence, errors);
+ typeLoader(), import->uri, import->qualifier, import->version, QString(),
+ QString(), import->flags | QQmlImports::ImportIncomplete, import->precedence,
+ errors);
if (!version.isValid())
return false;
@@ -815,10 +827,10 @@ bool QQmlTypeLoader::Blob::loadImportDependencies(
const QQmlTypeLoader::Blob::PendingImportPtr &currentImport, const QString &qmldirUri,
QQmlImports::ImportFlags flags, QList<QQmlError> *errors)
{
- const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirUri);
- const QList<QQmlDirParser::Import> implicitImports
- = QQmlMetaType::moduleImports(currentImport->uri, currentImport->version)
- + qmldir.imports();
+ QList<QQmlDirParser::Import> implicitImports
+ = QQmlMetaType::moduleImports(currentImport->uri, currentImport->version);
+ if (!qmldirUri.isEmpty())
+ implicitImports += typeLoader()->qmldirContent(qmldirUri).imports();
// Prevent overflow from one category of import into the other.
switch (currentImport->precedence) {
@@ -826,7 +838,7 @@ bool QQmlTypeLoader::Blob::loadImportDependencies(
case QQmlImportInstance::Lowest: {
QQmlError error;
error.setDescription(
- QString::fromLatin1("Too many dependent imports")
+ QString::fromLatin1("Too many dependent imports for %1 %2.%3")
.arg(currentImport->uri)
.arg(currentImport->version.majorVersion())
.arg(currentImport->version.minorVersion()));
@@ -965,6 +977,7 @@ QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QUrl &unNormalizedUrl
// this was started Asynchronous, but we need to force Synchronous
// completion now (if at all possible with this type of URL).
+#if QT_CONFIG(thread)
if (!m_thread->isThisThread()) {
// this only works when called directly from the UI thread, but not
// when recursively called on the QML thread via resolveTypes()
@@ -973,6 +986,7 @@ QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QUrl &unNormalizedUrl
m_thread->waitForNextMessage();
}
}
+#endif
}
return typeData;
@@ -992,6 +1006,26 @@ QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QByteArray &data, con
return QQmlRefPointer<QQmlTypeData>(typeData, QQmlRefPointer<QQmlTypeData>::Adopt);
}
+void QQmlTypeLoader::injectScript(const QUrl &relativeUrl, const QV4::Value &value)
+{
+ LockHolder<QQmlTypeLoader> holder(this);
+
+ QQmlScriptBlob *blob = new QQmlScriptBlob(relativeUrl, this);
+ blob->initializeFromNative(value);
+ blob->m_isDone = true;
+ blob->m_data.setStatus(QQmlDataBlob::Complete);
+ m_scriptCache.insert(relativeUrl, blob);
+}
+
+QQmlRefPointer<QQmlScriptBlob> QQmlTypeLoader::injectedScript(const QUrl &relativeUrl)
+{
+ LockHolder<QQmlTypeLoader> holder(this);
+ const auto it = m_scriptCache.constFind(relativeUrl);
+ return (it != m_scriptCache.constEnd() && (*it)->isNative())
+ ? *it
+ : QQmlRefPointer<QQmlScriptBlob>();
+}
+
/*!
Return a QQmlScriptBlob for \a url. The QQmlScriptData may be cached.
*/
@@ -1294,6 +1328,11 @@ and qmldir information.
*/
void QQmlTypeLoader::clearCache()
{
+ // Pending messages typically hold references to the blobs they want to be delivered to.
+ // We don't want them anymore.
+ if (m_thread)
+ m_thread->discardMessages();
+
for (TypeCache::Iterator iter = m_typeCache.begin(), end = m_typeCache.end(); iter != end; ++iter)
(*iter)->release();
for (ScriptCache::Iterator iter = m_scriptCache.begin(), end = m_scriptCache.end(); iter != end; ++iter)
@@ -1310,7 +1349,6 @@ void QQmlTypeLoader::clearCache()
m_importDirCache.clear();
m_importQmlDirCache.clear();
m_checksumCache.clear();
- QQmlMetaType::freeUnusedTypesAndCaches();
}
void QQmlTypeLoader::updateTypeCacheTrimThreshold()
@@ -1332,15 +1370,29 @@ void QQmlTypeLoader::trimCache()
// typeData->m_compiledData may be set early on in the proccess of loading a file, so
// it's important to check the general loading status of the typeData before making any
// other decisions.
- if (typeData->count() == 1 && (typeData->isError() || typeData->isComplete())
- && (!typeData->m_compiledData || typeData->m_compiledData->count() == 1)) {
- // There are no live objects of this type
- iter.value()->release();
- iter = m_typeCache.erase(iter);
- deletedOneType = true;
- } else {
+ if (typeData->count() != 1 || (!typeData->isError() && !typeData->isComplete())) {
++iter;
+ continue;
}
+
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit
+ = typeData->m_compiledData;
+ if (compilationUnit) {
+ if (compilationUnit->count()
+ > QQmlMetaType::countInternalCompositeTypeSelfReferences(
+ compilationUnit) + 1) {
+ ++iter;
+ continue;
+ }
+
+ QQmlMetaType::unregisterInternalCompositeType(compilationUnit);
+ Q_ASSERT(compilationUnit->count() == 1);
+ }
+
+ // There are no live objects of this type
+ iter.value()->release();
+ iter = m_typeCache.erase(iter);
+ deletedOneType = true;
}
if (!deletedOneType)
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index 6c53437d86..e9c4559527 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -18,6 +18,7 @@
#include <private/qqmldatablob_p.h>
#include <private/qqmlimport_p.h>
#include <private/qqmlmetatype_p.h>
+#include <private/qv4compileddata_p.h>
#include <QtQml/qtqmlglobal.h>
#include <QtQml/qqmlerror.h>
@@ -38,14 +39,14 @@ class QQmlProfiler;
class QQmlTypeLoaderThread;
class QQmlEngine;
-class Q_QML_PRIVATE_EXPORT QQmlTypeLoader
+class Q_QML_EXPORT QQmlTypeLoader
{
Q_DECLARE_TR_FUNCTIONS(QQmlTypeLoader)
public:
using ChecksumCache = QHash<quintptr, QByteArray>;
enum Mode { PreferSynchronous, Asynchronous, Synchronous };
- class Q_QML_PRIVATE_EXPORT Blob : public QQmlDataBlob
+ class Q_QML_EXPORT Blob : public QQmlDataBlob
{
public:
Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoader *loader);
@@ -122,6 +123,23 @@ public:
QQmlTypeLoader(QQmlEngine *);
~QQmlTypeLoader();
+ template<
+ typename Engine,
+ typename EnginePrivate = QQmlEnginePrivate,
+ typename = std::enable_if_t<std::is_same_v<Engine, QQmlEngine>>>
+ static QQmlTypeLoader *get(Engine *engine)
+ {
+ return get(EnginePrivate::get(engine));
+ }
+
+ template<
+ typename Engine,
+ typename = std::enable_if_t<std::is_same_v<Engine, QQmlEnginePrivate>>>
+ static QQmlTypeLoader *get(Engine *engine)
+ {
+ return &engine->typeLoader;
+ }
+
QQmlImportDatabase *importDatabase() const;
ChecksumCache *checksumCache() { return &m_checksumCache; }
const ChecksumCache *checksumCache() const { return &m_checksumCache; }
@@ -131,6 +149,9 @@ public:
QQmlRefPointer<QQmlTypeData> getType(const QUrl &unNormalizedUrl, Mode mode = PreferSynchronous);
QQmlRefPointer<QQmlTypeData> getType(const QByteArray &, const QUrl &url, Mode mode = PreferSynchronous);
+ void injectScript(const QUrl &relativeUrl, const QV4::Value &value);
+ QQmlRefPointer<QQmlScriptBlob> injectedScript(const QUrl &relativeUrl);
+
QQmlRefPointer<QQmlScriptBlob> getScript(const QUrl &unNormalizedUrl);
QQmlRefPointer<QQmlQmldirData> getQmldir(const QUrl &);
diff --git a/src/qml/qml/qqmltypeloaderqmldircontent.cpp b/src/qml/qml/qqmltypeloaderqmldircontent.cpp
index 57044fcdc3..5744cf2bb7 100644
--- a/src/qml/qml/qqmltypeloaderqmldircontent.cpp
+++ b/src/qml/qml/qqmltypeloaderqmldircontent.cpp
@@ -7,10 +7,9 @@
QT_BEGIN_NAMESPACE
-QList<QQmlError> QQmlTypeLoaderQmldirContent::errors(const QString &uri) const
+QList<QQmlError> QQmlTypeLoaderQmldirContent::errors(const QString &uri, const QUrl &url) const
{
QList<QQmlError> errors;
- const QUrl url(uri);
const auto parseErrors = m_parser.errors(uri);
for (const auto &parseError : parseErrors) {
QQmlError error;
@@ -29,6 +28,7 @@ void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QStr
m_hasContent = true;
m_location = location;
m_parser.parse(content);
+ m_parser.disambiguateFileSelectors();
}
void QQmlTypeLoaderQmldirContent::setError(const QQmlError &error)
diff --git a/src/qml/qml/qqmltypeloaderqmldircontent_p.h b/src/qml/qml/qqmltypeloaderqmldircontent_p.h
index 5749e04eaf..421eb16da4 100644
--- a/src/qml/qml/qqmltypeloaderqmldircontent_p.h
+++ b/src/qml/qml/qqmltypeloaderqmldircontent_p.h
@@ -35,7 +35,7 @@ public:
bool hasContent() const { return m_hasContent; }
bool hasError() const { return m_parser.hasError(); }
- QList<QQmlError> errors(const QString &uri) const;
+ QList<QQmlError> errors(const QString &uri, const QUrl &url) const;
QString typeNamespace() const { return m_parser.typeNamespace(); }
@@ -47,6 +47,13 @@ public:
QString qmldirLocation() const { return m_location; }
QString preferredPath() const { return m_parser.preferredPath(); }
+ bool hasRedirection() const
+ {
+ const QString preferred = preferredPath();
+ return !preferred.isEmpty()
+ && preferred != QStringView(m_location).chopped(strlen("qmldir"));
+ }
+
bool designerSupported() const { return m_parser.designerSupported(); }
bool hasTypeInfo() const { return !m_parser.typeInfos().isEmpty(); }
diff --git a/src/qml/qml/qqmltypeloaderthread.cpp b/src/qml/qml/qqmltypeloaderthread.cpp
index 3d35962c08..18d1dbe7ce 100644
--- a/src/qml/qml/qqmltypeloaderthread.cpp
+++ b/src/qml/qml/qqmltypeloaderthread.cpp
@@ -72,22 +72,12 @@ void QQmlTypeLoaderThread::loadWithCachedUnitAsync(const QQmlDataBlob::Ptr &b, c
void QQmlTypeLoaderThread::callCompleted(const QQmlDataBlob::Ptr &b)
{
-#if !QT_CONFIG(thread)
- if (!isThisThread())
- postMethodToThread(&This::callCompletedMain, b);
-#else
postMethodToMain(&This::callCompletedMain, b);
-#endif
}
void QQmlTypeLoaderThread::callDownloadProgressChanged(const QQmlDataBlob::Ptr &b, qreal p)
{
-#if !QT_CONFIG(thread)
- if (!isThisThread())
- postMethodToThread(&This::callDownloadProgressChangedMain, b, p);
-#else
postMethodToMain(&This::callDownloadProgressChangedMain, b, p);
-#endif
}
void QQmlTypeLoaderThread::initializeEngine(QQmlExtensionInterface *iface,
diff --git a/src/qml/qml/qqmltypemodule_p.h b/src/qml/qml/qqmltypemodule_p.h
index acf21de595..717b07ec60 100644
--- a/src/qml/qml/qqmltypemodule_p.h
+++ b/src/qml/qml/qqmltypemodule_p.h
@@ -94,7 +94,7 @@ public:
void walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const;
private:
- static Q_QML_PRIVATE_EXPORT QQmlType findType(
+ static Q_QML_EXPORT QQmlType findType(
const QList<QQmlTypePrivate *> *types, QTypeRevision version);
const QString m_module;
diff --git a/src/qml/qml/qqmltypenamecache_p.h b/src/qml/qml/qqmltypenamecache_p.h
index 6b3f9094f1..1ec0a65fa0 100644
--- a/src/qml/qml/qqmltypenamecache_p.h
+++ b/src/qml/qml/qqmltypenamecache_p.h
@@ -45,7 +45,7 @@ struct QQmlImportRef {
class QQmlType;
class QQmlEngine;
-class Q_QML_PRIVATE_EXPORT QQmlTypeNameCache : public QQmlRefCount
+class Q_QML_EXPORT QQmlTypeNameCache final : public QQmlRefCounted<QQmlTypeNameCache>
{
public:
QQmlTypeNameCache(const QQmlRefPointer<QQmlImports> &imports) : m_imports(imports) {}
@@ -74,27 +74,29 @@ public:
// Restrict the types allowed for key. We don't want QV4::ScopedString, for example.
template<QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion>
- Result query(const QHashedStringRef &key) const
+ Result query(const QHashedStringRef &key, QQmlTypeLoader *typeLoader) const
{
- return doQuery<const QHashedStringRef &, recursionRestriction>(key);
+ return doQuery<const QHashedStringRef &, recursionRestriction>(key, typeLoader);
}
template<QueryNamespaced queryNamespaced = QueryNamespaced::Yes>
- Result query(const QHashedStringRef &key, const QQmlImportRef *importNamespace) const
+ Result query(const QHashedStringRef &key, const QQmlImportRef *importNamespace,
+ QQmlTypeLoader *typeLoader) const
{
- return doQuery<const QHashedStringRef &, queryNamespaced>(key, importNamespace);
+ return doQuery<const QHashedStringRef &, queryNamespaced>(key, importNamespace, typeLoader);
}
template<QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion>
- Result query(const QV4::String *key) const
+ Result query(const QV4::String *key, QQmlTypeLoader *typeLoader) const
{
- return doQuery<const QV4::String *, recursionRestriction>(key);
+ return doQuery<const QV4::String *, recursionRestriction>(key, typeLoader);
}
template<QueryNamespaced queryNamespaced = QueryNamespaced::Yes>
- Result query(const QV4::String *key, const QQmlImportRef *importNamespace) const
+ Result query(const QV4::String *key, const QQmlImportRef *importNamespace,
+ QQmlTypeLoader *typeLoader) const
{
- return doQuery<const QV4::String *, queryNamespaced>(key, importNamespace);
+ return doQuery<const QV4::String *, queryNamespaced>(key, importNamespace, typeLoader);
}
private:
@@ -122,7 +124,7 @@ private:
static QString toQString(const QV4::String *key) { return key->toQStringNoThrow(); }
template<typename Key, QQmlImport::RecursionRestriction recursionRestriction>
- Result doQuery(Key name) const
+ Result doQuery(Key name, QQmlTypeLoader *typeLoader) const
{
Result result = doQuery(m_namedImports, name);
@@ -141,9 +143,9 @@ private:
QQmlType t;
bool typeRecursionDetected = false;
const bool typeFound = m_imports->resolveType(
- toHashedStringRef(name), &t, nullptr, &typeNamespace, &errors,
- QQmlType::AnyRegistrationType,
- recursionRestriction == QQmlImport::AllowRecursion
+ typeLoader, toHashedStringRef(name), &t, nullptr, &typeNamespace, &errors,
+ QQmlType::AnyRegistrationType,
+ recursionRestriction == QQmlImport::AllowRecursion
? &typeRecursionDetected
: nullptr);
if (typeFound)
@@ -155,7 +157,7 @@ private:
}
template<typename Key, QueryNamespaced queryNamespaced>
- Result doQuery(Key name, const QQmlImportRef *importNamespace) const
+ Result doQuery(Key name, const QQmlImportRef *importNamespace, QQmlTypeLoader *typeLoader) const
{
Q_ASSERT(importNamespace && importNamespace->scriptIndex == -1);
@@ -183,7 +185,7 @@ private:
QList<QQmlError> errors;
QQmlType t;
bool typeFound = m_imports->resolveType(
- qualifiedTypeName, &t, nullptr, &typeNamespace, &errors);
+ typeLoader, qualifiedTypeName, &t, nullptr, &typeNamespace, &errors);
if (typeFound)
return Result(t);
}
diff --git a/src/qml/qml/qqmltypenotavailable.cpp b/src/qml/qml/qqmltypenotavailable.cpp
deleted file mode 100644
index b3d33eb706..0000000000
--- a/src/qml/qml/qqmltypenotavailable.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qqmltypenotavailable_p.h"
-
-QT_BEGIN_NAMESPACE
-
-int qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message)
-{
- return qmlRegisterUncreatableType<QQmlTypeNotAvailable>(uri,versionMajor,versionMinor,qmlName,message);
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qqmltypenotavailable_p.cpp"
diff --git a/src/qml/qml/qqmltypenotavailable_p.h b/src/qml/qml/qqmltypenotavailable_p.h
deleted file mode 100644
index e198f3259a..0000000000
--- a/src/qml/qml/qqmltypenotavailable_p.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef QQMLTYPENOTAVAILABLE_H
-#define QQMLTYPENOTAVAILABLE_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <qqml.h>
-#include <private/qglobal_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class QQmlTypeNotAvailable : public QObject {
- Q_OBJECT
- QML_NAMED_ELEMENT(TypeNotAvailable)
- QML_ADDED_IN_VERSION(2, 15)
- QML_UNCREATABLE("Type not available.")
-};
-
-QT_END_NAMESPACE
-
-QML_DECLARE_TYPE(QQmlTypeNotAvailable)
-
-#endif // QQMLTYPENOTAVAILABLE_H
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index 5a3127104c..2ab81102c7 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -3,46 +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()
+
+void Heap::QQmlTypeWrapper::init(TypeNameMode m, QObject *o, const QQmlTypePrivate *type)
+{
+ Q_ASSERT(type);
+ FunctionObject::init();
+ flags = quint8(m) | quint8(Type);
+ object.init(o);
+ QQmlType::refHandle(type);
+ t.typePrivate = type;
+}
+
+void Heap::QQmlTypeWrapper::init(
+ TypeNameMode m, QObject *o, QQmlTypeNameCache *type, const QQmlImportRef *import)
{
- Object::init();
- mode = IncludeEnums;
- object.init();
+ Q_ASSERT(type);
+ FunctionObject::init();
+ flags = quint8(m) | quint8(Namespace);
+ object.init(o);
+ n.typeNamespace = type;
+ n.typeNamespace->addref();
+ n.importNamespace = import;
}
void Heap::QQmlTypeWrapper::destroy()
{
- 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
@@ -50,19 +126,48 @@ bool QQmlTypeWrapper::isSingleton() const
return d()->type().isSingleton();
}
+const QMetaObject *QQmlTypeWrapper::metaObject() const
+{
+ const QQmlType type = d()->type();
+ if (!type.isValid())
+ return nullptr;
+
+ if (type.isSingleton()) {
+ auto metaObjectCandidate = type.metaObject();
+ // if the candidate is the same as te baseMetaObject, we know that
+ // we don't have an extended singleton; in that case the
+ // actual instance might be subclass of type instead of type itself
+ // so we need to query the actual object for it's meta-object
+ if (metaObjectCandidate == type.baseMetaObject()) {
+ QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine()->qmlEngine());
+ auto object = qmlEngine->singletonInstance<QObject *>(type);
+ if (object)
+ return object->metaObject();
+ }
+ /* if we instead have an extended singleton, the dynamic proxy
+ meta-object must alreday be set up correctly
+ ### TODO: it isn't, as QQmlTypePrivate::init has no way to
+ query the object
+ */
+ return metaObjectCandidate;
+ }
+
+ return type.attachedPropertiesType(QQmlEnginePrivate::get(engine()->qmlEngine()));
+}
+
QObject *QQmlTypeWrapper::object() const
{
const QQmlType type = d()->type();
if (!type.isValid())
return nullptr;
- QQmlEngine *qmlEngine = engine()->qmlEngine();
+ QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine()->qmlEngine());
if (type.isSingleton())
- return QQmlEnginePrivate::get(qmlEngine)->singletonInstance<QObject *>(type);
+ return qmlEngine->singletonInstance<QObject *>(type);
return qmlAttachedPropertiesObject(
d()->object,
- type.attachedPropertiesFunction(QQmlEnginePrivate::get(qmlEngine)));
+ type.attachedPropertiesFunction(qmlEngine));
}
QObject* QQmlTypeWrapper::singletonObject() const
@@ -90,55 +195,83 @@ 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>());
- w->d()->mode = mode; w->d()->object = o;
- w->d()->typePrivate = t.priv();
- QQmlType::refHandle(w->d()->typePrivate);
- 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
// namespace.
-ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, const QQmlRefPointer<QQmlTypeNameCache> &t, const QQmlImportRef *importNamespace,
- Heap::QQmlTypeWrapper::TypeNameMode mode)
+ReturnedValue QQmlTypeWrapper::create(
+ QV4::ExecutionEngine *engine, QObject *o, const QQmlRefPointer<QQmlTypeNameCache> &t,
+ const QQmlImportRef *importNamespace, Heap::QQmlTypeWrapper::TypeNameMode mode)
{
Q_ASSERT(t);
Q_ASSERT(importNamespace);
+ initProto(engine);
+
Scope scope(engine);
- Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>());
- w->d()->mode = mode; w->d()->object = o; w->d()->typeNamespace = t.data(); w->d()->importNamespace = importNamespace;
- t->addref();
+ Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>(
+ mode, o, t.data(), importNamespace));
return w.asReturnedValue();
}
-static int enumForSingleton(QV4::ExecutionEngine *v4, String *name, QObject *qobjectSingleton,
- const QQmlType &type, bool *ok)
+static int enumForSingleton(QV4::ExecutionEngine *v4, String *name, const QQmlType &type, bool *ok)
{
Q_ASSERT(ok != nullptr);
- int value = type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, ok);
- if (*ok)
- return value;
-
- // ### Optimize
- QByteArray enumName = name->toQString().toUtf8();
- const QMetaObject *metaObject = qobjectSingleton->metaObject();
- for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) {
- QMetaEnum e = metaObject->enumerator(ii);
- value = e.keyToValue(enumName.constData(), ok);
- if (*ok)
- return value;
- }
- *ok = false;
- return -1;
+ const int value = type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, ok);
+ return *ok ? value : -1;
}
ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
@@ -149,7 +282,7 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons
if (!id.isString())
return Object::virtualGet(m, id, receiver, hasProperty);
- QV4::ExecutionEngine *v4 = static_cast<const QQmlTypeWrapper *>(m)->engine();
+ QV4::ExecutionEngine *v4 = m->engine();
QV4::Scope scope(v4);
ScopedString name(scope, id.asStringOrSymbol());
@@ -163,23 +296,25 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons
QObject *object = w->d()->object;
QQmlType type = w->d()->type();
+ QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(v4->qmlEngine());
if (type.isValid()) {
// singleton types are handled differently to other types.
if (type.isSingleton()) {
- QQmlEnginePrivate *e = QQmlEnginePrivate::get(v4->qmlEngine());
+
QJSValue scriptSingleton;
if (type.isQObjectSingleton() || type.isCompositeSingleton()) {
- if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) {
+ 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, qobjectSingleton, type, &ok);
+ int value = enumForSingleton(v4, name, type, &ok);
if (ok)
return QV4::Value::fromInt32(value).asReturnedValue();
- value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
+ value = type.scopedEnumIndex(enginePrivate, name, &ok);
if (ok) {
Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>());
enumWrapper->d()->typePrivate = type.priv();
@@ -200,7 +335,7 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons
return result;
}
} else if (type.isQJSValueSingleton()) {
- QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type);
+ QJSValue scriptSingleton = enginePrivate->singletonInstance<QJSValue>(type);
if (!scriptSingleton.isUndefined()) {
// NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable.
QV4::ScopedObject o(scope, QJSValuePrivate::asReturnedValue(&scriptSingleton));
@@ -215,11 +350,11 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons
if (name->startsWithUpper()) {
bool ok = false;
- int value = type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
+ int value = type.enumValue(enginePrivate, name, &ok);
if (ok)
return QV4::Value::fromInt32(value).asReturnedValue();
- value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
+ value = type.scopedEnumIndex(enginePrivate, name, &ok);
if (ok) {
Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>());
enumWrapper->d()->typePrivate = type.priv();
@@ -247,13 +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);
-
+ } 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);
@@ -357,23 +490,16 @@ bool QQmlTypeWrapper::virtualIsEqualTo(Managed *a, Managed *b)
return false;
}
-ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const Value &var)
+static ReturnedValue instanceOfQObject(
+ const QV4::QQmlTypeWrapper *typeWrapper, QObject *wrapperObject)
{
- Q_ASSERT(typeObject->as<QV4::QQmlTypeWrapper>());
- const QV4::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::QQmlTypeWrapper *>(typeObject);
-
- // can only compare a QObject* against a QML type
- const QObjectWrapper *wrapper = var.as<QObjectWrapper>();
- if (!wrapper)
- return QV4::Encode(false);
-
- QV4::ExecutionEngine *engine = typeObject->internalClass()->engine;
+ QV4::ExecutionEngine *engine = typeWrapper->internalClass()->engine;
// in case the wrapper outlived the QObject*
- const QObject *wrapperObject = wrapper->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
@@ -386,17 +512,72 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const
QQmlEnginePrivate *qenginepriv = QQmlEnginePrivate::get(engine->qmlEngine());
QQmlRefPointer<QQmlTypeData> td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl());
- if (ExecutableCompilationUnit *cu = td->compilationUnit())
- myQmlType = QQmlMetaType::metaObjectForType(cu->typeIds.id);
+ if (CompiledData::CompilationUnit *cu = td->compilationUnit())
+ myQmlType = QQmlMetaType::metaObjectForType(cu->metaType());
else
return Encode(false); // It seems myQmlType has some errors, so we could not compile it.
} else {
myQmlType = QQmlMetaType::metaObjectForType(myTypeId);
+ if (myQmlType.isNull())
+ return Encode(false);
}
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)
+{
+ Q_ASSERT(typeObject->as<QV4::QQmlTypeWrapper>());
+ const QV4::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::QQmlTypeWrapper *>(typeObject);
+
+ if (const QObjectWrapper *objectWrapper = var.as<QObjectWrapper>())
+ 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 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 QQmlMetaObject::canConvert(
+ valueWrapper->metaObject(), type.metaObjectForValueType());
+ }
+
+ 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();
+ }
+
+ 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)
@@ -420,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) {
@@ -428,8 +610,9 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object,
if (property) {
ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton)));
if (qualifiesForMethodLookup(property)) {
+ QV4::Heap::QObjectMethod *method = nullptr;
setupQObjectMethodLookup(
- lookup, ddata, property, val->objectValue(), nullptr);
+ lookup, ddata, property, val->objectValue(), method);
lookup->getter = QQmlTypeWrapper::lookupSingletonMethod;
} else {
setupQObjectLookup(
@@ -453,7 +636,7 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object,
bool ok = false;
int value = type.enumValue(QQmlEnginePrivate::get(engine->qmlEngine()), name, &ok);
if (ok) {
- lookup->qmlEnumValueLookup.ic = This->internalClass();
+ lookup->qmlEnumValueLookup.ic.set(engine, This->internalClass());
lookup->qmlEnumValueLookup.encodedEnumValue
= QV4::Value::fromInt32(value).asReturnedValue();
lookup->getter = QQmlTypeWrapper::lookupEnumValue;
@@ -468,9 +651,9 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object,
QQmlType::refHandle(enumWrapper->d()->typePrivate);
enumWrapper->d()->scopeEnumIndex = value;
- lookup->qmlScopedEnumWrapperLookup.ic = This->internalClass();
- lookup->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper
- = static_cast<Heap::Object*>(enumWrapper->heapObject());
+ lookup->qmlScopedEnumWrapperLookup.ic.set(engine, This->internalClass());
+ lookup->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper.set(engine,
+ static_cast<Heap::Object*>(enumWrapper->heapObject()));
lookup->getter = QQmlTypeWrapper::lookupScopedEnum;
return enumWrapper.asReturnedValue();
}
@@ -603,12 +786,12 @@ ReturnedValue QQmlTypeWrapper::lookupScopedEnum(Lookup *l, ExecutionEngine *engi
{
Scope scope(engine);
Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, static_cast<Heap::QQmlScopedEnumWrapper *>(
- l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper));
+ l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper.get()));
auto *o = static_cast<Heap::Object *>(base.heapObject());
if (!o || o->internalClass != l->qmlScopedEnumWrapperLookup.ic) {
QQmlType::derefHandle(enumWrapper->d()->typePrivate);
- l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper = nullptr;
+ l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper.clear();
l->getter = Lookup::getterGeneric;
return Lookup::getterGeneric(l, engine, base);
}
diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h
index ff2dda0a02..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,24 +33,65 @@ namespace QV4 {
namespace Heap {
-struct QQmlTypeWrapper : Object {
- enum TypeNameMode {
- IncludeEnums,
- ExcludeEnums
+struct QQmlTypeWrapper : FunctionObject {
+
+ enum TypeNameMode : quint8 {
+ ExcludeEnums = 0x0,
+ IncludeEnums = 0x1,
+ TypeNameModeMask = 0x1,
+ };
+
+ enum Kind : quint8 {
+ Type = 0x0,
+ Namespace = 0x2,
+ KindMask = 0x2
};
- void init();
+ void init(TypeNameMode m, QObject *o, const QQmlTypePrivate *type);
+ void init(TypeNameMode m, QObject *o, QQmlTypeNameCache *type, const QQmlImportRef *import);
+
void destroy();
- TypeNameMode mode;
- QV4QPointer<QObject> object;
+
+ 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;
- const QQmlTypePrivate *typePrivate;
- QQmlTypeNameCache *typeNamespace;
- const QQmlImportRef *importNamespace;
+ QV4QPointer<QObject> object;
+
+ union {
+ struct {
+ const QQmlTypePrivate *typePrivate;
+ const QQmlPropertyData *constructors;
+ } t;
+ struct {
+ QQmlTypeNameCache *typeNamespace;
+ const QQmlImportRef *importNamespace;
+ } n;
+ };
+
+ quint8 flags;
};
+using QQmlTypeConstructor = QQmlTypeWrapper;
+
struct QQmlScopedEnumWrapper : Object {
void init() { Object::init(); }
void destroy();
@@ -60,17 +102,21 @@ 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;
+ const QMetaObject *metaObject() const;
QObject *object() const;
QObject *singletonObject() const;
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 *,
@@ -92,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
diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp
index 428ce9b697..4088d6e6c4 100644
--- a/src/qml/qml/qqmlvaluetype.cpp
+++ b/src/qml/qml/qqmlvaluetype.cpp
@@ -55,16 +55,25 @@ void QQmlGadgetPtrWrapper::write(
QVariant QQmlGadgetPtrWrapper::value() const
{
Q_ASSERT(m_gadgetPtr);
- return QVariant(metaType(), m_gadgetPtr);
+
+ const QMetaType m = metaType();
+ return m == QMetaType::fromType<QVariant>()
+ ? *static_cast<const QVariant *>(m_gadgetPtr)
+ : QVariant(m, m_gadgetPtr);
}
void QQmlGadgetPtrWrapper::setValue(const QVariant &value)
{
Q_ASSERT(m_gadgetPtr);
- Q_ASSERT(metaType() == value.metaType());
- const QQmlValueType *type = valueType();
- type->destruct(m_gadgetPtr);
- type->construct(m_gadgetPtr, value.constData());
+
+ const QMetaType m = metaType();
+ m.destruct(m_gadgetPtr);
+ if (m == QMetaType::fromType<QVariant>()) {
+ m.construct(m_gadgetPtr, &value);
+ } else {
+ Q_ASSERT(m == value.metaType());
+ m.construct(m_gadgetPtr, value.constData());
+ }
}
int QQmlGadgetPtrWrapper::metaCall(QMetaObject::Call type, int id, void **argv)
diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h
index bc7a210cdf..dd23547c04 100644
--- a/src/qml/qml/qqmlvaluetype_p.h
+++ b/src/qml/qml/qqmlvaluetype_p.h
@@ -30,7 +30,7 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlValueType : public QDynamicMetaObjectData
+class Q_QML_EXPORT QQmlValueType : public QDynamicMetaObjectData
{
public:
QQmlValueType() = default;
@@ -60,7 +60,7 @@ private:
QMetaObject *m_dynamicMetaObject = nullptr;
};
-class Q_QML_PRIVATE_EXPORT QQmlGadgetPtrWrapper : public QObject
+class Q_QML_EXPORT QQmlGadgetPtrWrapper : public QObject
{
Q_OBJECT
public:
@@ -93,12 +93,17 @@ public:
property.writeOnGadget(m_gadgetPtr, value);
}
+ void writeOnGadget(const QMetaProperty &property, QVariant &&value)
+ {
+ property.writeOnGadget(m_gadgetPtr, std::move(value));
+ }
+
private:
const QQmlValueType *valueType() const;
void *m_gadgetPtr = nullptr;
};
-struct Q_QML_PRIVATE_EXPORT QQmlPointFValueType
+struct Q_QML_EXPORT QQmlPointFValueType
{
QPointF v;
Q_PROPERTY(qreal x READ x WRITE setX FINAL)
@@ -106,21 +111,22 @@ struct Q_QML_PRIVATE_EXPORT QQmlPointFValueType
Q_GADGET
QML_VALUE_TYPE(point)
QML_FOREIGN(QPointF)
- QML_ADDED_IN_VERSION(2, 0)
QML_EXTENDED(QQmlPointFValueType)
QML_STRUCTURED_VALUE
public:
- QQmlPointFValueType() = default;
+ Q_INVOKABLE QQmlPointFValueType() = default;
Q_INVOKABLE QQmlPointFValueType(const QPoint &point) : v(point) {}
Q_INVOKABLE QString toString() const;
qreal x() const;
qreal y() const;
void setX(qreal);
void setY(qreal);
+
+ operator QPointF() const { return v; }
};
-struct Q_QML_PRIVATE_EXPORT QQmlPointValueType
+struct Q_QML_EXPORT QQmlPointValueType
{
QPoint v;
Q_PROPERTY(int x READ x WRITE setX FINAL)
@@ -128,19 +134,22 @@ struct Q_QML_PRIVATE_EXPORT QQmlPointValueType
Q_GADGET
QML_ANONYMOUS
QML_FOREIGN(QPoint)
- QML_ADDED_IN_VERSION(2, 0)
QML_EXTENDED(QQmlPointValueType)
QML_STRUCTURED_VALUE
public:
+ QQmlPointValueType() = default;
+ Q_INVOKABLE QQmlPointValueType(const QPointF &point) : v(point.toPoint()) {}
Q_INVOKABLE QString toString() const;
int x() const;
int y() const;
void setX(int);
void setY(int);
+
+ operator QPoint() const { return v; }
};
-struct Q_QML_PRIVATE_EXPORT QQmlSizeFValueType
+struct Q_QML_EXPORT QQmlSizeFValueType
{
QSizeF v;
Q_PROPERTY(qreal width READ width WRITE setWidth FINAL)
@@ -148,21 +157,22 @@ struct Q_QML_PRIVATE_EXPORT QQmlSizeFValueType
Q_GADGET
QML_VALUE_TYPE(size)
QML_FOREIGN(QSizeF)
- QML_ADDED_IN_VERSION(2, 0)
QML_EXTENDED(QQmlSizeFValueType)
QML_STRUCTURED_VALUE
public:
- QQmlSizeFValueType() = default;
+ Q_INVOKABLE QQmlSizeFValueType() = default;
Q_INVOKABLE QQmlSizeFValueType(const QSize &size) : v(size) {}
Q_INVOKABLE QString toString() const;
qreal width() const;
qreal height() const;
void setWidth(qreal);
void setHeight(qreal);
+
+ operator QSizeF() const { return v; }
};
-struct Q_QML_PRIVATE_EXPORT QQmlSizeValueType
+struct Q_QML_EXPORT QQmlSizeValueType
{
QSize v;
Q_PROPERTY(int width READ width WRITE setWidth FINAL)
@@ -170,19 +180,22 @@ struct Q_QML_PRIVATE_EXPORT QQmlSizeValueType
Q_GADGET
QML_ANONYMOUS
QML_FOREIGN(QSize)
- QML_ADDED_IN_VERSION(2, 0)
QML_EXTENDED(QQmlSizeValueType)
QML_STRUCTURED_VALUE
public:
+ QQmlSizeValueType() = default;
+ Q_INVOKABLE QQmlSizeValueType(const QSizeF &size) : v(size.toSize()) {}
Q_INVOKABLE QString toString() const;
int width() const;
int height() const;
void setWidth(int);
void setHeight(int);
+
+ operator QSize() const { return v; }
};
-struct Q_QML_PRIVATE_EXPORT QQmlRectFValueType
+struct Q_QML_EXPORT QQmlRectFValueType
{
QRectF v;
Q_PROPERTY(qreal x READ x WRITE setX FINAL)
@@ -196,12 +209,11 @@ struct Q_QML_PRIVATE_EXPORT QQmlRectFValueType
Q_GADGET
QML_VALUE_TYPE(rect)
QML_FOREIGN(QRectF)
- QML_ADDED_IN_VERSION(2, 0)
QML_EXTENDED(QQmlRectFValueType)
QML_STRUCTURED_VALUE
public:
- QQmlRectFValueType() = default;
+ Q_INVOKABLE QQmlRectFValueType() = default;
Q_INVOKABLE QQmlRectFValueType(const QRect &rect) : v(rect) {}
Q_INVOKABLE QString toString() const;
qreal x() const;
@@ -218,9 +230,11 @@ public:
qreal right() const;
qreal top() const;
qreal bottom() const;
+
+ operator QRectF() const { return v; }
};
-struct Q_QML_PRIVATE_EXPORT QQmlRectValueType
+struct Q_QML_EXPORT QQmlRectValueType
{
QRect v;
Q_PROPERTY(int x READ x WRITE setX FINAL)
@@ -234,11 +248,12 @@ struct Q_QML_PRIVATE_EXPORT QQmlRectValueType
Q_GADGET
QML_ANONYMOUS
QML_FOREIGN(QRect)
- QML_ADDED_IN_VERSION(2, 0)
QML_EXTENDED(QQmlRectValueType)
QML_STRUCTURED_VALUE
public:
+ QQmlRectValueType() = default;
+ Q_INVOKABLE QQmlRectValueType(const QRectF &rect) : v(rect.toRect()) {}
Q_INVOKABLE QString toString() const;
int x() const;
int y() const;
@@ -254,14 +269,15 @@ public:
int right() const;
int top() const;
int bottom() const;
+
+ operator QRect() const { return v; }
};
#if QT_CONFIG(easingcurve)
namespace QQmlEasingEnums
{
-Q_NAMESPACE_EXPORT(Q_QML_PRIVATE_EXPORT)
+Q_NAMESPACE_EXPORT(Q_QML_EXPORT)
QML_NAMED_ELEMENT(Easing)
-QML_ADDED_IN_VERSION(2, 0)
enum Type {
Linear = QEasingCurve::Linear,
@@ -294,13 +310,12 @@ enum Type {
Q_ENUM_NS(Type)
};
-struct Q_QML_PRIVATE_EXPORT QQmlEasingValueType
+struct Q_QML_EXPORT QQmlEasingValueType
{
QEasingCurve v;
Q_GADGET
QML_ANONYMOUS
QML_FOREIGN(QEasingCurve)
- QML_ADDED_IN_VERSION(2, 0)
QML_EXTENDED(QQmlEasingValueType)
QML_STRUCTURED_VALUE
@@ -321,9 +336,19 @@ public:
void setPeriod(qreal);
void setBezierCurve(const QVariantList &);
QVariantList bezierCurve() const;
+
+ operator QEasingCurve() const { return v; }
};
#endif
+struct QQmlV4ExecutionEnginePtrForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QQmlV4ExecutionEnginePtr)
+ QML_EXTENDED(QQmlV4ExecutionEnginePtrForeign)
+};
+
QT_END_NAMESPACE
#endif // QQMLVALUETYPE_P_H
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index 4ef757b8e1..a85601e5b9 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -3,41 +3,34 @@
#include "qqmlvaluetypewrapper_p.h"
-#include <private/qqmlvaluetype_p.h>
#include <private/qqmlbinding_p.h>
-#include <private/qqmlglobal_p.h>
#include <private/qqmlbuiltinfunctions_p.h>
+#include <private/qqmlvaluetype_p.h>
-#include <private/qv4engine_p.h>
-#include <private/qv4functionobject_p.h>
-#include <private/qv4variantobject_p.h>
#include <private/qv4alloca_p.h>
-#include <private/qv4stackframe_p.h>
-#include <private/qv4objectiterator_p.h>
-#include <private/qv4qobjectwrapper_p.h>
-#include <private/qv4identifiertable_p.h>
-#include <private/qv4lookup_p.h>
-#include <private/qv4sequenceobject_p.h>
#include <private/qv4arraybuffer_p.h>
#include <private/qv4dateobject_p.h>
+#include <private/qv4engine_p.h>
+#include <private/qv4functionobject_p.h>
+#include <private/qv4identifiertable_p.h>
#include <private/qv4jsonobject_p.h>
+#include <private/qv4lookup_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4stackframe_p.h>
+#include <private/qv4variantobject_p.h>
+
+#include <QtCore/qline.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qloggingcategory.h>
+
#if QT_CONFIG(regularexpression)
#include <private/qv4regexpobject_p.h>
#endif
-#if QT_CONFIG(qml_locale)
-#include <private/qqmllocale_p.h>
-#endif
-#include <QtCore/qloggingcategory.h>
-#include <QtCore/qdatetime.h>
-#include <QtCore/QLine>
-#include <QtCore/QLineF>
-#include <QtCore/QSize>
-#include <QtCore/QSizeF>
-#include <QtCore/QTimeZone>
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcBindingRemoval)
+Q_DECLARE_LOGGING_CATEGORY(lcBuiltinsBindingRemoval)
DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeWrapper);
@@ -46,31 +39,22 @@ namespace QV4 {
Heap::QQmlValueTypeWrapper *Heap::QQmlValueTypeWrapper::detached() const
{
return internalClass->engine->memoryManager->allocate<QV4::QQmlValueTypeWrapper>(
- m_gadgetPtr, m_valueType, m_metaObject, nullptr, -1, NoFlag);
+ m_gadgetPtr, QMetaType(m_metaType), m_metaObject, nullptr, -1, NoFlag);
}
void Heap::QQmlValueTypeWrapper::destroy()
{
if (m_gadgetPtr) {
- m_valueType->metaType().destruct(m_gadgetPtr);
+ metaType().destruct(m_gadgetPtr);
::operator delete(m_gadgetPtr);
}
ReferenceObject::destroy();
}
-void Heap::QQmlValueTypeWrapper::setData(const void *data)
-{
- if (auto *gadget = gadgetPtr())
- valueType()->metaType().destruct(gadget);
- if (!gadgetPtr())
- setGadgetPtr(::operator new(valueType()->metaType().sizeOf()));
- valueType()->metaType().construct(gadgetPtr(), data);
-}
-
QVariant Heap::QQmlValueTypeWrapper::toVariant() const
{
Q_ASSERT(gadgetPtr());
- return QVariant(valueType()->metaType(), gadgetPtr());
+ return QVariant(metaType(), gadgetPtr());
}
bool Heap::QQmlValueTypeWrapper::setVariant(const QVariant &variant)
@@ -78,7 +62,7 @@ bool Heap::QQmlValueTypeWrapper::setVariant(const QVariant &variant)
Q_ASSERT(isVariant());
const QMetaType variantReferenceType = variant.metaType();
- if (variantReferenceType != valueType()->metaType()) {
+ if (variantReferenceType != metaType()) {
// This is a stale VariantReference. That is, the variant has been
// overwritten with a different type in the meantime.
// We need to modify this reference to the updated value type, if
@@ -86,12 +70,12 @@ bool Heap::QQmlValueTypeWrapper::setVariant(const QVariant &variant)
if (QQmlMetaType::isValueType(variantReferenceType)) {
const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(variantReferenceType);
if (gadgetPtr()) {
- valueType()->metaType().destruct(gadgetPtr());
+ metaType().destruct(gadgetPtr());
::operator delete(gadgetPtr());
}
setGadgetPtr(nullptr);
setMetaObject(mo);
- setValueType(QQmlMetaType::valueType(variantReferenceType));
+ setMetaType(variantReferenceType);
if (!mo)
return false;
} else {
@@ -106,8 +90,8 @@ bool Heap::QQmlValueTypeWrapper::setVariant(const QVariant &variant)
void *Heap::QQmlValueTypeWrapper::storagePointer()
{
if (!gadgetPtr()) {
- setGadgetPtr(::operator new(valueType()->metaType().sizeOf()));
- valueType()->metaType().construct(gadgetPtr(), nullptr);
+ setGadgetPtr(::operator new(metaType().sizeOf()));
+ metaType().construct(gadgetPtr(), nullptr);
}
return gadgetPtr();
}
@@ -132,7 +116,7 @@ ReturnedValue QQmlValueTypeWrapper::create(
// Either we're enforcing the location, then we have to read right away.
// Or we don't then we lazy-load. In neither case we pass any data.
Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>(
- nullptr, cloneFrom->valueType(), cloneFrom->metaObject(),
+ nullptr, cloneFrom->metaType(), cloneFrom->metaObject(),
object, cloneFrom->property(), cloneFrom->flags()));
r->d()->setLocation(cloneFrom->function(), cloneFrom->statementIndex());
if (cloneFrom->enforcesLocation())
@@ -202,15 +186,14 @@ ReturnedValue QQmlValueTypeWrapper::create(
Scope scope(engine);
initProto(engine);
- auto valueType = QQmlMetaType::valueType(type);
- if (!valueType) {
+ if (!type.isValid()) {
return engine->throwTypeError(QLatin1String("Type %1 is not a value type")
.arg(QString::fromUtf8(type.name())));
}
// If data is given explicitly, we assume it has just been read from the property
Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>(
- data, valueType, metaObject, object, property, flags));
+ data, type, metaObject, object, property, flags));
if (CppStackFrame *frame = engine->currentStackFrame)
r->d()->setLocation(frame->v4Function, frame->statementNumber());
if (!data && r->d()->enforcesLocation())
@@ -224,15 +207,14 @@ ReturnedValue QQmlValueTypeWrapper::create(
Scope scope(engine);
initProto(engine);
- auto valueType = QQmlMetaType::valueType(type);
- if (!valueType) {
+ if (!type.isValid()) {
return engine->throwTypeError(QLatin1String("Type %1 is not a value type")
.arg(QString::fromUtf8(type.name())));
}
Scoped<QQmlValueTypeWrapper> r(
scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>(
- data, valueType, metaObject, nullptr, -1, Heap::ReferenceObject::NoFlag));
+ data, type, metaObject, nullptr, -1, Heap::ReferenceObject::NoFlag));
return r->asReturnedValue();
}
@@ -247,7 +229,7 @@ bool QQmlValueTypeWrapper::toGadget(void *data) const
{
if (d()->isReference() && !readReferenceValue())
return false;
- const QMetaType type = d()->valueType()->metaType();
+ const QMetaType type = d()->metaType();
type.destruct(data);
type.construct(data, d()->gadgetPtr());
return true;
@@ -315,7 +297,7 @@ static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
{
if (isFunction) {
// calling a Q_INVOKABLE function of a value type
- return QV4::QObjectMethod::create(engine->rootContext(), valueTypeWrapper, coreIndex);
+ return QV4::QObjectMethod::create(engine, valueTypeWrapper, coreIndex);
}
const QMetaObject *metaObject = valueTypeWrapper->metaObject();
@@ -350,12 +332,6 @@ static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
time, valueTypeWrapper, index, referenceFlags(metaObject, index));
};
-#if QT_CONFIG(qml_locale)
- const auto wrapLocale = [engine](const QLocale &locale) {
- return QQmlLocale::wrap(engine, locale);
- };
-#endif
-
#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
case metatype: { \
cpptype v; \
@@ -368,7 +344,7 @@ static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
QMetaObject::ReadProperty, &metaObject, &index);
const int metaTypeId = isEnum
- ? QMetaType::Int
+ ? metaType.underlyingType().id()
: (metaType.flags() & QMetaType::PointerToQObject)
? QMetaType::QObjectStar
: metaType.id();
@@ -383,6 +359,8 @@ static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
VALUE_TYPE_LOAD(QMetaType::Int, int, int);
VALUE_TYPE_LOAD(QMetaType::UInt, uint, uint);
+ VALUE_TYPE_LOAD(QMetaType::Long, long, double);
+ VALUE_TYPE_LOAD(QMetaType::ULong, ulong, double);
VALUE_TYPE_LOAD(QMetaType::LongLong, qlonglong, double);
VALUE_TYPE_LOAD(QMetaType::ULongLong, qulonglong, double);
VALUE_TYPE_LOAD(QMetaType::Double, double, double);
@@ -406,9 +384,6 @@ static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
VALUE_TYPE_LOAD(QMetaType::QJsonValue, QJsonValue, wrapJsonValue);
VALUE_TYPE_LOAD(QMetaType::QJsonObject, QJsonObject, wrapJsonObject);
VALUE_TYPE_LOAD(QMetaType::QJsonArray, QJsonArray, wrapJsonArray);
-#if QT_CONFIG(qml_locale)
- VALUE_TYPE_LOAD(QMetaType::QLocale, QLocale, wrapLocale);
-#endif
case QMetaType::QPixmap:
case QMetaType::QImage: {
QVariant v(metaType);
@@ -557,12 +532,12 @@ bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const
int QQmlValueTypeWrapper::typeId() const
{
- return d()->valueType()->metaType().id();
+ return d()->metaType().id();
}
QMetaType QQmlValueTypeWrapper::type() const
{
- return d()->valueType()->metaType();
+ return d()->metaType();
}
bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
@@ -571,9 +546,9 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
Q_ALLOCA_DECLARE(void, gadget);
if (d()->isReference()) {
if (!d()->gadgetPtr()) {
- Q_ALLOCA_ASSIGN(void, gadget, d()->valueType()->metaType().sizeOf());
+ Q_ALLOCA_ASSIGN(void, gadget, d()->metaType().sizeOf());
d()->setGadgetPtr(gadget);
- d()->valueType()->metaType().construct(d()->gadgetPtr(), nullptr);
+ d()->metaType().construct(d()->gadgetPtr(), nullptr);
destructGadgetOnExit = true;
}
if (!readReferenceValue())
@@ -586,7 +561,7 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
QMetaObject::metacall(target, QMetaObject::WriteProperty, propertyIndex, a);
if (destructGadgetOnExit) {
- d()->valueType()->metaType().destruct(d()->gadgetPtr());
+ d()->metaType().destruct(d()->gadgetPtr());
d()->setGadgetPtr(nullptr);
}
return true;
@@ -623,9 +598,9 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con
RETURN_UNDEFINED();
QString result;
- if (!QMetaType::convert(w->d()->valueType()->metaType(), w->d()->gadgetPtr(),
+ if (!QMetaType::convert(w->d()->metaType(), w->d()->gadgetPtr(),
QMetaType(QMetaType::QString), &result)) {
- result = QString::fromUtf8(w->d()->valueType()->metaType().name()) + QLatin1Char('(');
+ result = QString::fromUtf8(w->d()->metaType().name()) + QLatin1Char('(');
const QMetaObject *mo = w->d()->metaObject();
const int propCount = mo->propertyCount();
for (int i = 0; i < propCount; ++i) {
@@ -662,7 +637,7 @@ ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *obj
if (!result.isValid())
return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
- lookup->qgadgetLookup.ic = r->internalClass();
+ lookup->qgadgetLookup.ic.set(engine, r->internalClass());
// & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h
lookup->qgadgetLookup.metaObject = quintptr(r->d()->metaObject()) + 1;
lookup->qgadgetLookup.metaType = result.propType().iface();
@@ -803,7 +778,7 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v
QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
- QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction());
+ QV4::Scoped<JavaScriptFunctionObject> f(scope, bindingFunction->bindingFunction());
QV4::ScopedContext ctx(scope, f->scope());
QQmlBinding *newBinding = QQmlBinding::create(&cacheData, f->function(), referenceObject, context, ctx);
newBinding->setSourceLocation(bindingFunction->currentLocation());
@@ -814,12 +789,12 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v
QQmlPropertyPrivate::setBinding(newBinding);
return true;
} else if (referenceObject) {
- if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) {
+ if (Q_UNLIKELY(lcBuiltinsBindingRemoval().isInfoEnabled())) {
if (auto binding = QQmlPropertyPrivate::binding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd.coreIndex()))) {
Q_ASSERT(binding->kind() == QQmlAbstractBinding::QmlBinding);
const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
const auto stackFrame = v4->currentStackFrame;
- qCInfo(lcBindingRemoval,
+ qCInfo(lcBuiltinsBindingRemoval,
"Overwriting binding on %s::%s which was initially bound at %s by setting \"%s\" at %s:%d",
referenceObject->metaObject()->className(), referenceObject->metaObject()->property(referencePropertyIndex).name(),
qPrintable(qmlBinding->expressionIdentifier()),
@@ -833,6 +808,12 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v
QMetaProperty property = metaObject->property(pd.coreIndex());
Q_ASSERT(property.isValid());
+ if (value.isUndefined() && pd.isResettable()) {
+ property.resetOnGadget(reinterpret_cast<QObject *>(r->d()->gadgetPtr()));
+ if (heapObject)
+ r->d()->writeBack(pd.coreIndex());
+ return true;
+ }
QVariant v = QV4::ExecutionEngine::toVariant(value, property.metaType());
@@ -840,7 +821,7 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v
v = v.toInt();
void *gadget = r->d()->gadgetPtr();
- property.writeOnGadget(gadget, v);
+ property.writeOnGadget(gadget, std::move(v));
if (heapObject)
r->d()->writeBack(pd.coreIndex());
diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h
index 0c4cbb7dd3..97709dfb4c 100644
--- a/src/qml/qml/qqmlvaluetypewrapper_p.h
+++ b/src/qml/qml/qqmlvaluetypewrapper_p.h
@@ -19,14 +19,6 @@
#include <private/qtqmlglobal_p.h>
#include <private/qv4referenceobject_p.h>
-#include <private/qqmlpropertycache_p.h>
-#include <private/qqmltype_p_p.h>
-#include <private/qqmltypewrapper_p.h>
-#include <private/qv4object_p.h>
-#include <private/qv4qobjectwrapper_p.h>
-#include <private/qv4sequenceobject_p.h>
-#include <private/qv4value_p.h>
-#include <private/qv4referenceobject_p.h>
QT_BEGIN_NAMESPACE
@@ -41,11 +33,12 @@ namespace Heap {
DECLARE_HEAP_OBJECT(QQmlValueTypeWrapper, ReferenceObject) {
DECLARE_MARKOBJECTS(QQmlValueTypeWrapper);
- void init(const void *data, QQmlValueType *valueType, const QMetaObject *metaObject,
- Object *object, int property, Flags flags)
+ void init(
+ const void *data, QMetaType metaType, const QMetaObject *metaObject,
+ Object *object, int property, Flags flags)
{
ReferenceObject::init(object, property, flags);
- setValueType(valueType);
+ setMetaType(metaType);
setMetaObject(metaObject);
if (data)
setData(data);
@@ -55,10 +48,10 @@ DECLARE_HEAP_OBJECT(QQmlValueTypeWrapper, ReferenceObject) {
void destroy();
- QQmlValueType *valueType() const
+ QMetaType metaType() const
{
- Q_ASSERT(m_valueType != nullptr);
- return m_valueType;
+ Q_ASSERT(m_metaType != nullptr);
+ return QMetaType(m_metaType);
}
void setGadgetPtr(void *gadgetPtr) { m_gadgetPtr = gadgetPtr; }
@@ -66,7 +59,19 @@ DECLARE_HEAP_OBJECT(QQmlValueTypeWrapper, ReferenceObject) {
const QMetaObject *metaObject() const { return m_metaObject; }
- void setData(const void *data);
+ void setData(const void *data)
+ {
+ const QMetaType type = metaType();
+ void *gadget = gadgetPtr();
+ if (gadget) {
+ type.destruct(gadget);
+ } else {
+ gadget = ::operator new(type.sizeOf());
+ setGadgetPtr(gadget);
+ }
+ type.construct(gadget, data);
+ }
+
QVariant toVariant() const;
void *storagePointer();
@@ -77,14 +82,14 @@ DECLARE_HEAP_OBJECT(QQmlValueTypeWrapper, ReferenceObject) {
private:
void setMetaObject(const QMetaObject *metaObject) { m_metaObject = metaObject; }
- void setValueType(QQmlValueType *valueType)
+ void setMetaType(QMetaType metaType)
{
- Q_ASSERT(valueType != nullptr);
- m_valueType = valueType;
+ Q_ASSERT(metaType.isValid());
+ m_metaType = metaType.iface();
}
void *m_gadgetPtr;
- QQmlValueType *m_valueType;
+ const QtPrivate::QMetaTypeInterface *m_metaType;
const QMetaObject *m_metaObject;
};
@@ -107,12 +112,24 @@ public:
ExecutionEngine *engine, const void *, const QMetaObject *metaObject, QMetaType type);
QVariant toVariant() const;
+
+ template<typename ValueType>
+ ValueType *cast()
+ {
+ if (QMetaType::fromType<ValueType>() != d()->metaType())
+ return nullptr;
+ if (d()->isReference() && !readReferenceValue())
+ return nullptr;
+ return static_cast<ValueType *>(d()->gadgetPtr());
+ }
+
bool toGadget(void *data) const;
bool isEqual(const QVariant& value) const;
int typeId() const;
QMetaType type() const;
bool write(QObject *target, int propertyIndex) const;
bool readReferenceValue() const { return d()->readReference(); }
+ const QMetaObject *metaObject() const { return d()->metaObject(); }
QQmlPropertyData dataForPropertyKey(PropertyKey id) const;
diff --git a/src/qml/qml/qqmlvme_p.h b/src/qml/qml/qqmlvme_p.h
index b4aae29a93..b8dfc28050 100644
--- a/src/qml/qml/qqmlvme_p.h
+++ b/src/qml/qml/qqmlvme_p.h
@@ -48,7 +48,7 @@ private:
std::atomic<bool> *runWhile = nullptr;
};
-class Q_QML_PRIVATE_EXPORT QQmlVME
+class Q_QML_EXPORT QQmlVME
{
public:
static void enableComponentComplete();
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index c863fa6647..dffddd2e0d 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -21,105 +21,131 @@
#include <private/qqmlpropertycachemethodarguments_p.h>
#include <private/qqmlvaluetypewrapper_p.h>
+#include <QtCore/qsequentialiterable.h>
+
#include <climits> // for CHAR_BIT
QT_BEGIN_NAMESPACE
-class ResolvedList
+QQmlVMEResolvedList::QQmlVMEResolvedList(QQmlListProperty<QObject> *prop)
{
- Q_DISABLE_COPY_MOVE(ResolvedList)
+ // see QQmlVMEMetaObject::metaCall for how this was constructed
+ auto encodedIndex = quintptr(prop->data);
+ constexpr quintptr usableBits = sizeof(quintptr) * CHAR_BIT;
+ quintptr inheritanceDepth = encodedIndex >> (usableBits / 2);
+ m_id = encodedIndex & ((quintptr(1) << (usableBits / 2)) - 1);
+
+ // walk up to the correct meta object if necessary
+ auto mo = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(prop->object)->metaObject);
+ while (inheritanceDepth--)
+ mo = mo->parentVMEMetaObject();
+ m_metaObject = mo;
+ Q_ASSERT(m_metaObject);
+ Q_ASSERT(::strstr(m_metaObject->toDynamicMetaObject(prop->object)
+ ->property(m_metaObject->propOffset() + m_id)
+ .typeName(),
+ "QQmlListProperty"));
+ Q_ASSERT(m_metaObject->object == prop->object);
+
+ // readPropertyAsList() with checks transformed into Q_ASSERT
+ // and without allocation.
+ if (m_metaObject->propertyAndMethodStorage.isUndefined()
+ && m_metaObject->propertyAndMethodStorage.valueRef()) {
+ return;
+ }
-public:
- ResolvedList(QQmlListProperty<QObject> *prop)
- {
- // see QQmlVMEMetaObject::metaCall for how this was constructed
- auto encodedIndex = quintptr(prop->data);
- constexpr quintptr usableBits = sizeof(quintptr) * CHAR_BIT;
- quintptr inheritanceDepth = encodedIndex >> (usableBits / 2);
- m_id = encodedIndex & ((quintptr(1) << (usableBits / 2)) - 1);
-
- // walk up to the correct meta object if necessary
- auto mo = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(prop->object)->metaObject);
- while (inheritanceDepth--)
- mo = mo->parentVMEMetaObject();
- m_metaObject = mo;
- Q_ASSERT(m_metaObject);
- Q_ASSERT(::strstr(m_metaObject->toDynamicMetaObject(prop->object)->property(
- m_metaObject->propOffset() + m_id).typeName(), "QQmlListProperty") );
- Q_ASSERT(m_metaObject->object == prop->object);
-
- // readPropertyAsList() with checks transformed into Q_ASSERT
- // and without allocation.
- if (m_metaObject->propertyAndMethodStorage.isUndefined() &&
- m_metaObject->propertyAndMethodStorage.valueRef()) {
- return;
- }
+ if (auto *md = static_cast<QV4::MemberData *>(
+ m_metaObject->propertyAndMethodStorage.asManaged())) {
+ const QV4::Value *v = md->data() + m_id;
+ Q_ASSERT(v->as<QV4::Object>());
+ m_list = static_cast<QV4::Heap::Object *>(v->heapObject());
+ Q_ASSERT(m_list);
+ }
+}
- if (auto *md = static_cast<QV4::MemberData *>(
- m_metaObject->propertyAndMethodStorage.asManaged())) {
- const auto *v = (md->data() + m_id)->as<QV4::VariantObject>();
- Q_ASSERT(v);
- Q_ASSERT(v->d());
- QVariant &data = v->d()->data();
- Q_ASSERT(data.userType() == qMetaTypeId<QVector<QQmlGuard<QObject>>>());
- m_list = static_cast<QVector<QQmlGuard<QObject>> *>(data.data());
- Q_ASSERT(m_list);
- }
+void QQmlVMEResolvedList::append(QObject *o) const
+{
+ QV4::Scope scope(m_list->internalClass->engine);
+ QV4::Heap::ArrayData *arrayData = m_list->arrayData;
+
+ const uint length = arrayData->length();
+ if (Q_UNLIKELY(length == std::numeric_limits<uint>::max())) {
+ scope.engine->throwRangeError(QLatin1String("Too many elements."));
+ return;
}
- ~ResolvedList() = default;
+ QV4::ScopedObject object(scope, m_list);
+ QV4::ArrayData::realloc(object, QV4::Heap::ArrayData::Simple, length + 1, false);
+ arrayData->vtable()->put(
+ object, length, QV4::QObjectWrapper::wrap(scope.engine, o));
+}
+
+QObject *QQmlVMEResolvedList::at(qsizetype i) const
+{
+ QV4::Scope scope(m_list->internalClass->engine);
+ QV4::Scoped<QV4::QObjectWrapper> result(scope, m_list->arrayData->get(i));
+ return result ? result->object() : nullptr;
+}
- QQmlVMEMetaObject *metaObject() const { return m_metaObject; }
- QVector<QQmlGuard<QObject>> *list() const { return m_list; }
- quintptr id() const { return m_id; }
+void QQmlVMEResolvedList::replace(qsizetype i, QObject *o) const
+{
+ QV4::Scope scope(m_list->internalClass->engine);
+ QV4::ScopedObject object(scope, m_list);
+ m_list->arrayData->vtable()->put(object, i, QV4::QObjectWrapper::wrap(scope.engine, o));
+}
- void activateSignal() const
- {
- m_metaObject->activate(m_metaObject->object, int(m_id + m_metaObject->methodOffset()),
- nullptr);
- }
+QQmlVMEResolvedList::~QQmlVMEResolvedList() = default;
-private:
- QQmlVMEMetaObject *m_metaObject = nullptr;
- QVector<QQmlGuard<QObject>> *m_list = nullptr;
- quintptr m_id = 0;
-};
+void QQmlVMEResolvedList::activateSignal() const
+{
+ m_metaObject->activate(m_metaObject->object, int(m_id + m_metaObject->methodOffset()), nullptr);
+}
-static void list_append(QQmlListProperty<QObject> *prop, QObject *o)
+void QQmlVMEMetaObject::list_append(QQmlListProperty<QObject> *prop, QObject *o)
{
- const ResolvedList resolved(prop);
- resolved.list()->append(o);
+ const QQmlVMEResolvedList resolved(prop);
+ resolved.append(o);
resolved.activateSignal();
}
+void QQmlVMEMetaObject::list_append_nosignal(QQmlListProperty<QObject> *prop, QObject *o)
+{
+ QQmlVMEResolvedList(prop).append(o);
+}
+
static qsizetype list_count(QQmlListProperty<QObject> *prop)
{
- return ResolvedList(prop).list()->size();
+ return QQmlVMEResolvedList(prop).size();
}
static QObject *list_at(QQmlListProperty<QObject> *prop, qsizetype index)
{
- return ResolvedList(prop).list()->at(index);
+ return QQmlVMEResolvedList(prop).at(index);
}
-static void list_clear(QQmlListProperty<QObject> *prop)
+void QQmlVMEMetaObject::list_clear(QQmlListProperty<QObject> *prop)
{
- const ResolvedList resolved(prop);
- resolved.list()->clear();
+ const QQmlVMEResolvedList resolved(prop);
+ resolved.clear();
resolved.activateSignal();
}
+void QQmlVMEMetaObject::list_clear_nosignal(QQmlListProperty<QObject> *prop)
+{
+ QQmlVMEResolvedList(prop).clear();
+}
+
static void list_replace(QQmlListProperty<QObject> *prop, qsizetype index, QObject *o)
{
- const ResolvedList resolved(prop);
- resolved.list()->replace(index, o);
+ const QQmlVMEResolvedList resolved(prop);
+ resolved.replace(index, o);
resolved.activateSignal();
}
static void list_removeLast(QQmlListProperty<QObject> *prop)
{
- const ResolvedList resolved(prop);
- resolved.list()->removeLast();
+ const QQmlVMEResolvedList resolved(prop);
+ resolved.removeLast();
resolved.activateSignal();
}
@@ -248,6 +274,15 @@ QQmlInterceptorMetaObject::~QQmlInterceptorMetaObject()
void QQmlInterceptorMetaObject::registerInterceptor(QQmlPropertyIndex index, QQmlPropertyValueInterceptor *interceptor)
{
+ for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) {
+ if (Q_UNLIKELY(vi->m_propertyIndex.coreIndex() == index.coreIndex())) {
+ qWarning() << "Attempting to set another interceptor on "
+ << object->metaObject()->className() << "property"
+ << object->metaObject()->property(index.coreIndex()).name()
+ << "- unsupported";
+ }
+ }
+
interceptor->m_propertyIndex = index;
interceptor->m_next = interceptors;
interceptors = interceptor;
@@ -335,7 +370,7 @@ bool QQmlInterceptorMetaObject::doIntercept(QMetaObject::Call c, int id, void **
// change the value soon. Such an animation needs to be canceled if the
// current value is explicitly set.
// So, we cannot return here if prevComponentValue == newComponentValue.
- valueType->writeOnGadget(valueProp, prevComponentValue);
+ valueType->writeOnGadget(valueProp, std::move(prevComponentValue));
valueType->write(object, id, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
vi->write(newComponentValue);
@@ -395,6 +430,12 @@ QQmlVMEMetaObject::QQmlVMEMetaObject(QV4::ExecutionEngine *engine,
uint size = compiledObject->nProperties + compiledObject->nFunctions;
if (size) {
QV4::Heap::MemberData *data = QV4::MemberData::allocate(engine, size);
+ // we only have a weak reference below; if the VMEMetaObject is already marked
+ // (triggered by the allocate call above)
+ // we therefore might never mark the member data; consequently, mark it now
+ QV4::WriteBarrier::markCustom(engine, [data](QV4::MarkStack *ms) {
+ data->mark(ms);
+ });
propertyAndMethodStorage.set(engine, data);
std::fill(data->values.values, data->values.values + data->values.size, QV4::Encode::undefined());
}
@@ -583,6 +624,22 @@ QDateTime QQmlVMEMetaObject::readPropertyAsDateTime(int id) const
return v->d()->data().value<QDateTime>();
}
+#if QT_CONFIG(regularexpression)
+QRegularExpression QQmlVMEMetaObject::readPropertyAsRegularExpression(int id) const
+{
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
+ if (!md)
+ return QRegularExpression();
+
+ QV4::Scope scope(engine);
+ QV4::ScopedValue sv(scope, *(md->data() + id));
+ const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
+ if (!v || v->d()->data().userType() != QMetaType::QRegularExpression)
+ return QRegularExpression();
+ return v->d()->data().value<QRegularExpression>();
+}
+#endif
+
QSizeF QQmlVMEMetaObject::readPropertyAsSizeF(int id) const
{
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
@@ -625,20 +682,19 @@ QObject* QQmlVMEMetaObject::readPropertyAsQObject(int id) const
return wrapper->object();
}
-QVector<QQmlGuard<QObject>> *QQmlVMEMetaObject::readPropertyAsList(int id) const
+void QQmlVMEMetaObject::initPropertyAsList(int id) const
{
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
- return nullptr;
+ return;
QV4::Scope scope(engine);
- QV4::Scoped<QV4::VariantObject> v(scope, *(md->data() + id));
- if (!v || v->d()->data().metaType() != QMetaType::fromType<QVector<QQmlGuard<QObject>>>()) {
- const QVector<QQmlGuard<QObject>> guards;
- v = engine->newVariantObject(QMetaType::fromType<QVector<QQmlGuard<QObject>>>(), &guards);
+ QV4::ScopedObject v(scope, *(md->data() + id));
+ if (!v) {
+ v = engine->newObject();
+ v->arrayCreate();
md->set(engine, id, v);
}
- return static_cast<QVector<QQmlGuard<QObject>> *>(v->d()->data().data());
}
QRectF QQmlVMEMetaObject::readPropertyAsRectF(int id) const
@@ -678,7 +734,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
// if we reach this point, propertyCount must have been > 0, and thus compiledObject != nullptr
Q_ASSERT(compiledObject);
const QV4::CompiledData::Property &property = compiledObject->propertyTable()[id];
- const QV4::CompiledData::BuiltinType t = property.builtinType();
+ const QV4::CompiledData::CommonType t = property.commonType();
// the context can be null if accessing var properties from cpp after re-parenting an item.
QQmlEnginePrivate *ep = (ctxt.isNull() || ctxt->engine() == nullptr)
@@ -719,7 +775,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
}
quintptr encodedIndex = (inheritanceDepth << idBits) + id;
- readPropertyAsList(id); // Initializes if necessary
+ initPropertyAsList(id);
*static_cast<QQmlListProperty<QObject> *>(a[0])
= QQmlListProperty<QObject>(
object, reinterpret_cast<void *>(quintptr(encodedIndex)),
@@ -739,40 +795,48 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
}
} else {
switch (t) {
- case QV4::CompiledData::BuiltinType::Int:
+ case QV4::CompiledData::CommonType::Void:
+ break;
+ case QV4::CompiledData::CommonType::Int:
*reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id);
break;
- case QV4::CompiledData::BuiltinType::Bool:
+ case QV4::CompiledData::CommonType::Bool:
*reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id);
break;
- case QV4::CompiledData::BuiltinType::Real:
+ case QV4::CompiledData::CommonType::Real:
*reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id);
break;
- case QV4::CompiledData::BuiltinType::String:
+ case QV4::CompiledData::CommonType::String:
*reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id);
break;
- case QV4::CompiledData::BuiltinType::Url:
+ case QV4::CompiledData::CommonType::Url:
*reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id);
break;
- case QV4::CompiledData::BuiltinType::Date:
+ case QV4::CompiledData::CommonType::Date:
*reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id);
break;
- case QV4::CompiledData::BuiltinType::DateTime:
+ case QV4::CompiledData::CommonType::DateTime:
*reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id);
break;
- case QV4::CompiledData::BuiltinType::Rect:
+ case QV4::CompiledData::CommonType::RegExp:
+#if QT_CONFIG(regularexpression)
+ *reinterpret_cast<QRegularExpression *>(a[0])
+ = readPropertyAsRegularExpression(id);
+#endif
+ break;
+ case QV4::CompiledData::CommonType::Rect:
*reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id);
break;
- case QV4::CompiledData::BuiltinType::Size:
+ case QV4::CompiledData::CommonType::Size:
*reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id);
break;
- case QV4::CompiledData::BuiltinType::Point:
+ case QV4::CompiledData::CommonType::Point:
*reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id);
break;
- case QV4::CompiledData::BuiltinType::Time:
+ case QV4::CompiledData::CommonType::Time:
*reinterpret_cast<QTime *>(a[0]) = readPropertyAsTime(id);
break;
- case QV4::CompiledData::BuiltinType::Var:
+ case QV4::CompiledData::CommonType::Var:
if (ep) {
*reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id);
} else {
@@ -781,7 +845,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
*reinterpret_cast<QVariant *>(a[0]) = QVariant();
}
break;
- case QV4::CompiledData::BuiltinType::InvalidBuiltin:
+ case QV4::CompiledData::CommonType::Invalid:
if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
QV4::Scope scope(engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
@@ -835,8 +899,20 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
needActivate = true;
}
} else {
- QV4::ScopedValue sequence(scope, QV4::SequencePrototype::fromData(
- engine, propType, a[0]));
+ if (const QQmlType type = QQmlMetaType::qmlListType(propType);
+ type.isSequentialContainer()) {
+ sequence = QV4::SequencePrototype::fromData(
+ engine, propType, type.listMetaSequence(), a[0]);
+ } else if (QSequentialIterable iterable;
+ QMetaType::convert(
+ propType, a[0],
+ QMetaType::fromType<QSequentialIterable>(),
+ &iterable)) {
+ sequence = QV4::SequencePrototype::fromData(
+ engine, propType, iterable.metaContainer(), a[0]);
+ } else {
+ sequence = QV4::Encode::undefined();
+ }
md->set(engine, id, sequence);
if (sequence->isUndefined()) {
qmlWarning(object)
@@ -850,55 +926,64 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
}
} else {
switch (t) {
- case QV4::CompiledData::BuiltinType::Int:
+ case QV4::CompiledData::CommonType::Void:
+ break;
+ case QV4::CompiledData::CommonType::Int:
needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id);
writeProperty(id, *reinterpret_cast<int *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Bool:
+ case QV4::CompiledData::CommonType::Bool:
needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id);
writeProperty(id, *reinterpret_cast<bool *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Real:
+ case QV4::CompiledData::CommonType::Real:
needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id);
writeProperty(id, *reinterpret_cast<double *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::String:
+ case QV4::CompiledData::CommonType::String:
needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id);
writeProperty(id, *reinterpret_cast<QString *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Url:
+ case QV4::CompiledData::CommonType::Url:
needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id);
writeProperty(id, *reinterpret_cast<QUrl *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Date:
+ case QV4::CompiledData::CommonType::Date:
needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id);
writeProperty(id, *reinterpret_cast<QDate *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::DateTime:
+ case QV4::CompiledData::CommonType::DateTime:
needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id);
writeProperty(id, *reinterpret_cast<QDateTime *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Rect:
+ case QV4::CompiledData::CommonType::RegExp:
+#if QT_CONFIG(regularexpression)
+ needActivate = *reinterpret_cast<QRegularExpression *>(a[0])
+ != readPropertyAsRegularExpression(id);
+ writeProperty(id, *reinterpret_cast<QRegularExpression *>(a[0]));
+#endif
+ break;
+ case QV4::CompiledData::CommonType::Rect:
needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id);
writeProperty(id, *reinterpret_cast<QRectF *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Size:
+ case QV4::CompiledData::CommonType::Size:
needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id);
writeProperty(id, *reinterpret_cast<QSizeF *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Point:
+ case QV4::CompiledData::CommonType::Point:
needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id);
writeProperty(id, *reinterpret_cast<QPointF *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Time:
+ case QV4::CompiledData::CommonType::Time:
needActivate = *reinterpret_cast<QTime *>(a[0]) != readPropertyAsTime(id);
writeProperty(id, *reinterpret_cast<QTime *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Var:
+ case QV4::CompiledData::CommonType::Var:
if (ep)
writeProperty(id, *reinterpret_cast<QVariant *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::InvalidBuiltin:
+ case QV4::CompiledData::CommonType::Invalid:
if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
QV4::Scope scope(engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
@@ -980,15 +1065,20 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
int coreIndex = encodedIndex.coreIndex();
const int valueTypePropertyIndex = encodedIndex.valueTypeIndex();
- // Remove binding (if any) on write
- if(c == QMetaObject::WriteProperty) {
- int flags = *reinterpret_cast<int*>(a[3]);
- if (flags & QQmlPropertyData::RemoveBindingOnAliasWrite) {
- QQmlData *targetData = QQmlData::get(target);
- if (targetData && targetData->hasBindingBit(coreIndex))
- QQmlPropertyPrivate::removeBinding(target, encodedIndex);
+ const auto removePendingBinding
+ = [c, a](QObject *target, int coreIndex, QQmlPropertyIndex encodedIndex) {
+ // Remove binding (if any) on write
+ if (c == QMetaObject::WriteProperty) {
+ int flags = *reinterpret_cast<int*>(a[3]);
+ if (flags & QQmlPropertyData::RemoveBindingOnAliasWrite) {
+ QQmlData *targetData = QQmlData::get(target);
+ if (targetData && targetData->hasBindingBit(coreIndex)) {
+ QQmlPropertyPrivate::removeBinding(target, encodedIndex);
+ targetData->clearBindingBit(coreIndex);
+ }
+ }
}
- }
+ };
if (valueTypePropertyIndex != -1) {
if (!targetDData->propertyCache)
@@ -998,6 +1088,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance(
ctxt->engine(), pd->propType());
if (valueType) {
+ removePendingBinding(target, coreIndex, encodedIndex);
valueType->read(target, coreIndex);
int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a);
@@ -1010,10 +1101,14 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
// deep alias
void *argv[1] = { &target };
QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv);
+ removePendingBinding(
+ target, valueTypePropertyIndex,
+ QQmlPropertyIndex(valueTypePropertyIndex));
return QMetaObject::metacall(target, c, valueTypePropertyIndex, a);
}
} else {
+ removePendingBinding(target, coreIndex, encodedIndex);
return QMetaObject::metacall(target, c, coreIndex, a);
}
@@ -1046,7 +1141,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
QV4::Scope scope(v4);
- QV4::ScopedFunctionObject function(scope, method(id));
+ QV4::Scoped<QV4::JavaScriptFunctionObject> function(scope, method(id));
if (!function) {
// The function was not compiled. There are some exceptional cases which the
// expression rewriter does not rewrite properly (e.g., \r-terminated lines
@@ -1067,15 +1162,11 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
if (arguments && arguments->names) {
const quint32 parameterCount = arguments->names->size();
Q_ASSERT(parameterCount == function->formalParameterCount());
- if (void *result = a[0])
- arguments->types[0].destruct(result);
- function->call(nullptr, a, arguments->types, parameterCount);
+ function->call(object, a, arguments->types, parameterCount);
} else {
Q_ASSERT(function->formalParameterCount() == 0);
const QMetaType returnType = methodData->propType();
- if (void *result = a[0])
- returnType.destruct(result);
- function->call(nullptr, a, &returnType, 0);
+ function->call(object, a, &returnType, 0);
}
if (scope.hasException()) {
@@ -1113,7 +1204,7 @@ QV4::ReturnedValue QQmlVMEMetaObject::method(int index) const
QV4::ReturnedValue QQmlVMEMetaObject::readVarProperty(int id) const
{
- Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var);
+ Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].commonType() == QV4::CompiledData::CommonType::Var);
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
@@ -1138,7 +1229,7 @@ QVariant QQmlVMEMetaObject::readPropertyAsVariant(int id) const
void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value)
{
- Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var);
+ Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].commonType() == QV4::CompiledData::CommonType::Var);
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
@@ -1199,7 +1290,7 @@ void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value)
void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value)
{
- if (compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var) {
+ if (compiledObject && compiledObject->propertyTable()[id].commonType() == QV4::CompiledData::CommonType::Var) {
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return;
@@ -1303,7 +1394,7 @@ void QQmlVMEMetaObject::setVMEProperty(int index, const QV4::Value &v)
void QQmlVMEMetaObject::ensureQObjectWrapper()
{
Q_ASSERT(cache);
- QV4::QObjectWrapper::wrap(engine, object);
+ QV4::QObjectWrapper::ensureWrapper(engine, object);
}
void QQmlVMEMetaObject::mark(QV4::MarkStack *markStack)
diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h
index 1b9a122b1f..d37c20a41b 100644
--- a/src/qml/qml/qqmlvmemetaobject_p.h
+++ b/src/qml/qml/qqmlvmemetaobject_p.h
@@ -16,27 +16,73 @@
// We mean it.
//
-#include <QtCore/QMetaObject>
-#include <QtCore/QBitArray>
-#include <QtCore/QPair>
-#include <QtCore/QDate>
-#include <QtCore/qlist.h>
-#include <QtCore/qdebug.h>
-
-#include <private/qobject_p.h>
-
-#include "qqmlguard_p.h"
-
-#include <private/qqmlguardedcontextdata_p.h>
#include <private/qbipointer_p.h>
-
+#include <private/qqmlguard_p.h>
+#include <private/qqmlguardedcontextdata_p.h>
+#include <private/qqmlpropertyvalueinterceptor_p.h>
#include <private/qv4object_p.h>
#include <private/qv4value_p.h>
-#include <private/qqmlpropertyvalueinterceptor_p.h>
+
+#include <QtCore/private/qobject_p.h>
+
+#if QT_CONFIG(regularexpression)
+#include <QtCore/qregularexpression.h>
+#endif
+
+#include <QtCore/qbitarray.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qpair.h>
QT_BEGIN_NAMESPACE
class QQmlVMEMetaObject;
+class QQmlVMEResolvedList
+{
+ Q_DISABLE_COPY_MOVE(QQmlVMEResolvedList)
+
+public:
+ QQmlVMEResolvedList(QQmlListProperty<QObject> *prop);
+ ~QQmlVMEResolvedList();
+
+ QQmlVMEMetaObject *metaObject() const { return m_metaObject; }
+ QV4::Heap::Object *list() const { return m_list; }
+ quintptr id() const { return m_id; }
+
+ void append(QObject *o) const;
+ void replace(qsizetype i, QObject *o) const;
+ QObject *at(qsizetype i) const;
+
+ qsizetype size() const { return m_list->arrayData->length(); }
+
+ void clear() const
+ {
+ QV4::Scope scope(m_list->internalClass->engine);
+ QV4::ScopedObject object(scope, m_list);
+ m_list->arrayData->vtable()->truncate(object, 0);
+ }
+
+ void removeLast() const
+ {
+ const uint length = m_list->arrayData->length();
+ if (length == 0)
+ return;
+
+ QV4::Scope scope(m_list->internalClass->engine);
+ QV4::ScopedObject object(scope, m_list);
+ m_list->arrayData->vtable()->truncate(object, length - 1);
+ }
+
+ void activateSignal() const;
+
+private:
+ QQmlVMEMetaObject *m_metaObject = nullptr;
+ QV4::Heap::Object *m_list = nullptr;
+ quintptr m_id = 0;
+};
+
class QQmlVMEVariantQObjectPtr : public QQmlGuard<QObject>
{
public:
@@ -52,7 +98,7 @@ private:
};
-class Q_QML_PRIVATE_EXPORT QQmlInterceptorMetaObject : public QDynamicMetaObjectData
+class Q_QML_EXPORT QQmlInterceptorMetaObject : public QDynamicMetaObjectData
{
public:
QQmlInterceptorMetaObject(QObject *obj, const QQmlPropertyCache::ConstPtr &cache);
@@ -127,7 +173,7 @@ inline QQmlInterceptorMetaObject *QQmlInterceptorMetaObject::get(QObject *obj)
}
class QQmlVMEMetaObjectEndpoint;
-class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject
+class Q_QML_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject
{
public:
QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj,
@@ -149,6 +195,11 @@ public:
static QQmlVMEMetaObject *getForMethod(QObject *o, int coreIndex);
static QQmlVMEMetaObject *getForSignal(QObject *o, int coreIndex);
+ static void list_append(QQmlListProperty<QObject> *prop, QObject *o);
+ static void list_clear(QQmlListProperty<QObject> *prop);
+ static void list_append_nosignal(QQmlListProperty<QObject> *prop, QObject *o);
+ static void list_clear_nosignal(QQmlListProperty<QObject> *prop);
+
protected:
int metaCall(QObject *o, QMetaObject::Call _c, int _id, void **_a) override;
@@ -176,9 +227,14 @@ public:
QDate readPropertyAsDate(int id) const;
QTime readPropertyAsTime(int id) const;
QDateTime readPropertyAsDateTime(int id) const;
+
+#if QT_CONFIG(regularexpression)
+ QRegularExpression readPropertyAsRegularExpression(int id) const;
+#endif
+
QRectF readPropertyAsRectF(int id) const;
QObject *readPropertyAsQObject(int id) const;
- QVector<QQmlGuard<QObject> > *readPropertyAsList(int id) const;
+ void initPropertyAsList(int id) const;
void writeProperty(int id, int v);
void writeProperty(int id, bool v);
diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp
index cd4ef6e42d..b4800584a3 100644
--- a/src/qml/qml/qqmlxmlhttprequest.cpp
+++ b/src/qml/qml/qqmlxmlhttprequest.cpp
@@ -19,6 +19,8 @@
#include <QtQml/qjsengine.h>
#include <QtQml/qqmlfile.h>
#include <QtNetwork/qnetworkreply.h>
+
+#include <QtCore/qpointer.h>
#include <QtCore/qstringconverter.h>
#include <QtCore/qxmlstream.h>
#include <QtCore/qstack.h>
@@ -116,11 +118,12 @@ public:
QList<NodeImpl *> attributes;
};
-class DocumentImpl : public QQmlRefCount, public NodeImpl
+class DocumentImpl final : public QQmlRefCounted<DocumentImpl>, public NodeImpl
{
+ using Base1 = QQmlRefCounted<DocumentImpl>;
public:
DocumentImpl() : root(nullptr) { type = Document; }
- virtual ~DocumentImpl() {
+ ~DocumentImpl() override {
delete root;
}
@@ -130,8 +133,8 @@ public:
NodeImpl *root;
- void addref() { QQmlRefCount::addref(); }
- void release() { QQmlRefCount::release(); }
+ void addref() { Base1::addref(); }
+ void release() { Base1::release(); }
};
namespace Heap {
@@ -998,9 +1001,15 @@ public:
QString responseBody();
const QByteArray & rawResponseBody() const;
bool receivedXml() const;
+ QUrl url() const;
const QString & responseType() const;
void setResponseType(const QString &);
+ void setOverrideMimeType(QStringView mimeType) { m_overrideMime = mimeType.toUtf8(); }
+ void setOverrideCharset(QStringView charset) { m_overrideCharset = charset.toUtf8(); }
+
+ const QByteArray mimeType() const;
+ const QByteArray charset() const;
QV4::ReturnedValue jsonResponseBody(QV4::ExecutionEngine*);
QV4::ReturnedValue xmlResponseBody(QV4::ExecutionEngine*);
@@ -1029,6 +1038,9 @@ private:
bool m_gotXml;
QByteArray m_mime;
QByteArray m_charset;
+ QByteArray m_overrideMime;
+ QByteArray m_overrideCharset;
+
QStringDecoder findTextDecoder() const;
void readEncoding();
@@ -1057,8 +1069,6 @@ private:
QQmlXMLHttpRequest::QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::ExecutionEngine *v4)
: m_state(Unsent), m_errorFlag(false), m_sendFlag(false)
, m_redirectCount(0), m_gotXml(false), m_network(nullptr), m_nam(manager)
- , m_responseType()
- , m_parsedDocument()
{
m_wasConstructedWithQmlContext = !v4->callingQmlContext().isNull();
}
@@ -1162,6 +1172,7 @@ void QQmlXMLHttpRequest::fillHeadersList()
void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url)
{
+ m_url = url;
QNetworkRequest request = m_request;
if (QQmlFile::isLocalFile(url)) {
@@ -1453,7 +1464,8 @@ void QQmlXMLHttpRequest::readEncoding()
}
}
- if (m_mime.isEmpty() || m_mime == "text/xml" || m_mime == "application/xml" || m_mime.endsWith("+xml"))
+ const auto mime = mimeType();
+ if (mime.isEmpty() || mime == "text/xml" || mime == "application/xml" || mime.endsWith("+xml"))
m_gotXml = true;
}
@@ -1462,6 +1474,25 @@ bool QQmlXMLHttpRequest::receivedXml() const
return m_gotXml;
}
+QUrl QQmlXMLHttpRequest::url() const
+{
+ return m_url;
+}
+
+const QByteArray QQmlXMLHttpRequest::mimeType() const
+{
+ // Final MIME type is the override MIME type unless that is null in which
+ // case it is the response MIME type.
+ return m_overrideMime.isEmpty() ? m_mime : m_overrideMime;
+}
+
+const QByteArray QQmlXMLHttpRequest::charset() const
+{
+ // Final charset is the override charset unless that is null in which case
+ // it is the response charset.
+ return m_overrideCharset.isEmpty() ? m_charset : m_overrideCharset;
+}
+
const QString & QQmlXMLHttpRequest::responseType() const
{
return m_responseType;
@@ -1503,8 +1534,8 @@ QStringDecoder QQmlXMLHttpRequest::findTextDecoder() const
{
QStringDecoder decoder;
- if (!m_charset.isEmpty())
- decoder = QStringDecoder(m_charset);
+ if (!charset().isEmpty())
+ decoder = QStringDecoder(charset());
if (!decoder.isValid() && m_gotXml) {
QXmlStreamReader reader(m_responseEntityBody);
@@ -1512,11 +1543,8 @@ QStringDecoder QQmlXMLHttpRequest::findTextDecoder() const
decoder = QStringDecoder(reader.documentEncoding().toString().toUtf8());
}
- if (!decoder.isValid() && m_mime == "text/html") {
- auto encoding = QStringConverter::encodingForHtml(m_responseEntityBody);
- if (encoding)
- decoder = QStringDecoder(*encoding);
- }
+ if (!decoder.isValid() && mimeType() == "text/html")
+ decoder = QStringDecoder::decoderForHtml(m_responseEntityBody);
if (!decoder.isValid()) {
auto encoding = QStringConverter::encodingForData(m_responseEntityBody);
@@ -1632,6 +1660,7 @@ struct QQmlXMLHttpRequestWrapper : public Object
V4_NEEDS_DESTROY
};
+// https://xhr.spec.whatwg.org/
struct QQmlXMLHttpRequestCtor : public FunctionObject
{
V4_OBJECT2(QQmlXMLHttpRequestCtor, FunctionObject)
@@ -1660,6 +1689,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject
static ReturnedValue method_abort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_getResponseHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_getAllResponseHeaders(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_overrideMimeType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_readyState(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_status(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
@@ -1669,6 +1699,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject
static ReturnedValue method_get_response(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_set_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_responseURL(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
};
}
@@ -1677,7 +1708,7 @@ DEFINE_OBJECT_VTABLE(QQmlXMLHttpRequestWrapper);
void Heap::QQmlXMLHttpRequestCtor::init(ExecutionEngine *engine)
{
- Heap::FunctionObject::init(engine->rootContext(), QStringLiteral("XMLHttpRequest"));
+ Heap::FunctionObject::init(engine, QStringLiteral("XMLHttpRequest"));
Scope scope(engine);
Scoped<QV4::QQmlXMLHttpRequestCtor> ctor(scope, this);
@@ -1709,6 +1740,7 @@ void QQmlXMLHttpRequestCtor::setupProto()
p->defineDefaultProperty(QStringLiteral("abort"), method_abort);
p->defineDefaultProperty(QStringLiteral("getResponseHeader"), method_getResponseHeader);
p->defineDefaultProperty(QStringLiteral("getAllResponseHeaders"), method_getAllResponseHeaders);
+ p->defineDefaultProperty(QStringLiteral("overrideMimeType"), method_overrideMimeType);
// Read-only properties
p->defineAccessorProperty(QStringLiteral("readyState"), method_get_readyState, nullptr);
@@ -1717,6 +1749,7 @@ void QQmlXMLHttpRequestCtor::setupProto()
p->defineAccessorProperty(QStringLiteral("responseText"),method_get_responseText, nullptr);
p->defineAccessorProperty(QStringLiteral("responseXML"),method_get_responseXML, nullptr);
p->defineAccessorProperty(QStringLiteral("response"),method_get_response, nullptr);
+ p->defineAccessorProperty(QStringLiteral("responseURL"),method_get_responseURL, nullptr);
// Read-write properties
p->defineAccessorProperty(QStringLiteral("responseType"), method_get_responseType, method_set_responseType);
@@ -2042,6 +2075,71 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_set_responseType(const FunctionObje
return Encode::undefined();
}
+ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseURL(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
+ if (!w)
+ V4THROW_REFERENCE("Not an XMLHttpRequest object");
+ QQmlXMLHttpRequest *r = w->d()->request;
+
+ if (r->readyState() != QQmlXMLHttpRequest::Loading &&
+ r->readyState() != QQmlXMLHttpRequest::Done) {
+ return Encode(scope.engine->newString(QString()));
+ } else {
+ QUrl url = r->url();
+ url.setFragment(QString());
+ return Encode(scope.engine->newString(url.toString()));
+ }
+}
+
+ReturnedValue QQmlXMLHttpRequestCtor::method_overrideMimeType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
+ if (!w)
+ V4THROW_REFERENCE("Not an XMLHttpRequest object");
+ QQmlXMLHttpRequest *r = w->d()->request;
+
+ if (argc != 1)
+ THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
+
+ // If state is loading or done, throw an InvalidStateError exception.
+ if (r->readyState() == QQmlXMLHttpRequest::Loading ||
+ r->readyState() == QQmlXMLHttpRequest::Done)
+ THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
+
+ // Set override MIME type to `application/octet-stream`.
+ r->setOverrideMimeType(QStringLiteral("application/octet-stream"));
+ const auto parts = argv[0].toQStringNoThrow().split(QLatin1Char(';'));
+ const auto type = parts.at(0).trimmed();
+
+ const auto mimeInvalidCharacter = [](QChar uni) {
+ if (uni.unicode() > 127) // Only accept ASCII
+ return true;
+ const char ch = char(uni.unicode());
+ return !(ch == '-' || ch == '/' || isAsciiLetterOrNumber(ch));
+ };
+
+ // If mime is a parsable MIME type, ...
+ if (type.count(QLatin1Char('/')) == 1
+ && std::find_if(type.begin(), type.end(), mimeInvalidCharacter) == type.end()) {
+ // ... then set override MIME type to its MIME type portion.
+ r->setOverrideMimeType(type);
+ }
+ for (const auto &part : parts) {
+ const QLatin1String charset("charset=");
+ // If override MIME type has a `charset` parameter, ...
+ if (part.trimmed().startsWith(charset)) {
+ // ... then set override charset to its value.
+ const int offset(part.indexOf(charset) + charset.size());
+ r->setOverrideCharset(part.sliced(offset).trimmed());
+ }
+ }
+
+ return Encode::undefined();
+}
+
void qt_rem_qmlxmlhttprequest(ExecutionEngine * /* engine */, void *d)
{
QQmlXMLHttpRequestData *data = (QQmlXMLHttpRequestData *)d;